forecast_pairs#
forecast_pairs pairs historical features with a future target so that a model
can learn to predict count events (or duration index-units) ahead. Row t
in the output holds the features from count events ago aligned with the target
value that is known at time t. The pairing is fully causal: it lags X,
never leads y, so nothing here peeks into the future.
Causal framing#
The target y must itself be causal, meaning its value at index t is fully
determined by data available at or before t. A trailing rolling sum or
exponential mean satisfies this; a centered window does not. forecast_pairs
shifts the features backward in time by count events (or by duration
index-units), then aligns the shifted features to the target clock. The result
is a training set where knowing X at time t - count lets a model predict
y at time t.
Parameters#
X: feature array (1-D or 2-D) forcount=mode, or a(values, index)tuple forduration=mode.y: target array (1-D) forcount=mode, or a(values, index)tuple forduration=mode. Must be causal.count=(int): shiftXby this many events. Event-based; no index needed. Mutually exclusive withduration=.duration=(numeric): shiftX's index by this many index-units viaDelay. Time-based; requires an explicit index on bothXandy. Mutually exclusive withcount=.dropna=False: ifTrue, drop rows whereX_shiftedorycontains NaN. The firstcountrows ofX_shiftedare NaN (the warmup period where no lagged feature exists yet);dropna=Trueremoves them automatically.
Returns#
A three-tuple (X_shifted, y, as_of):
X_shifted: the features shifted back by the forecast horizon.y: the target series, unchanged.as_of: each row's completion index (the time when the target is realized). Forcount=mode this is a zero-based integer position. Forduration=mode this is the target's index.
One-of constraint#
Exactly one of count= or duration= must be supplied. Passing both or
neither raises ValueError.
Example#
import numpy as np
from screamer import RollingSum
from screamer.supervised import forecast_pairs
rng = np.random.default_rng(42)
X = rng.standard_normal(200)
# Build a causal target: sum of the next h events, but expressed as
# a trailing RollingSum so it is causal at each step.
h = 5
y = RollingSum(h)(X)
X_shifted, y_train, as_of = forecast_pairs(X, y, count=h, dropna=True)
print("training rows:", len(X_shifted))
print("X_shifted[:3]:", X_shifted[:3].round(4))
print("y_train[:3] :", y_train[:3].round(4))
print("as_of[:3] :", as_of[:3])
training rows: 195
X_shifted[:3]: [ 0.3047 -1.04 0.7505]
y_train[:3] : [-2.6022 -1.4344 -2.5011]
as_of[:3] : [5 6 7]