(+351) 21 24 10006  ·  info@bconcepts.pt
Carnaxide, Lisbon

How to create time-based samples for training/testing in Python for data

João Barros 13 de October de 2026 4 min read

Learn how to create time-based samples for training/testing in Python for data — an essential task for time series modeling that prevents data leakage and ensures realistic validation. The technique is useful for forecasting sales, web metrics, or any series where chronological order matters.

Prerequisites

  • Python 3.8+ installed
  • Packages: pandas, scikit-learn, numpy (install with pip if needed)
  • Basic knowledge of pandas and train/test in machine learning

Step 1: Why do temporal splitting instead of shuffle

In temporal data, shuffling can cause data leakage: information from the future enters the training set and inflates the metric. Temporal splitting preserves chronological order — training with older data and testing with more recent data.

Step 2: Minimal data structure

We start with a simple DataFrame that has a date column and a target variable. We ensure the date column is datetime and sort by it.

import pandas as pd
import numpy as np

# Exemplo mínimo
np.random.seed(42)
dates = pd.date_range('2022-01-01', periods=100, freq='D')
value = np.random.randn(100).cumsum() + 50

df = pd.DataFrame({'date': dates, 'value': value})
# Converter e ordenar
df['date'] = pd.to_datetime(df['date'])
df = df.sort_values('date').reset_index(drop=True)
print(df.head())

Step 3: Simple temporal train/test split (holdout)

A common approach is to use a final percentage as test (for example, the last 20%). This is simple and prevents temporal mixing.

# Proporção de teste
test_size = 0.2
n = len(df)
split = int(n * (1 - test_size))

train = df.iloc[:split].reset_index(drop=True)
test = df.iloc[split:].reset_index(drop=True)

print('Train:', train['date'].min(), '->', train['date'].max())
print('Test:', test['date'].min(), '->', test['date'].max())

Step 4: Temporal validation with rolling windows

To evaluate model stability, we use temporal cross-validation (rolling/expanding window). Here we create sequential splits where each fold trains on data up to a point t and tests on a future block.

from sklearn.model_selection import TimeSeriesSplit

# Exemplo com TimeSeriesSplit (k folds)
X = df[['value']].copy()  # features fictícias
y = df['value']

tscv = TimeSeriesSplit(n_splits=5)
for i, (train_idx, test_idx) in enumerate(tscv.split(X)):
    print(f'Fold {i+1}: train {train_idx[0]}-{train_idx[-1]}, test {test_idx[0]}-{test_idx[-1]}')

Step 5: Walk-forward window and fixed test blocks

If you need to forecast a fixed horizon (e.g. 7 days), create a generator that yields windows where the test has fixed length and the training expands or slides.

def rolling_windows(df, train_size, test_size, step=1):
    n = len(df)
    start = 0
    while True:
        train_end = start + train_size
        test_end = train_end + test_size
        if test_end > n:
            break
        yield df.iloc[start:train_end].reset_index(drop=True), df.iloc[train_end:test_end].reset_index(drop=True)
        start += step

# Exemplo
for i, (tr, te) in enumerate(rolling_windows(df, train_size=60, test_size=7, step=7)):
    print(f'Window {i+1}: train {tr.date.min().date()}->{tr.date.max().date()}, test {te.date.min().date()}->{te.date.max().date()}')

Step 6: Avoid leaks with future-based features

When creating features (lags, rolling means) ensure you only use information up to t-1 to predict t. Use shift() in pandas to create lags and avoid functions that aggregate future values.

# Exemplo de lags e média móvel sem data leakage
df['lag_1'] = df['value'].shift(1)
df['rolling_7'] = df['value'].shift(1).rolling(window=7, min_periods=1).mean()
# Remover primeiras linhas com NaN resultantes
df_feat = df.dropna().reset_index(drop=True)
print(df_feat.head())

Check the result

Confirm that training dates are always earlier than testing dates and that training features do not contain future values. Check the sizes of the sets and print a sample of the first rows after creating lags/rolling.

# Verificações rápidas
assert train['date'].max() < test['date'].min(), 'Data leakage: sobreposição temporal!'
print('Tamanhos: ', len(train), len(test))
print(df_feat[['date','value','lag_1','rolling_7']].head())

Conclusion

Splitting time series correctly is critical to avoid data leakage and obtain realistic metrics. Use temporal holdout for simple tests, TimeSeriesSplit for more robust validation, and rolling windows to assess stability over time. Next step: train a simple model (e.g.: RandomForest, XGBoost) using the lags and validate with temporal splits. Tip: always visualize the dates and windows to ensure there was no overlap.