%23%20%2F%2F%2F%20script%0A%23%20requires-python%20%3D%20%22%3E%3D3.11%22%0A%23%20dependencies%20%3D%20%5B%0A%23%20%20%20%20%20%22scikit-learn%22%2C%0A%23%20%20%20%20%20%22yohou%5Bplotting%5D%22%2C%0A%23%20%5D%0A%23%20%2F%2F%2F%0A%0Aimport%20marimo%0A%0A__generated_with%20%3D%20%220.23.8%22%0Aapp%20%3D%20marimo.App(width%3D%22medium%22)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_()%3A%0A%20%20%20%20import%20marimo%20as%20mo%0A%0A%20%20%20%20return%20(mo%2C)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%20Observe-Predict%20Workflow%0A%0A%20%20%20%20In%20production%20forecasting%20you%20rarely%20retrain%20from%20scratch%20each%20time%20new%0A%20%20%20%20data%20arrives.%20Instead%20you%20**observe**%20new%20data%20(update%20memory%2Fstate)%20and%0A%20%20%20%20then%20**predict**%20using%20the%20already-fitted%20model.%20This%20notebook%20walks%0A%20%20%20%20through%20a%20two-year%20test%20set%20in%20six-month%20batches%2C%20comparing%20a%0A%20%20%20%20single-shot%20prediction%20against%20a%20full%20walk-forward%20loop.%0A%0A%20%20%20%20%23%23%20Prerequisites%0A%0A%20%20%20%20Familiarity%20with%20%5B%60PointReductionForecaster%60%5D(%2Fpages%2Fapi%2Fgenerated%2Fyohou.point.reduction.PointReductionForecaster%2F)%20and%20%60fit%2Fpredict%60%0A%20%20%20%20(see%20%5BGetting%20Started%5D(%2Fpages%2Ftutorials%2Fgetting-started%2F)).%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_()%3A%0A%20%20%20%20import%20polars%20as%20pl%0A%20%20%20%20from%20sklearn.linear_model%20import%20Ridge%0A%0A%20%20%20%20from%20yohou.compose%20import%20FeaturePipeline%0A%20%20%20%20from%20yohou.datasets%20import%20fetch_tourism_monthly%0A%20%20%20%20from%20yohou.metrics%20import%20MeanAbsoluteScaledError%0A%20%20%20%20from%20yohou.model_selection%20import%20train_test_split%0A%20%20%20%20from%20yohou.plotting%20import%20plot_forecast%0A%20%20%20%20from%20yohou.point%20import%20PointReductionForecaster%0A%20%20%20%20from%20yohou.preprocessing%20import%20LagTransformer%0A%20%20%20%20from%20yohou.stationarity%20import%20SeasonalDifferencing%0A%0A%20%20%20%20return%20(%0A%20%20%20%20%20%20%20%20FeaturePipeline%2C%0A%20%20%20%20%20%20%20%20LagTransformer%2C%0A%20%20%20%20%20%20%20%20MeanAbsoluteScaledError%2C%0A%20%20%20%20%20%20%20%20PointReductionForecaster%2C%0A%20%20%20%20%20%20%20%20Ridge%2C%0A%20%20%20%20%20%20%20%20SeasonalDifferencing%2C%0A%20%20%20%20%20%20%20%20fetch_tourism_monthly%2C%0A%20%20%20%20%20%20%20%20pl%2C%0A%20%20%20%20%20%20%20%20plot_forecast%2C%0A%20%20%20%20%20%20%20%20train_test_split%2C%0A%20%20%20%20)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%201.%20Load%20and%20Prepare%20Data%0A%0A%20%20%20%20We%20use%20a%20single%20tourism%20series%20(T1)%20and%20hold%20out%20the%20final%2024%20months%0A%20%20%20%20as%20the%20test%20set%20with%20a%206-month%20forecasting%20horizon.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(fetch_tourism_monthly%2C%20train_test_split)%3A%0A%20%20%20%20bunch%20%3D%20fetch_tourism_monthly()%0A%20%20%20%20y%20%3D%20(%0A%20%20%20%20%20%20%20%20bunch.frame%0A%20%20%20%20%20%20%20%20.select(%22time%22%2C%20%22T1__tourists%22)%0A%20%20%20%20%20%20%20%20.rename(%7B%22T1__tourists%22%3A%20%22tourists%22%7D)%0A%20%20%20%20%20%20%20%20.drop_nulls()%0A%20%20%20%20)%0A%0A%20%20%20%20forecasting_horizon%20%3D%206%0A%20%20%20%20y_train%2C%20y_test%20%3D%20train_test_split(y%2C%20test_size%3D24)%0A%0A%20%20%20%20print(f%22Series%20length%3A%20%7Blen(y)%7D%20months%22)%0A%20%20%20%20print(f%22Train%3A%20%7Blen(y_train)%7D%20months%2C%20Test%3A%20%7Blen(y_test)%7D%20months%22)%0A%20%20%20%20return%20forecasting_horizon%2C%20y_test%2C%20y_train%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%202.%20Fit%20the%20Forecaster%0A%0A%20%20%20%20Build%20a%20Ridge%20forecaster%20with%20seasonal%20differencing%20and%20lag%20features.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(%0A%20%20%20%20FeaturePipeline%2C%0A%20%20%20%20LagTransformer%2C%0A%20%20%20%20PointReductionForecaster%2C%0A%20%20%20%20Ridge%2C%0A%20%20%20%20SeasonalDifferencing%2C%0A%20%20%20%20forecasting_horizon%2C%0A%20%20%20%20y_train%2C%0A)%3A%0A%20%20%20%20forecaster%20%3D%20PointReductionForecaster(%0A%20%20%20%20%20%20%20%20estimator%3DRidge()%2C%0A%20%20%20%20%20%20%20%20target_transformer%3DSeasonalDifferencing(seasonality%3D12)%2C%0A%20%20%20%20%20%20%20%20feature_transformer%3DFeaturePipeline(%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20(%22lags%22%2C%20LagTransformer(lag%3D%5B1%2C%202%2C%203%2C%2012%5D))%2C%0A%20%20%20%20%20%20%20%20%5D)%2C%0A%20%20%20%20)%0A%20%20%20%20forecaster.fit(y_train%2C%20forecasting_horizon%3Dforecasting_horizon)%0A%20%20%20%20return%20(forecaster%2C)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%203.%20Single-Shot%20Predict%0A%0A%20%20%20%20A%20single-shot%20forecast%20produces%20one%20batch%20of%20predictions%20from%20the%20end%0A%20%20%20%20of%20the%20training%20data%2C%20covering%20the%20first%20six%20months%20of%20the%20test%20period.%0A%20%20%20%20All%20rows%20share%20the%20same%20%60vintage_time%60.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(forecaster%2C%20forecasting_horizon)%3A%0A%20%20%20%20y_pred_single%20%3D%20forecaster.predict(forecasting_horizon%3Dforecasting_horizon)%0A%20%20%20%20print(y_pred_single)%0A%20%20%20%20return%20(y_pred_single%2C)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%204.%20Walk-Forward%20with%20observe_predict%0A%0A%20%20%20%20%60observe_predict%60%20steps%20through%20the%20test%20set%20in%20batches%20of%20size%20%60stride%60%2C%0A%20%20%20%20observing%20actual%20values%20and%20issuing%20a%20fresh%20forecast%20after%20each%20batch.%0A%20%20%20%20Setting%20%60stride%3Dforecasting_horizon%60%20tiles%20the%20test%20set%20with%20no%20gaps%0A%20%20%20%20or%20overlaps.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(forecaster%2C%20forecasting_horizon%2C%20y_test)%3A%0A%20%20%20%20y_pred_loop%20%3D%20forecaster.observe_predict(y%3Dy_test%2C%20stride%3Dforecasting_horizon)%0A%20%20%20%20print(f%22Total%20predictions%3A%20%7Blen(y_pred_loop)%7D%22)%0A%20%20%20%20print(f%22Distinct%20vintage_times%3A%20%7By_pred_loop%5B'vintage_time'%5D.n_unique()%7D%22)%0A%20%20%20%20print(y_pred_loop.head(12))%0A%20%20%20%20return%20(y_pred_loop%2C)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%205.%20Score%20and%20Compare%0A%0A%20%20%20%20Filter%20predictions%20to%20the%20test%20period%20and%20compare%20single-shot%20and%0A%20%20%20%20walk-forward%20MASE.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(%0A%20%20%20%20MeanAbsoluteScaledError%2C%0A%20%20%20%20forecasting_horizon%2C%0A%20%20%20%20pl%2C%0A%20%20%20%20y_pred_loop%2C%0A%20%20%20%20y_pred_single%2C%0A%20%20%20%20y_test%2C%0A%20%20%20%20y_train%2C%0A)%3A%0A%20%20%20%20y_pred_in_test%20%3D%20y_pred_loop.filter(pl.col(%22time%22)%20%3C%3D%20y_test%5B%22time%22%5D%5B-1%5D)%0A%0A%20%20%20%20scorer%20%3D%20MeanAbsoluteScaledError(seasonality%3D12)%0A%20%20%20%20scorer.fit(y_train)%0A%20%20%20%20mase_loop%20%3D%20scorer.score(y_test%2C%20y_pred_in_test)%0A%20%20%20%20mase_single%20%3D%20scorer.score(y_test%5B%3Aforecasting_horizon%5D%2C%20y_pred_single)%0A%0A%20%20%20%20print(f%22Single-shot%20MASE%20(months%201%20to%206)%3A%20%20%7Bmase_single%3A.3f%7D%22)%0A%20%20%20%20print(f%22Walk-forward%20MASE%20(all%2024%20months)%3A%20%7Bmase_loop%3A.3f%7D%22)%0A%20%20%20%20return%20(y_pred_in_test%2C)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%206.%20Visualize%20the%20Predictions%0A%0A%20%20%20%20Plot%20all%20walk-forward%20prediction%20vintages%20against%20the%20actual%20test%20values.%0A%20%20%20%20Each%20vintage%20appears%20as%20a%20separate%20colored%20segment%20overlaid%20on%20the%0A%20%20%20%20actual%20test%20series.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(plot_forecast%2C%20y_pred_in_test%2C%20y_test%2C%20y_train)%3A%0A%20%20%20%20plot_forecast(y_test%2C%20y_pred_in_test%2C%20y_train%3Dy_train%5B-24%3A%5D)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%20Next%20Steps%0A%0A%20%20%20%20-%20**Exogenous%20features**%3A%20See%20the%20%5BExogenous%20Features%5D(%2Fpages%2Ftutorials%2Fexogenous-features%2F)%20tutorial%20for%20passing%20external%20regressors%20through%20the%20observe%2Fpredict%20loop%0A%20%20%20%20-%20**Interval%20forecasting**%3A%20See%20the%20%5BInterval%20Forecasting%5D(%2Fpages%2Ftutorials%2Finterval-forecasting%2F)%20tutorial%20for%20prediction%20intervals%20with%20conformal%20methods%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20app.run()%0A
2a9e1f32b884ebe8cc3156f08a91f50d