functime: Polars-native global forecasting and feature extraction for panel data
Time-series machine learning at scale. Built with Polars for embarrassingly parallel feature extraction and forecasts on panel data.
At a glance
- What is it?
- functime is a Python library that treats every time series in a panel as one column of a single Polars frame, so feature extraction and forecasting run as parallel expressions rather than per-series loops. It is a good fit when you have many short-to-medium series and need them all forecast at once, and a poor fit when you need a single long-horizon univariate model with probabilistic intervals.
- Who is it for?
- Adopt functime if your data is already a panel of many series with a shared frequency and you want features and forecasts computed in one Polars pass. Do not adopt it if you need probabilistic forecast intervals, irregular per-series timestamps, or a single very long series.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 136 days ago.
- What is it written in?
- Mainly Python, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 15, 2026, and from our analysis. They are not legal advice.
DEEP OPEN-SOURCE ANALYSIS
The problem functime targets: many series, one frequency, one pass
Most Python forecasting code is written one series at a time. You loop over SKUs, sensors or regions, fit a model per entity, and collect the results. That loop is fine for a hundred series and painful for a hundred thousand, because the per-series overhead (Python call frames, model construction, data copying) dominates the actual arithmetic. functime's stated goal is to remove the loop. The README describes the library as being for "production-ready global forecasting and time-series feature extraction on large panel datasets", and the highlight list claims you can "forecast and extract features across 100,000 time series in seconds on your laptop". That number comes from the project's own marketing copy, not from an independent run, so treat it as a design target rather than a measurement.
The unit of work in functime is the panel: a table with an entity column, a time column and one or more value columns. Every series in that table shares a frequency, which is passed to the forecaster as a Polars offset string such as freq="1mo". This is the central constraint and the central advantage. Because all series are assumed to live on the same regular grid, functime can express lags, rolling windows and Fourier terms as column expressions rather than per-series operations, and Polars can execute them in parallel across the entity groups. If your series have different sampling rates or irregular gaps, that assumption breaks and you are outside the library's intended shape.
How the Polars ts namespace and lazy transforms do the work
The mechanism is a custom Polars namespace. Importing functime registers a ts accessor on Polars Series and expressions, so feature extractors become ordinary column expressions. The README example shows the pattern: pl.col("value").ts.binned_entropy(bin_count=10), pl.col("value").ts.lempel_ziv_complexity(threshold=3), and pl.col("value").ts.longest_streak_above_mean() all inside a single select. The library documents over 100 feature extractors, including the tsfresh and Catch22 families. Because these are expressions, they compose with group_by and group_by_dynamic. Grouping by the entity column computes one feature row per series; grouping dynamically with every="12mo" computes rolling feature windows per series. Both paths are executed by Polars, and the README notes the extractors also work on LazyFrames, so the query planner can push down and reorder the work before anything materialises.
The same expression-first design carries into forecasting. A forecaster is constructed with a frequency and a lag count, for example linear_model(freq="1mo", lags=24). Calling fit on the panel builds one model across all entities rather than one per entity, which is what makes it a global forecaster. The README also shows a functional shorthand where construction and fitting collapse into a single call: linear_model(freq="1mo", lags=24)(y=y_train, fh=3). Target and feature transforms are passed as arguments. target_transform=scale() applies a transform to the values being predicted; feature_transform=add_fourier_terms(sp=12, K=6) adds seasonal terms with a period of 12 and 6 harmonics. Exogenous regressors go in through a separate X frame, and the README states every forecaster supports them.
Installing functime and running the first forecast
The README recommends uv and gives both install paths. uv add functime, or pip install functime. Extras are declared per backend: functime[cat] for the catboost forecaster, functime[xgb] for xgboost, functime[lgb] for lightgbm, and functime[llm] for the LLM-powered forecast analyst. They can be combined, as in uv add "functime[llm,lgb]". If you skip the extras you still get the feature extractors and the linear model path, but the gradient-boosted forecasters will not import.
The quickstart loads a parquet file of commodity prices directly from the repository URL, then takes entity_col, time_col = y.columns[:2], which assumes the first two columns are the entity and the time index in that order. The split is y.pipe(train_test_split(test_size=3)), producing y_train and y_test. Fitting and predicting is two calls on a constructed forecaster, or one call in the functional form. Scoring uses mase(y_true=y_test, y_pred=y_pred, y_train=y_train); the README also lists SMAPE among the metrics and describes them as parallel. For exogenous features the README builds X from the entity and time columns, pipes it through add_fourier_terms, and passes X_train to fit and X_future to predict. Note that add_fourier_terms is applied to a LazyFrame and then .collect() is called, so the exogenous frame is materialised before it reaches the forecaster.
Two things in the quickstart are worth flagging before you copy it. First, the README uses y.pipe(train_test_split(test_size=3)) for both the target and the exogenous frame, which means the split is recomputed rather than reused; whether the two splits align depends on train_test_split being deterministic, and the material supplied here does not confirm that. Second, the example reads a remote parquet over HTTPS on every run, which is fine for a tutorial and wrong for a pipeline.
Where functime is the wrong tool
The panel assumption is the sharpest limitation. Every series must share a frequency, and that frequency is a single string passed at construction time. If one product ships weekly and another ships monthly, you cannot put them in the same forecaster without resampling one of them, and resampling changes the feature values the extractors see. Irregular event data, such as transaction logs with no fixed cadence, does not fit at all.
The second limitation is that the documented surface is point forecasting. The README lists MASE and SMAPE as metrics and describes backtesting with expanding and sliding window splitters, and it documents automated lags and hyperparameter tuning through FLAML. It does not describe prediction intervals or a quantile output anywhere in the supplied material. If your downstream consumer needs an 80 percent interval rather than a single number, you should verify whether the forecasters expose one before you build on this. I could not confirm that from the README.
The third is dependency weight. The feature extraction path depends on Polars and the ts namespace registration, which happens as an import side effect. That means import functime must run before any pl.col(...).ts... expression is valid, and a module that forgets the import fails at expression build time rather than at import time. The forecasting path adds FLAML, and the boosted forecasters add their own extras on top. Each of those is a version you now track. The README does not state a supported Polars version range, so pinning is on you.
functime against a per-series statistical library
The obvious alternative is a per-series library in the statsmodels or sktime family, where you loop over entities and fit an ARIMA or exponential smoothing model to each one. The difference is not speed alone, it is what the model learns. A per-series model sees one series and estimates its parameters from that series only. A global model in functime, as the README describes it, fits across the panel, so a short series borrows structure from the longer ones. That helps when many of your series are short, which is the common case in retail and demand planning. It hurts when one series has genuinely different dynamics, because the shared model will pull its forecasts toward the panel average.
The other difference is where the work happens. A per-series loop keeps each series in its own pandas object and pays Python overhead per iteration. functime keeps everything in one Polars frame and pushes the work into the query engine, which is why the feature extraction examples can use group_by and group_by_dynamic instead of a loop. If your panel is small, say a few dozen series, the per-series library is simpler to reason about and gives you per-series diagnostics that functime's global fit does not surface. The crossover point is a judgement call the README does not define.
Maintenance, release cadence and licence
The release history is worth reading before you depend on this. v0.9.4 shipped in December 2023, v0.9.5 in February 2024, and then nothing until v1.0.0 in May 2026, a gap of roughly two years between the last 0.9 release and the 1.0 tag. That pattern suggests a project that went quiet and then re-emerged with a stable API, but the material here does not explain the gap, so do not assume a steady stream of patch releases going forward. Plan for the possibility that you are the one tracking upstream Polars changes.
functime is Apache-2.0. That is a permissive licence, which generally means you can use it in commercial and closed-source products, but it also carries an explicit patent grant and requires you to preserve the licence and notices. Nothing here is legal advice; if you are redistributing functime inside a product, read the licence text and your own counsel's guidance rather than this summary.
On upgrade cost: the extras are the moving parts. functime[lgb], functime[cat] and functime[xgb] each pull a separate gradient boosting library, and functime[llm] pulls an LLM client stack. A v1.0.0 to v1.x upgrade is likely to be cheap for the feature extraction path, which is mostly expression definitions, and more expensive for the forecasting path if the FLAML integration or the forecaster signatures change. The README already shows two calling conventions for the same forecaster, constructed and functional, so both are part of the API surface you would need to re-verify.
Editorial conclusion
Adopt functime if your data is already a panel of many series with a shared frequency and you want features and forecasts computed in one Polars pass. Do not adopt it if you need probabilistic forecast intervals, irregular per-series timestamps, or a single very long series. Before committing, verify that your Polars version matches the one functime pins, that your entity and time columns survive a train_test_split without reordering, and that the forecaster you pick has the extra installed (functime[lgb], functime[cat], functime[xgb]).
Community notes