%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%20Stationarity%20of%20Panel%20Data%0A%0A%20%20%20%20Stationarity%20transformations%20and%20decomposition%20work%20per-group%20on%0A%20%20%20%20panel%20data.%20Each%20group%20gets%20its%20own%20trend%2C%20seasonality%2C%20and%20residual%0A%20%20%20%20extraction.%0A%0A%20%20%20%20%23%23%201.%20Load%20and%20Explore%20Panel%20Data%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%20from%20copy%20import%20deepcopy%0A%0A%20%20%20%20from%20sklearn.linear_model%20import%20Ridge%0A%0A%20%20%20%20from%20yohou.compose%20import%20DecompositionPipeline%0A%20%20%20%20from%20yohou.datasets%20import%20fetch_tourism_quarterly%0A%20%20%20%20from%20yohou.metrics%20import%20MeanAbsoluteError%0A%20%20%20%20from%20yohou.model_selection%20import%20train_test_split%0A%20%20%20%20from%20yohou.plotting%20import%20(%0A%20%20%20%20%20%20%20%20plot_forecast%2C%0A%20%20%20%20%20%20%20%20plot_score_per_vintage%2C%0A%20%20%20%20%20%20%20%20plot_score_time_series%2C%0A%20%20%20%20%20%20%20%20plot_time_series%2C%0A%20%20%20%20)%0A%20%20%20%20from%20yohou.point%20import%20PointReductionForecaster%2C%20SeasonalNaive%0A%20%20%20%20from%20yohou.preprocessing%20import%20LagTransformer%0A%20%20%20%20from%20yohou.stationarity%20import%20SeasonalDifferencing%0A%20%20%20%20from%20yohou.stationarity.seasonality%20import%20PatternSeasonalityForecaster%0A%20%20%20%20from%20yohou.stationarity.trend%20import%20PolynomialTrendForecaster%0A%20%20%20%20from%20yohou.utils.panel%20import%20inspect_panel%0A%0A%20%20%20%20return%20(%0A%20%20%20%20%20%20%20%20DecompositionPipeline%2C%0A%20%20%20%20%20%20%20%20LagTransformer%2C%0A%20%20%20%20%20%20%20%20MeanAbsoluteError%2C%0A%20%20%20%20%20%20%20%20PatternSeasonalityForecaster%2C%0A%20%20%20%20%20%20%20%20PointReductionForecaster%2C%0A%20%20%20%20%20%20%20%20PolynomialTrendForecaster%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%20SeasonalNaive%2C%0A%20%20%20%20%20%20%20%20deepcopy%2C%0A%20%20%20%20%20%20%20%20fetch_tourism_quarterly%2C%0A%20%20%20%20%20%20%20%20inspect_panel%2C%0A%20%20%20%20%20%20%20%20plot_forecast%2C%0A%20%20%20%20%20%20%20%20plot_score_per_vintage%2C%0A%20%20%20%20%20%20%20%20plot_score_time_series%2C%0A%20%20%20%20%20%20%20%20plot_time_series%2C%0A%20%20%20%20%20%20%20%20train_test_split%2C%0A%20%20%20%20)%0A%0A%0A%40app.cell%0Adef%20_(fetch_tourism_quarterly%2C%20inspect_panel%2C%20mo%2C%20train_test_split)%3A%0A%20%20%20%20_bunch%20%3D%20fetch_tourism_quarterly()%0A%20%20%20%20%23%20Select%208%20series%20with%20uniform%20length%20for%20a%20manageable%20panel%20demo%0A%20%20%20%20_selected%20%3D%20%5Bf%22T%7Bi%7D__tourists%22%20for%20i%20in%20range(3%2C%2011)%5D%0A%20%20%20%20tourism%20%3D%20_bunch.frame.select(%22time%22%2C%20*_selected).drop_nulls()%0A%20%20%20%20_globals%2C%20groups%20%3D%20inspect_panel(tourism)%0A%20%20%20%20y_train%2C%20y_test%20%3D%20train_test_split(tourism%2C%20test_size%3D0.2)%0A%20%20%20%20horizon%20%3D%20len(y_test)%0A%0A%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20f%22**Groups**%20(%7Blen(groups)%7D%20panel%20groups)%3A%20%7Blist(groups.keys())%7D%5Cn%5Cn%22%0A%20%20%20%20%20%20%20%20f%22**Train**%3A%20%7Blen(y_train)%7D%20quarters%2C%20**Test**%3A%20%7Blen(y_test)%7D%20quarters%22%0A%20%20%20%20)%0A%20%20%20%20return%20horizon%2C%20tourism%2C%20y_test%2C%20y_train%0A%0A%0A%40app.cell%0Adef%20_(plot_time_series%2C%20tourism)%3A%0A%20%20%20%20plot_time_series(tourism%2C%20title%3D%22Tourism%20Quarterly%3A%20All%20Panel%20Groups%22)%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%202.%20Seasonal%20Differencing%20on%20Panel%20Data%0A%0A%20%20%20%20%60SeasonalDifferencing(seasonality%3D4)%60%20removes%20quarterly%20seasonality%0A%20%20%20%20from%20each%20state%20independently.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(SeasonalDifferencing%2C%20plot_time_series%2C%20tourism)%3A%0A%20%20%20%20sd%20%3D%20SeasonalDifferencing(seasonality%3D4)%0A%20%20%20%20sd.fit(tourism)%0A%20%20%20%20tourism_diff%20%3D%20sd.transform(tourism)%0A%20%20%20%20plot_time_series(tourism_diff%2C%20title%3D%22Seasonal%20Differencing%20(lag%3D4)%3A%20All%20Groups%22)%0A%20%20%20%20return%20sd%2C%20tourism_diff%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(%22%22%22%0A%20%20%20%20The%20seasonal%20differencing%20transformer%20is%20perfectly%20invertible%20(within%20floating-point%20precision).%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(sd%2C%20tourism%2C%20tourism_diff)%3A%0A%20%20%20%20_inv%20%3D%20sd.inverse_transform(tourism_diff%2C%20tourism.head(sd.seasonality))%0A%20%20%20%20%23%20inverse_transform%20reconstructs%20the%20original%2C%20except%20for%20the%20first%204%20rows%0A%20%20%20%20_overlap%20%3D%20tourism.tail(len(_inv))%0A%20%20%20%20_err%20%3D%20_inv.drop(%22time%22).to_numpy()%20-%20_overlap.drop(%22time%22).to_numpy()%0A%20%20%20%20_max_err%20%3D%20abs(_err).max()%0A%20%20%20%20print(f%22Inverse%20transform%20check%3A%20max%20error%20%3D%20%7B_max_err%3A.2e%7D%22)%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%203.%20DecompositionPipeline%20on%20Panel%20Data%0A%0A%20%20%20%20Each%20group%20gets%20its%20own%20linear%20trend%20%2B%20residual%20model.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(%0A%20%20%20%20DecompositionPipeline%2C%0A%20%20%20%20LagTransformer%2C%0A%20%20%20%20PointReductionForecaster%2C%0A%20%20%20%20PolynomialTrendForecaster%2C%0A%20%20%20%20Ridge%2C%0A%20%20%20%20horizon%2C%0A%20%20%20%20y_train%2C%0A)%3A%0A%0A%20%20%20%20fc_decomp%20%3D%20DecompositionPipeline(%0A%20%20%20%20%20%20%20%20forecasters%3D%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20(%22trend%22%2C%20PolynomialTrendForecaster(degree%3D1))%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22residual%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20PointReductionForecaster(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20estimator%3DRidge(alpha%3D1.0)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20feature_transformer%3DLagTransformer(lag%3D%5B1%2C%204%5D)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20%20%20%20%20%5D%2C%0A%20%20%20%20%20%20%20%20store_residuals%3DTrue%2C%0A%20%20%20%20)%0A%20%20%20%20fc_decomp.fit(y_train%2C%20forecasting_horizon%3Dhorizon)%0A%20%20%20%20y_pred_decomp%20%3D%20fc_decomp.predict(forecasting_horizon%3Dhorizon)%0A%20%20%20%20return%20(y_pred_decomp%2C)%0A%0A%0A%40app.cell%0Adef%20_(plot_forecast%2C%20y_pred_decomp%2C%20y_test%2C%20y_train)%3A%0A%20%20%20%20plot_forecast(%0A%20%20%20%20%20%20%20%20y_test%2C%0A%20%20%20%20%20%20%20%20y_pred_decomp%2C%0A%20%20%20%20%20%20%20%20y_train%3Dy_train%2C%0A%20%20%20%20%20%20%20%20groups%3D%5B%22T3%22%2C%20%22T4%22%2C%20%22T5%22%5D%2C%0A%20%20%20%20%20%20%20%20title%3D%22DecompositionPipeline%3A%20Trend%20%2B%20Residual%20(Panel)%22%2C%0A%20%20%20%20)%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%204.%20Trend%20%2B%20Seasonality%20%2B%20Residual%0A%0A%20%20%20%20Add%20a%20%5B%60PatternSeasonalityForecaster%60%5D(%2Fpages%2Fapi%2Fgenerated%2Fyohou.stationarity.seasonality.PatternSeasonalityForecaster%2F)%20between%20trend%20and%20residual%0A%20%20%20%20for%20a%20three-component%20decomposition.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(%0A%20%20%20%20DecompositionPipeline%2C%0A%20%20%20%20LagTransformer%2C%0A%20%20%20%20PatternSeasonalityForecaster%2C%0A%20%20%20%20PointReductionForecaster%2C%0A%20%20%20%20PolynomialTrendForecaster%2C%0A%20%20%20%20Ridge%2C%0A%20%20%20%20horizon%2C%0A%20%20%20%20plot_forecast%2C%0A%20%20%20%20y_test%2C%0A%20%20%20%20y_train%2C%0A)%3A%0A%20%20%20%20fc_three%20%3D%20DecompositionPipeline(%0A%20%20%20%20%20%20%20%20forecasters%3D%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20(%22trend%22%2C%20PolynomialTrendForecaster(degree%3D1))%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20(%22season%22%2C%20PatternSeasonalityForecaster(seasonality%3D12))%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22residual%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20PointReductionForecaster(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20estimator%3DRidge(alpha%3D1.0)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20feature_transformer%3DLagTransformer(lag%3D%5B1%2C%2012%5D)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20%20%20%20%20%5D%2C%0A%20%20%20%20)%0A%20%20%20%20fc_three.fit(y_train%2C%20forecasting_horizon%3Dhorizon)%0A%20%20%20%20_y_pred_three%20%3D%20fc_three.predict(forecasting_horizon%3Dhorizon)%0A%0A%20%20%20%20plot_forecast(%0A%20%20%20%20%20%20%20%20y_test%2C%0A%20%20%20%20%20%20%20%20_y_pred_three%2C%0A%20%20%20%20%20%20%20%20y_train%3Dy_train%2C%0A%20%20%20%20%20%20%20%20groups%3D%5B%22T3%22%2C%20%22T4%22%2C%20%22T5%22%5D%2C%0A%20%20%20%20%20%20%20%20title%3D%22Trend%20%2B%20Seasonality%20%2B%20Residual%20(Panel)%22%2C%0A%20%20%20%20)%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%205.%20Compare%20Approaches%0A%0A%20%20%20%20Score%20each%20approach%20per%20group%20to%20see%20decomposition%20benefits.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(%0A%20%20%20%20MeanAbsoluteError%2C%0A%20%20%20%20SeasonalNaive%2C%0A%20%20%20%20horizon%2C%0A%20%20%20%20plot_score_time_series%2C%0A%20%20%20%20y_pred_decomp%2C%0A%20%20%20%20y_test%2C%0A%20%20%20%20y_train%2C%0A)%3A%0A%20%20%20%20_naive%20%3D%20SeasonalNaive(seasonality%3D4)%0A%20%20%20%20_naive.fit(y_train%2C%20forecasting_horizon%3Dhorizon)%0A%20%20%20%20_y_pred_naive%20%3D%20_naive.predict(forecasting_horizon%3Dhorizon)%0A%0A%20%20%20%20_scorer%20%3D%20MeanAbsoluteError()%0A%0A%20%20%20%20plot_score_time_series(%0A%20%20%20%20%20%20%20%20_scorer%2C%0A%20%20%20%20%20%20%20%20y_test%2C%0A%20%20%20%20%20%20%20%20%7B%22SeasonalNaive%22%3A%20_y_pred_naive%2C%20%22Decomposition%22%3A%20y_pred_decomp%7D%2C%0A%20%20%20%20%20%20%20%20groups%3D%5B%22T3%22%2C%20%22T4%22%2C%20%22T5%22%5D%2C%0A%20%20%20%20%20%20%20%20title%3D%22MAE%20over%20Time%20per%20Panel%20Group%22%2C%0A%20%20%20%20)%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%20Multi-vintage%20Scoring%0A%0A%20%20%20%20The%20%60observe_predict%60%20method%20with%20%60stride%3D1%60%20produces%20one%20forecast%20per%0A%20%20%20%20observation%20point%2C%20creating%20multiple%20*vintages*.%20Each%20vintage%20represents%0A%20%20%20%20a%20different%20forecast%20origin%2C%20so%20you%20can%20analyse%20how%20accuracy%20evolves%20as%0A%20%20%20%20the%20model%20absorbs%20more%20data.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(SeasonalNaive%2C%20deepcopy%2C%20horizon%2C%20y_test%2C%20y_train)%3A%0A%20%20%20%20_naive_v%20%3D%20SeasonalNaive(seasonality%3D4)%0A%20%20%20%20_naive_v.fit(y_train%2C%20forecasting_horizon%3Dhorizon)%0A%20%20%20%20_vintage_model%20%3D%20deepcopy(_naive_v)%0A%20%20%20%20y_pred_vintages%20%3D%20_vintage_model.observe_predict(%0A%20%20%20%20%20%20%20%20y%3Dy_test%2C%0A%20%20%20%20%20%20%20%20stride%3D1%2C%0A%20%20%20%20%20%20%20%20forecasting_horizon%3Dhorizon%2C%0A%20%20%20%20)%0A%20%20%20%20print(f%22Vintages%3A%20%7By_pred_vintages%5B'vintage_time'%5D.n_unique()%7D%22)%0A%20%20%20%20y_pred_vintages.head(10)%0A%20%20%20%20return%20(y_pred_vintages%2C)%0A%0A%0A%40app.cell%0Adef%20_(MeanAbsoluteError%2C%20y_train)%3A%0A%20%20%20%20vintage_scorer%20%3D%20MeanAbsoluteError()%0A%20%20%20%20vintage_scorer.fit(y_train)%0A%20%20%20%20return%20(vintage_scorer%2C)%0A%0A%0A%40app.cell%0Adef%20_(plot_score_per_vintage%2C%20vintage_scorer%2C%20y_pred_vintages%2C%20y_test)%3A%0A%20%20%20%20plot_score_per_vintage(%0A%20%20%20%20%20%20%20%20vintage_scorer%2C%0A%20%20%20%20%20%20%20%20y_test%2C%0A%20%20%20%20%20%20%20%20y_pred_vintages%2C%0A%20%20%20%20%20%20%20%20title%3D%22MAE%20per%20Forecast%20Vintage%22%2C%0A%20%20%20%20%20%20%20%20y_label%3D%22MAE%22%2C%0A%20%20%20%20%20%20%20%20height%3D380%2C%0A%20%20%20%20)%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**Panel%20pipelines**%3A%20See%20%5B%60examples%2Fcompose%2Fpanel_pipelines.py%60%5D(%2Fexamples%2Fpanel-data%2Fpanel_pipelines%2F)%0A%20%20%20%20-%20**Stationarity%20transforms**%3A%20See%20%5B%60examples%2Fstationarity%2Fstationarity_transforms.py%60%5D(%2Fexamples%2Fdata-features%2Fstationarity_transforms%2F)%0A%20%20%20%20-%20**Decomposition%20details**%3A%20See%20%5B%60examples%2Fstationarity%2Fdecomposition.py%60%5D(%2Fexamples%2Fgetting-started%2Fdecomposition%2F)%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
622f90d4376cbcec9bac60660cbcd4ff