To forecast tomorrow with an ordinary regression model, you have to hand it the past as columns. A lag feature is exactly that: "the value k steps ago", turned into its own feature.
Task: write lag_features(series, lags) returning one row per time step.
t holds series[t - lag] for each lag in lags, in the order lags gives.t - lag is negative that value is in the past of the data itself and doesn't exist, so put None there.None.lag of 0 means the current value.So the early rows are necessarily incomplete, and what you do about that is the real decision. Most pipelines drop any row containing a None before training — at the cost of losing the start of the series, which hurts when you only have a few hundred points and want a lag of 30. Keeping them visible, as this function does, makes that cost explicit rather than silently deciding for you.
One thing never to do here: reach forward. A feature built from series[t + 1] leaks the future into the model, which then scores beautifully offline and fails completely in production, where tomorrow's value isn't available yet. Every lag must be non-negative, and that constraint is the whole discipline of time-series feature engineering in one line.