Skip to content
datarekha
Time Series Easy Asked at AmazonAsked at MicrosoftAsked at Airbnb

Why can't you shuffle a time series before splitting into train and test sets?

The short answer

Usually, shuffling a time series makes evaluation optimistic because training rows can come from after the date being predicted, so the test measures interpolation rather than forecasting. Split at a time cutoff and use walk-forward validation; random splitting is defensible only when the production task is genuinely a random holdout and every feature is available at prediction time.

How to think about it

Usually, you should not shuffle a time series before splitting it: random rows let observations after the forecast date influence training, so the test measures predicting the past with knowledge of the future rather than forecasting the future from the past. Split at a time cutoff and use walk-forward validation; random splitting is valid only when the production task itself is a random holdout or interpolation problem and every feature is available at prediction time.

Why shuffling changes the question

Suppose I am building a model for an online retailer that predicts daily orders. On September 30, the real system must predict October 1 using information available through September 30. It cannot use October 15 orders, because October 15 has not happened yet.

That is the production question:

Given the past, how accurately can I predict the future?

A random split asks a different question:

If I hide 20 percent of historical rows, can I predict those rows using the other 80 percent?

Those questions are equivalent only when observations are independent and identically distributed, meaning one row does not depend on its neighbors and the data-generating process does not change over time. A forecasting series usually violates both assumptions.

Temporal leakage means information from after a prediction cutoff enters the model’s training process or its features. With a random split, a training row can occur after a validation row. The model may not receive the future target as a named input, but its fitted parameters are still estimated using future outcomes.

Imagine daily orders rising from 100 in January to 160 in December because the retailer is expanding its advertising. An 80/20 random split puts examples from December in training while some January, May, and October examples sit in the test set. The model can use the December outcomes to estimate the year’s trend. That may improve its score on a hidden May row, but a model trained on January 31 could not have learned that trend from December outcomes.

The score is not necessarily measuring a useless model. It is measuring the wrong deployment scenario.

Time series also have serial correlation, which means nearby observations tend to be related because conditions persist from one time point to the next. Orders on Tuesday are often related to orders on Monday. Electricity demand at 10:00 is related to demand at 09:00. A customer’s purchase today may be related to yesterday’s activity.

Randomly separating neighboring observations allows the training set to contain unusually close clues about a test observation. That makes the test less like an unseen future period and more like a missing puzzle piece surrounded by pieces from both sides.

The leak becomes obvious with lag features

Most forecasting models use features such as:

  • yesterday’s orders;
  • the average orders over the previous seven days;
  • the number of active users in the previous hour;
  • a rolling conversion rate.

A lag feature is a value from an earlier time, such as orders[t - 1]. A rolling feature is a statistic calculated over a moving historical window, such as the average of the previous seven orders.

Consider a random split where February 15 is in validation and February 16 is in training. If the lag features were created before splitting, the training row for February 16 contains February 15’s order count as lag_1. The validation target has entered the training feature matrix.

That is direct contamination. It may not be a literal copy of the validation target into the validation row, but the training process has still been allowed to see information from the validation period. With rolling aggregates, target encodings, and features built from future transactions, the contamination can be even more direct.

There is a second, separate mistake: computing rolling values after shuffling the rows. In that case, “the previous seven rows” may mean seven random dates rather than the previous seven days. The resulting feature is not a historical feature at all. It is a statistic of an arbitrary permutation.

The safe pattern is to sort by time, construct features from prior observations, and then make a chronological split:

import pandas as pd

df = (
    pd.read_csv("orders.csv", parse_dates=["date"])
      .sort_values("date")
)

df["lag_1"] = df["orders"].shift(1)
df["rolling_7"] = df["orders"].shift(1).rolling(7).mean()

df = df.dropna(subset=["lag_1", "rolling_7"])

cutoff = pd.Timestamp("2024-09-30")
train = df[df["date"] <= cutoff].copy()
test = df[df["date"] > cutoff].copy()

For daily data in 2024, this gives 274 training dates through September 30 and 92 test dates from October 1 through December 31, assuming there is one row for every calendar day. The first test feature can use September 30’s observed orders. It cannot use October 1’s orders.

There is an important forecast-protocol detail here. This feature construction is suitable for one-day-ahead forecasting, where the system predicts October 1, observes the actual result, then uses it while predicting October 2. If the business needs one forecast for all 92 days at once, the actual October 1 value is not available when predicting October 2. The evaluation must then use recursive predictions or features that do not depend on future test targets.

The correct validation pattern

A single chronological holdout is appropriate for the final evaluation. Train on the past and test on the next untouched block.

For model selection and hyperparameter tuning, I would use walk-forward validation, also called rolling-origin validation. Each validation period comes after its corresponding training period:

  • train on January through March, validate on April;
  • train on January through April, validate on May;
  • train on January through May, validate on June.

This is called an expanding window because the training set grows. It answers the operational question repeatedly: “If I had reached this date, how would the model have performed on the next period?”

A rolling window uses only a fixed recent history, such as the latest 90 days. That can be better when old data has become irrelevant. A retailer whose customer base changed after a pricing overhaul may learn more from the latest 90 days than from five-year-old orders. The trade-off is less training data and potentially higher variance.

The final test period must remain untouched while choosing features, hyperparameters, thresholds, and retraining rules. Otherwise it quietly becomes another validation set.

Sometimes a gap is needed between training and validation. Suppose the target is the total orders during the next seven days. A training example near the boundary may cover September 25 through October 1, while a validation example starts on October 1. The labels overlap. Even though the rows have different forecast origins, they share an outcome period. Leaving a gap of at least the relevant horizon, or using a purged time split, prevents that overlap from making the score optimistic.

Preprocessing must respect the same boundary. Fit scalers, imputers, encoders, feature selectors, and target-based statistics inside each training fold. A chronological row split does not fix a scaler fitted on the entire dataset. That scaler has still used future distribution information.

Warning: Setting shuffle=False is necessary for a basic chronological split, but it is not a complete fix. A feature calculated with future rows, a scaler fitted globally, actual weather used instead of the weather forecast available at prediction time, or an inclusive boundary that appears in both sets can still leak information.

When random splitting can be reasonable

“Never shuffle” is a good forecasting rule, not a law of nature.

Random splitting can be reasonable when the production task is also random holdout prediction. For example, if the goal is to fill randomly missing historical measurements and the model will have access to observations from both before and after the missing timestamp, a random split may match deployment. It can also be reasonable for genuinely independent observations where time is merely a recording field.

The test is point-in-time availability. For every feature, ask: “At the exact moment this prediction would have been made, could the system have known this value?”

A calendar month is usually available in advance. Actual end-of-day revenue is not. A weather forecast may be available; the final observed temperature may not be. A fraud model used on incoming transactions may be evaluated differently from a model forecasting next month’s fraud volume.

For panel data, where each row might be a store-day or user-day, the split must match the business question. If I am forecasting next week’s sales for existing stores, I should split all stores by date. If I am evaluating performance on entirely new stores, I also need to hold out store identities. A random store-day split can put the same store’s future behavior in training while testing its past, producing an easy but misleading result.

Failure modes and what you see first

The classic symptom is an impressive offline metric followed by a painful production drop. A shuffled evaluation might report 2 percent MAPE, while live predictions show 15 percent MAPE after launch. That gap is not proof of a specific bug, but it is a strong signal to inspect temporal leakage, feature availability, and regime changes before blaming the model.

Another symptom is that the model performs unusually well near the split boundary, then fails when forecasting several steps ahead. This often means test features were built with actual test targets. The first prediction is valid, but later predictions receive information that would not exist in a real batch forecast.

A simple boundary check catches one common implementation error:

assert train["date"].max() < test["date"].min()

If that assertion fails, the same timestamp appears in both sets or the dates were not sorted as expected. It is a small check with an excellent return on investment.

What they’ll ask next

Does shuffling always cause direct data leakage?

No. It always destroys the chronological holdout, but it does not always put a future target directly into a feature. If the series is stationary, observations are effectively independent, and deployment is random missing-row prediction, random splitting may be appropriate. For a forward forecast, it usually estimates the wrong risk even when no feature is visibly contaminated.

Is using only the last 20 percent as test enough?

It is a sensible final test, but not always enough for model selection. One future block can be unusually easy or hard because of a holiday, outage, promotion, or regime change. Use several walk-forward validation periods for tuning, then evaluate once on the later untouched block. Report performance by horizon and by important regimes, not only one average number.

What matters more: the timestamp or feature availability?

Both. A chronological split is about when the target occurs. Point-in-time feature construction is about when each input became knowable. A model can have perfectly ordered rows and still use revised revenue, future weather observations, or a rolling statistic that includes the target day. In production, the prediction timestamp and the data-availability timestamp must agree.

Say this in the interview: “I do not shuffle a forecasting series because random splitting lets future periods influence training and tests interpolation instead of future generalization; I use a chronological holdout and walk-forward validation, with point-in-time features and a gap when horizons overlap.”

Learn it properly Why time series is different

Keep practising

All Time Series questions

Explore further