Design the feature set for a global daily demand model over 400,000 SKU-store pairs
A grocery chain wants one daily demand forecast for each of 400,000 SKU-store pairs, 14 days ahead, refreshed every night. You have two years of daily sales, a planned-promotion table (each promo is entered about 3 weeks ahead), a price table, a product catalogue (category, brand, pack size), a store table (format, region) and a holiday calendar. About 12% of the pairs have fewer than 60 days of history, and sales per pair range from 0.2 to 900 units a day.
- Why one global model rather than 400,000 per-series models? Name two situations where you would still keep a local model.
- List the feature families you would build and say which ones need extra care to avoid leakage. Give the minimum lag you allow, and why.
- How do you handle the 900-versus-0.2 scale spread? Say what you divide by, over which window, and what you check afterwards.
- A colleague wants to add the SKU-store id as a categorical feature. Do you? What do you do for a pair with no history?
1. Global versus local. A global model fits one function on the stacked panel, so every pair borrows the weekly shape, holiday response, promo lift and price response from all the others. A per-series model re-estimates those from its own history, which is noisy and, for the 12% short series, close to impossible. Simulation evidence from this lesson (verified): with 120 days per series, a global boosted model on 20 pooled series already beat per-series ETS in MASE (0.893 against 0.947), 1,000 pooled series reached 0.862, and the per-series boosted model (0.995) was worse than naive-plus-ETS. There is also an operational argument: one model to train, version and monitor instead of 400,000. Keep a local model (a) for a handful of long, high-value series with idiosyncratic structure that an analyst can model, and (b) as an ensemble member and a sanity baseline next to the global model (ETS or seasonal naive), with weights chosen on the backtest.
2. Features.
- Lags and rolling statistics of the target, all at least 14 days back (the horizon), or you must use a direct or recursive scheme. A lag shorter than the horizon cannot be computed for day 14 at forecast time. Rolling windows must end at the origin, never include the target day (a 7-day mean that includes the current day cut the backtest WAPE from 0.325 to 0.279 in the simulation: a fake gain).
- Calendar: weekday, month, day-of-year, holiday, days to and since the next holiday, payday.
- Known-future: planned promo flag, promo type, days since promo start and to end, lead and lag windows for pull-forward and hangover, planned price and relative price versus the category. Leakage risk: use the value that was known at the forecast origin (promos enter about 3 weeks ahead), not the final realised table. Keep point-in-time snapshots of these tables.
- Static: category, brand, pack size, store format, region, the series scale, and the age since launch. Use real attributes, not an id.
- Cross-series: category-by-store lags and rolling means, which carry signal a noisy single series cannot.
3. Scale. Divide the target and every lag or rolling feature by a per-series scale computed on the training window only, for example the mean of the last 56 training days plus a small constant, train on the unitless values and multiply back, and give the log of the scale as a feature. Check the result by scale segment, not only pooled: in the simulation, pooled WAPE moved only 0.338 to 0.332 (2%) while the smallest-scale fifth moved 0.614 to 0.498 (19%). Also check bias by segment, because log1p targets without correction came out 10.9% low. Never compute the scale on data that include the test window.
4. Series id. Usually not. In the simulation the id added nothing beyond scaled lags (WAPE 0.330 without, 0.331 as an integer, 0.330 as a categorical on trained series; 0.344, 0.344 and 0.342 on unseen series), it invites overfitting of a high-cardinality categorical, and it does nothing for new pairs, the case that matters most. Prefer attributes and out-of-fold target encodings computed on past data only. For a pair with no history, forecast from its attributes and its category-store aggregates (the model can run with lag features set to missing and the category lags filled), or borrow the curve of the nearest analogue SKUs; after 3 to 5 weeks the ordinary lag features take over. A global model trained on other series forecast unseen series with enough history as well as one trained on them (WAPE 0.334 either way, verified).
Share this question