← Back to list

Machine Learning for Time Series: Building Workflows That Scale

Focus on pipelines and reproducibility across many series.

Zhe Sun, Phd · 2025-10-06 22:40 · 53 claps · 3.7 min read
#time-series-forecasting #machine-learning #time-series-analysis
Open on Medium ↗
Wiki topics: ML · Machine Learning EDU · Education & Learning ⏱️ · Productivity

Machine Learning for Time Series: Building Workflows That Scale

Focus on pipelines and reproducibility across many series.

[embed]time-series-analysis-toolkit/README.md at main · GeneSUN/time-series-analysis-toolkit A comprehensive toolkit for time series analysis, including scripts for visualizing results, detecting stationarity…github.com

This GitHub repository includes scripts and articles covering the full spectrum of time series techniques — Preprocessing, EDA, Modeling, and Evaluation. The current article is just one piece of that broader collection.

[embed]Google Colab Edit descriptioncolab.research.google.com

This notebook contains all the components covered in the article. It is recommended to use it to reproduce and practice everything discussed here.

Time series data is everywhere: energy consumption, stock prices, web traffic, weather, and beyond. A common challenge in time series machine learning is twofold:

  • How to transform long sequences into a tabular format suitable for modeling;

  • How to structure predictive models when working with many series — (1). should we train one model per series, or (2). build a single model that generalizes across them?

The first question is already answered in **colab notebook and [article](https://medium.com/@injure21/transform-time-series-data-for-supervised-learning-from-sequence-to-samples-a7b12306b077)**,

This post focus on the second question, walks through three practical approaches, with code examples drawn from a working notebook:

  1. Building a single model for one time series.
  2. Training many models, one for each series.
  3. Using pipelines to streamline preprocessing and modeling.

1. Single-Series Modeling: Start Simple

The first step is to pick one series — for example, energy consumption in a single household — and build a regression model to forecast it.

We begin by engineering features:

  • Lag features: yesterday’s and last week’s consumption.
  • Rolling statistics: 7-day averages or standard deviations.
  • Seasonal rolling features: same-hour averages from prior weeks.
  • Calendar encodings: month, day of week, time of day (with sin/cos encoding).
  • Weather & exogenous features: temperature, humidity, holidays, etc.

Using scikit-learn, we can preprocess continuous, categorical, and boolean features with a ColumnTransformer, then feed them into a simple Ridge regression:

preprocessor = ColumnTransformer(
    transformers=[
        ("num", StandardScaler(), continuous_features),
        ("cat", OneHotEncoder(handle_unknown="ignore"), categorical_features),
        ("bool", "passthrough", boolean_features),
    ]
)

model = Pipeline(steps=[
    ("preprocessor", preprocessor),
    ("regressor", Ridge(alpha=1.0))
])

Evaluation uses metrics like MAE, RMSE, and bias.

By looking at the feature importance, we found:

  • Current day energy consumption is significantly affected by previous day.
  • Special holiday, such as Black Friday (day 342, 343,344 of the year), Chrismas Eve, has significant affect of energy consumption.

2. Scaling Up: Many Series, Many Models

What if we want forecasts for hundreds of households?

One approach is to replicate the single-series workflow, but train a separate model per household (or per product, per sensor, etc.).

This setup has pros and cons:

  • ✅ Each model can capture idiosyncrasies of its series.
  • ❌ Training and maintaining hundreds or thousands of models becomes computationally expensive and operationally complex.

Still, for moderate scale (say 100–200 entities), it’s a practical way to achieve accuracy while reusing a familiar pipeline.

Pipelines: Making It Reproducible

Feature engineering for time series can quickly become messy. Pipelines solve this by

  • Bundling preprocessing, modeling, and evaluation into a single reproducible workflow.
  • Swapping models or adjust parameters without rewriting feature code.
  • Scaling seamlessly across hundreds of series.
#@title 1.ModelRunner
import pandas as pd
from sklearn.compose import ColumnTransformer
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import OneHotEncoder, StandardScaler

class ModelRunner:
    def __init__(self, feature_dict, model_cls, model_params=None,
                 use_scaler=True, use_onehot=True):
        self.feature_dict = feature_dict
        self.model_cls = model_cls
        self.model_params = model_params or {}
        self.use_scaler = use_scaler
        self.use_onehot = use_onehot
        self.pipeline = self._build_pipeline()

    def _build_pipeline(self):
        transformers = []

        if self.use_scaler and self.feature_dict["continuous_features"]:
            transformers.append(("num", StandardScaler(), self.feature_dict["continuous_features"]))
        elif self.feature_dict["continuous_features"]:
            transformers.append(("num", "passthrough", self.feature_dict["continuous_features"]))

        if self.use_onehot and self.feature_dict["categorical_features"]:
            transformers.append(("cat", OneHotEncoder(handle_unknown="ignore"), self.feature_dict["categorical_features"]))
        elif self.feature_dict["categorical_features"]:
            transformers.append(("cat", "passthrough", self.feature_dict["categorical_features"]))

        if self.feature_dict["boolean_features"]:
            transformers.append(("bool", "passthrough", self.feature_dict["boolean_features"]))

        preprocessor = ColumnTransformer(transformers, remainder="drop")
        model = self.model_cls(**self.model_params)

        return Pipeline(steps=[
            ("preprocessor", preprocessor),
            ("regressor", model)
        ])

    def fit(self, X, y):
        self.pipeline.fit(X, y)

    def predict(self, X):
        return self.pipeline.predict(X)

The notebook shows how you can use this ModelRunner to fit/predict multiple model for one series, and then expand to hundreds of series:


all_preds = []
all_metrics = []

for config in model_configs:
    runner = ModelRunner(
        feature_dict=feature_dict,
        model_cls=config["cls"],
        model_params=config.get("params", {}),
        use_scaler=config.get("use_scaler", True),
        use_onehot=config.get("use_onehot", True)
    )

    model_name = config["name"]

    for lcl_id in tqdm(lcl_ids, desc=f"Running {model_name}"):
        preds_df, metrics_df = run_model_for_customer(
                  runner, 
                  model_name = model_name, 
                  lcl_id = lcl_id, 
                  train_df = train_df, 
                  test_df = test_df, 
                  feature_dict = feature_dict)

        all_preds.append(preds_df)

        metrics_df["LCLid"] = lcl_id
        metrics_df["model"] = model_name
        all_metrics.append( metrics_df)

all_preds = pd.concat(all_preds, ignore_index=True)
all_metrics = pd.concat(all_metrics, ignore_index=True)

*Model Config

Instead of using sklearn pipeline, you can also use dataclass, feel free to try it. Below is the example:

[embed]Google Colab Edit descriptioncolab.research.google.com

Closing Thoughts

Machine learning for time series isn’t just about finding the “best” algorithm. It’s about designing workflows that scale from one dataset to many, while balancing accuracy, interpretability, and engineering complexity.

In future posts, we’ll explore how global forecasting models (GFMs) can replace “one-model-per-series” with a single unified model across all entities — unlocking massive efficiency gains at scale.


메타데이터
post_id
c0136bd321a9
slug
machine-learning-for-time-series-prediction-c0136bd321a9
url
https://medium.com/@injure21/machine-learning-for-time-series-prediction-c0136bd321a9
canonical_url
https://medium.com/@injure21/machine-learning-for-time-series-prediction-c0136bd321a9
author_url
https://medium.com/@injure21
status
ok
fetched_at
2026-06-25 12:15:08