%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%20Panel%20Reduction%20Forecasting%0A%0A%20%20%20%20Panel%20data%20contains%20multiple%20related%20time%20series%20identified%20by%20a%20common%0A%20%20%20%20naming%20convention.%20%20Yohou%20uses%20a%20%60__%60%20(double%20underscore)%20separator%20to%0A%20%20%20%20distinguish%20panel%20groups%20from%20series%20names.%0A%0A%20%20%20%20This%20notebook%20demonstrates%20how%20%5B%60PointReductionForecaster%60%5D(%2Fpages%2Fapi%2Fgenerated%2Fyohou.point.reduction.PointReductionForecaster%2F)%20handles%20panel%0A%20%20%20%20data%20through%20different%20%60panel_strategy%60%20options%2C%20plus%20the%20fully%0A%20%20%20%20independent%20%5B%60LocalPanelForecaster%60%5D(%2Fpages%2Fapi%2Fgenerated%2Fyohou.compose.local_panel_forecaster.LocalPanelForecaster%2F)%20approach.%0A%0A%20%20%20%20%23%23%201.%20Panel%20Data%20Conventions%0A%0A%20%20%20%20Panel%20columns%20follow%20the%20%60%3Cgroup%3E__%3Cseries%3E%60%20naming%20pattern.%0A%20%20%20%20%5B%60inspect_panel%60%5D(%2Fpages%2Fapi%2Fgenerated%2Fyohou.utils.panel.inspect_panel%2F)%20discovers%20groups%20automatically.%20We%20load%20the%20KDD%20Cup%0A%20%20%20%202018%20air%20quality%20dataset%2C%20a%20multivariate%20panel%20with%203%20Beijing%0A%20%20%20%20stations%2C%20each%20monitoring%206%20pollutants.%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%20import%20polars%20as%20pl%0A%20%20%20%20from%20sklearn.linear_model%20import%20Ridge%0A%0A%20%20%20%20from%20yohou.compose%20import%20LocalPanelForecaster%0A%20%20%20%20from%20yohou.datasets%20import%20fetch_kdd_cup%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_heatmap%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%0A%20%20%20%20from%20yohou.preprocessing%20import%20LagTransformer%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%20LagTransformer%2C%0A%20%20%20%20%20%20%20%20LocalPanelForecaster%2C%0A%20%20%20%20%20%20%20%20MeanAbsoluteError%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%20deepcopy%2C%0A%20%20%20%20%20%20%20%20fetch_kdd_cup%2C%0A%20%20%20%20%20%20%20%20inspect_panel%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%20plot_score_heatmap%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_kdd_cup%2C%20inspect_panel%2C%20mo)%3A%0A%20%20%20%20_bunch%20%3D%20fetch_kdd_cup(n_groups%3D3)%0A%20%20%20%20store%20%3D%20_bunch.frame.drop_nulls().tail(300)%0A%0A%20%20%20%20global_cols%2C%20panel_groups%20%3D%20inspect_panel(store)%0A%0A%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20f%22**Dataset%20shape**%3A%20%7Bstore.shape%7D%5Cn%5Cn%22%0A%20%20%20%20%20%20%20%20f%22**Global%20columns**%20(shared)%3A%20%60%7Bglobal_cols%7D%60%5Cn%5Cn%22%0A%20%20%20%20%20%20%20%20f%22**Panel%20groups**%20(%7Blen(panel_groups)%7D)%3A%5Cn%5Cn%22%0A%20%20%20%20%20%20%20%20%2B%20%22%5Cn%22.join(f%22-%20%60%7Bname%7D%60%3A%20%7Bcols%7D%22%20for%20name%2C%20cols%20in%20sorted(panel_groups.items()))%0A%20%20%20%20)%0A%20%20%20%20return%20panel_groups%2C%20store%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%5B%60plot_time_series%60%5D(%2Fpages%2Fapi%2Fgenerated%2Fyohou.plotting.exploration.plot_time_series%2F)%20renders%20all%20panel%20columns%20in%20one%20figure.%20Each%20%60__%60-prefixed%0A%20%20%20%20series%20gets%20its%20own%20trace%2C%20giving%20an%20overview%20of%20pollutant%20levels%20across%20stations.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(plot_time_series%2C%20store)%3A%0A%20%20%20%20plot_time_series(%0A%20%20%20%20%20%20%20%20store%2C%0A%20%20%20%20%20%20%20%20title%3D%22KDD%20Cup%202018%3A%20Air%20Quality%20Panel%22%2C%0A%20%20%20%20)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(mo%2C%20store%2C%20train_test_split)%3A%0A%20%20%20%20_target_cols%20%3D%20%5Bc%20for%20c%20in%20store.columns%20if%20c%20!%3D%20%22time%22%5D%0A%20%20%20%20y%20%3D%20store.select(%22time%22%2C%20*_target_cols)%0A%20%20%20%20y_train%2C%20y_test%20%3D%20train_test_split(y%2C%20test_size%3D0.15)%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**Targets**%3A%20%7Blen(_target_cols)%7D%20pollutant%20columns%5Cn%5Cn%22%0A%20%20%20%20%20%20%20%20f%22**Train**%3A%20%7Blen(y_train)%7D%20rows%20%7C%20**Test**%3A%20%7Blen(y_test)%7D%20rows%20%7C%20%22%0A%20%20%20%20%20%20%20%20f%22**Horizon**%3A%20%7Bhorizon%7D%22%0A%20%20%20%20)%0A%20%20%20%20return%20horizon%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.%20Strategy%3A%20Global%20(Default)%0A%0A%20%20%20%20With%20%60panel_strategy%3D%22global%22%60%20(the%20default)%2C%20each%20panel%20group%20gets%0A%20%20%20%20**independent%20observation%20buffers%20and%20transformers**%20but%20shares%20the%0A%20%20%20%20**same%20model%20hyperparameters**.%20%20The%20forecaster%20detects%20groups%20via%20the%0A%20%20%20%20%60__%60%20separator%2C%20strips%20prefixes%2C%20fits%20per-group%20transformers%2C%20and%0A%20%20%20%20pools%20the%20transformed%20data%20into%20a%20single%20estimator.%0A%0A%20%20%20%20This%20is%20efficient%20when%20groups%20share%20similar%20dynamics%20and%20you%20want%0A%20%20%20%20one%20set%20of%20hyperparameters%20to%20govern%20all%20groups.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(LagTransformer%2C%20PointReductionForecaster%2C%20Ridge%2C%20horizon%2C%20y_train)%3A%0A%20%20%20%20fc_global%20%3D%20PointReductionForecaster(%0A%20%20%20%20%20%20%20%20estimator%3DRidge(alpha%3D1.0)%2C%0A%20%20%20%20%20%20%20%20feature_transformer%3DLagTransformer(lag%3D%5B1%2C%2024%5D)%2C%0A%20%20%20%20%20%20%20%20panel_strategy%3D%22global%22%2C%20%20%23%20default%2C%20shown%20explicitly%20for%20clarity%0A%20%20%20%20)%0A%20%20%20%20fc_global.fit(y_train%2C%20forecasting_horizon%3Dhorizon)%0A%20%20%20%20y_pred_global%20%3D%20fc_global.predict(forecasting_horizon%3Dhorizon)%0A%20%20%20%20return%20fc_global%2C%20y_pred_global%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%5B%60plot_forecast%60%5D(%2Fpages%2Fapi%2Fgenerated%2Fyohou.plotting.forecasting.plot_forecast%2F)%20with%20%60groups%60%20shows%20predictions%20for%20selected%0A%20%20%20%20groups%20in%20a%20faceted%20layout%2C%20with%20training%20history%20trimmed%20to%20the%20last%2048%20steps.%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_global%2C%20y_test%2C%20y_train)%3A%0A%20%20%20%20_groups%20%3D%20sorted(%7Bc.split(%22__%22)%5B0%5D%20for%20c%20in%20y_train.columns%20if%20%22__%22%20in%20c%7D)%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_global%2C%0A%20%20%20%20%20%20%20%20y_train%3Dy_train%2C%0A%20%20%20%20%20%20%20%20n_history%3D48%2C%0A%20%20%20%20%20%20%20%20groups%3D_groups%5B%3A2%5D%2C%0A%20%20%20%20%20%20%20%20title%3D%22Global%20Strategy%3A%20One%20Model%2C%20Per-Group%20State%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%203.%20Strategy%3A%20Multivariate%0A%0A%20%20%20%20With%20%60panel_strategy%3D%22multivariate%22%60%2C%20the%20forecaster%20**skips%20panel%0A%20%20%20%20detection%20entirely**.%20%20All%20%60__%60-prefixed%20columns%20are%20treated%20as%0A%20%20%20%20ordinary%20multivariate%20columns.%20%20A%20single%20transformer%20and%20model%20see%0A%20%20%20%20the%20full%20wide%20DataFrame%2C%20enabling%20**cross-group%20feature%20interactions**.%0A%0A%20%20%20%20This%20is%20useful%20when%20groups%20are%20correlated%20and%20you%20want%20the%20model%20to%0A%20%20%20%20learn%20from%20inter-group%20relationships%20(e.g.%20co-located%20pollution%20monitors%2C%0A%20%20%20%20spatially%20correlated%20air%20quality%20patterns).%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(LagTransformer%2C%20PointReductionForecaster%2C%20Ridge%2C%20horizon%2C%20y_train)%3A%0A%20%20%20%20fc_multivariate%20%3D%20PointReductionForecaster(%0A%20%20%20%20%20%20%20%20estimator%3DRidge(alpha%3D1.0)%2C%0A%20%20%20%20%20%20%20%20feature_transformer%3DLagTransformer(lag%3D%5B1%2C%2024%5D)%2C%0A%20%20%20%20%20%20%20%20panel_strategy%3D%22multivariate%22%2C%0A%20%20%20%20)%0A%20%20%20%20fc_multivariate.fit(y_train%2C%20forecasting_horizon%3Dhorizon)%0A%20%20%20%20y_pred_multivariate%20%3D%20fc_multivariate.predict(forecasting_horizon%3Dhorizon)%0A%20%20%20%20return%20(y_pred_multivariate%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%5B%60plot_forecast%60%5D(%2Fpages%2Fapi%2Fgenerated%2Fyohou.plotting.forecasting.plot_forecast%2F)%20for%20the%20multivariate%20strategy%20shows%20the%20same%20groups%2C%0A%20%20%20%20letting%20you%20compare%20predictions%20against%20the%20global%20strategy%20above.%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_multivariate%2C%20y_test%2C%20y_train)%3A%0A%20%20%20%20_groups%20%3D%20sorted(%7Bc.split(%22__%22)%5B0%5D%20for%20c%20in%20y_train.columns%20if%20%22__%22%20in%20c%7D)%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_multivariate%2C%0A%20%20%20%20%20%20%20%20y_train%3Dy_train%2C%0A%20%20%20%20%20%20%20%20n_history%3D48%2C%0A%20%20%20%20%20%20%20%20groups%3D_groups%5B%3A2%5D%2C%0A%20%20%20%20%20%20%20%20title%3D%22Multivariate%20Strategy%3A%20Cross-Group%20Features%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.%20Strategy%3A%20Local%20(Independent%20Clones)%0A%0A%20%20%20%20%5B%60LocalPanelForecaster%60%5D(%2Fpages%2Fapi%2Fgenerated%2Fyohou.compose.local_panel_forecaster.LocalPanelForecaster%2F)%20wraps%20any%20forecaster%20and%20fits%20**completely%0A%20%20%20%20independent%20clones**%20per%20panel%20group.%20%20Unlike%20%60panel_strategy%3D%22global%22%60%0A%20%20%20%20which%20shares%20hyperparameters%20across%20groups%2C%20each%20clone%20has%20its%20own%0A%20%20%20%20parameters.%20%20This%20is%20the%20right%20choice%20when%20groups%20are%20heterogeneous.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(%0A%20%20%20%20LagTransformer%2C%0A%20%20%20%20LocalPanelForecaster%2C%0A%20%20%20%20PointReductionForecaster%2C%0A%20%20%20%20Ridge%2C%0A%20%20%20%20horizon%2C%0A%20%20%20%20y_train%2C%0A)%3A%0A%20%20%20%20fc_local%20%3D%20LocalPanelForecaster(%0A%20%20%20%20%20%20%20%20forecaster%3DPointReductionForecaster(%0A%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%20feature_transformer%3DLagTransformer(lag%3D%5B1%2C%2024%5D)%2C%0A%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20)%0A%20%20%20%20fc_local.fit(y_train%2C%20forecasting_horizon%3Dhorizon)%0A%20%20%20%20y_pred_local%20%3D%20fc_local.predict(forecasting_horizon%3Dhorizon)%0A%20%20%20%20return%20(y_pred_local%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%5B%60plot_forecast%60%5D(%2Fpages%2Fapi%2Fgenerated%2Fyohou.plotting.forecasting.plot_forecast%2F)%20for%20the%20local%20strategy%20shows%20fully%20independent%20per-group%0A%20%20%20%20clones%2C%20which%20may%20capture%20group-specific%20dynamics%20better.%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_local%2C%20y_test%2C%20y_train)%3A%0A%20%20%20%20_groups%20%3D%20sorted(%7Bc.split(%22__%22)%5B0%5D%20for%20c%20in%20y_train.columns%20if%20%22__%22%20in%20c%7D)%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_local%2C%0A%20%20%20%20%20%20%20%20y_train%3Dy_train%2C%0A%20%20%20%20%20%20%20%20n_history%3D48%2C%0A%20%20%20%20%20%20%20%20groups%3D_groups%5B%3A2%5D%2C%0A%20%20%20%20%20%20%20%20title%3D%22Local%20Strategy%3A%20Independent%20Per-Group%20Clones%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.%20Strategy%20Comparison%0A%0A%20%20%20%20Compare%20all%20three%20strategies%20using%20per-group%20MAE%20(stepwise%2Bvintagewise%20aggregation%0A%20%20%20%20produces%20one%20score%20per%20group%2C%20averaged%20across%20timesteps).%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%20mo%2C%0A%20%20%20%20panel_groups%2C%0A%20%20%20%20pl%2C%0A%20%20%20%20y_pred_global%2C%0A%20%20%20%20y_pred_local%2C%0A%20%20%20%20y_pred_multivariate%2C%0A%20%20%20%20y_test%2C%0A%20%20%20%20y_train%2C%0A)%3A%0A%20%20%20%20_scorer_sv%20%3D%20MeanAbsoluteError(aggregation_method%3D%5B%22stepwise%22%2C%20%22vintagewise%22%5D)%0A%20%20%20%20_scorer_sv.fit(y_train)%0A%0A%20%20%20%20_scores_global%20%3D%20_scorer_sv.score(y_test%2C%20y_pred_global)%0A%20%20%20%20_scores_multi%20%3D%20_scorer_sv.score(y_test%2C%20y_pred_multivariate)%0A%20%20%20%20_scores_local%20%3D%20_scorer_sv.score(y_test%2C%20y_pred_local)%0A%0A%20%20%20%20_rows%20%3D%20%5B%5D%0A%20%20%20%20for%20_group%20in%20sorted(panel_groups.keys())%3A%0A%20%20%20%20%20%20%20%20_group_cols%20%3D%20%5Bc%20for%20c%20in%20_scores_global.columns%20if%20c.startswith(f%22%7B_group%7D__%22)%5D%0A%20%20%20%20%20%20%20%20if%20_group_cols%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20_rows.append(%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22Group%22%3A%20_group%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22Global%20MAE%22%3A%20round(sum(_scores_global%5Bc%5D.item()%20for%20c%20in%20_group_cols)%20%2F%20len(_group_cols)%2C%202)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22Multivariate%20MAE%22%3A%20round(sum(_scores_multi%5Bc%5D.item()%20for%20c%20in%20_group_cols)%20%2F%20len(_group_cols)%2C%202)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22Local%20MAE%22%3A%20round(sum(_scores_local%5Bc%5D.item()%20for%20c%20in%20_group_cols)%20%2F%20len(_group_cols)%2C%202)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D)%0A%0A%20%20%20%20comparison%20%3D%20pl.DataFrame(_rows)%0A%20%20%20%20mo.ui.table(comparison)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(%0A%20%20%20%20MeanAbsoluteError%2C%0A%20%20%20%20plot_score_time_series%2C%0A%20%20%20%20y_pred_global%2C%0A%20%20%20%20y_pred_local%2C%0A%20%20%20%20y_pred_multivariate%2C%0A%20%20%20%20y_test%2C%0A%20%20%20%20y_train%2C%0A)%3A%0A%20%20%20%20_scorer%20%3D%20MeanAbsoluteError()%0A%20%20%20%20_scorer.fit(y_train)%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%0A%20%20%20%20%20%20%20%20%20%20%20%20%22Global%22%3A%20y_pred_global%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22Multivariate%22%3A%20y_pred_multivariate%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22Local%22%3A%20y_pred_local%2C%0A%20%20%20%20%20%20%20%20%7D%2C%0A%20%20%20%20%20%20%20%20title%3D%22MAE%20Over%20Time%3A%20Strategy%20Comparison%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.5%20Reduction%20Strategies%20on%20Panel%20Data%0A%0A%20%20%20%20The%20%60reduction_strategy%60%20parameter%20works%20with%20all%20panel%20strategies.%0A%20%20%20%20Below%20we%20compare%20multi-output%20(default)%20vs%20direct%20on%20the%20global%0A%20%20%20%20panel%20strategy.%20Each%20strategy%20trains%20on%20the%20pooled%20panel%20data%0A%20%20%20%20but%20differs%20in%20**how**%20multi-step%20targets%20are%20handled.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(%0A%20%20%20%20LagTransformer%2C%0A%20%20%20%20MeanAbsoluteError%2C%0A%20%20%20%20PointReductionForecaster%2C%0A%20%20%20%20Ridge%2C%0A%20%20%20%20horizon%2C%0A%20%20%20%20mo%2C%0A%20%20%20%20y_test%2C%0A%20%20%20%20y_train%2C%0A)%3A%0A%20%20%20%20_reduction_scores%20%3D%20%7B%7D%0A%20%20%20%20for%20_strat%20in%20%5B%22multi-output%22%2C%20%22direct%22%5D%3A%0A%20%20%20%20%20%20%20%20_fc%20%3D%20PointReductionForecaster(%0A%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%20feature_transformer%3DLagTransformer(lag%3D%5B1%2C%2024%5D)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20panel_strategy%3D%22global%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20reduction_strategy%3D_strat%2C%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20_fc.fit(y_train%2C%20forecasting_horizon%3Dhorizon)%0A%20%20%20%20%20%20%20%20_pred%20%3D%20_fc.predict(forecasting_horizon%3Dhorizon)%0A%20%20%20%20%20%20%20%20_mae%20%3D%20MeanAbsoluteError()%0A%20%20%20%20%20%20%20%20_mae.fit(y_train)%0A%20%20%20%20%20%20%20%20_reduction_scores%5B_strat%5D%20%3D%20_mae.score(y_test.head(len(_pred))%2C%20_pred)%0A%0A%20%20%20%20mo.ui.table(%0A%20%20%20%20%20%20%20%20%5B%7B%22reduction_strategy%22%3A%20k%2C%20%22MAE%22%3A%20f%22%7Bv%3A.2f%7D%22%7D%20for%20k%2C%20v%20in%20_reduction_scores.items()%5D%2C%0A%20%20%20%20%20%20%20%20label%3D%22MAE%20by%20reduction_strategy%20(global%20panel)%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%20When%20to%20Use%20Each%20Strategy%0A%0A%20%20%20%20%7C%20Strategy%20%7C%20Parameter%20%7C%20Best%20For%20%7C%0A%20%20%20%20%7C---%7C---%7C---%7C%0A%20%20%20%20%7C%20**Global**%20%7C%20%60panel_strategy%3D%22global%22%60%20(default)%20%7C%20Homogeneous%20groups%20sharing%20similar%20dynamics%20%7C%0A%20%20%20%20%7C%20**Multivariate**%20%7C%20%60panel_strategy%3D%22multivariate%22%60%20%7C%20Correlated%20groups%20with%20cross-group%20interactions%20%7C%0A%20%20%20%20%7C%20**Local**%20%7C%20%60LocalPanelForecaster(forecaster%3D...)%60%20%7C%20Heterogeneous%20groups%20needing%20fully%20independent%20models%20%7C%0A%0A%20%20%20%20**Rules%20of%20thumb%3A**%0A%0A%20%20%20%20-%20Start%20with%20%60%22global%22%60%20(simplest%2C%20good%20default)%0A%20%20%20%20-%20Try%20%60%22multivariate%22%60%20if%20groups%20are%20strongly%20correlated%20(e.g.%20nearby%20monitoring%20stations)%0A%20%20%20%20-%20Use%20%5B%60LocalPanelForecaster%60%5D(%2Fpages%2Fapi%2Fgenerated%2Fyohou.compose.local_panel_forecaster.LocalPanelForecaster%2F)%20when%20groups%20differ%20significantly%20(e.g.%20different%20cities%2C%20sensor%20types)%0A%20%20%20%20-%20Combine%20with%20%5B%60ColumnForecaster%60%5D(%2Fpages%2Fapi%2Fgenerated%2Fyohou.compose.column_forecaster.ColumnForecaster%2F)%20to%20assign%20different%20model%20families%20per%20group%0A%0A%20%20%20%20%23%23%20Next%20Steps%0A%0A%20%20%20%20-%20**Reduction%20strategies**%3A%20See%20%5B%60reduction_strategies.py%60%5D(%2Fexamples%2Fgetting-started%2Freduction_strategies%2F)%20for%20multi-output%20vs%20direct%20vs%20dir-rec%20comparison%0A%20%20%20%20-%20**LocalPanelForecaster%20deep%20dive**%3A%20See%20%5B%60examples%2Fcompose%2Flocal_panel_forecaster.py%60%5D(%2Fexamples%2Fpanel-data%2Flocal_panel_forecaster%2F)%0A%20%20%20%20-%20**Per-group%20specialisation**%3A%20See%20%5B%60examples%2Fpoint%2Fpanel_forecasting.py%60%5D(%2Fexamples%2Fpanel-data%2Fpanel_forecasting%2F)%0A%20%20%20%20-%20**Composition%20patterns**%3A%20See%20%5B%60examples%2Fcompose%2Fpanel_pipelines.py%60%5D(%2Fexamples%2Fpanel-data%2Fpanel_pipelines%2F)%0A%20%20%20%20-%20**Panel%20intervals**%3A%20See%20%5B%60examples%2Finterval%2Fpanel_intervals.py%60%5D(%2Fexamples%2Fpanel-data%2Fpanel_intervals%2F)%0A%20%20%20%20-%20**Panel%20cross-validation**%3A%20See%20%5B%60examples%2Fmodel_selection%2Fpanel_cross_validation.py%60%5D(%2Fexamples%2Fpanel-data%2Fpanel_cross_validation%2F)%0A%20%20%20%20%22%22%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%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_(deepcopy%2C%20fc_global%2C%20horizon%2C%20y_test)%3A%0A%20%20%20%20_vintage_model%20%3D%20deepcopy(fc_global)%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%0Adef%20_(plot_score_heatmap%2C%20vintage_scorer%2C%20y_pred_vintages%2C%20y_test)%3A%0A%20%20%20%20plot_score_heatmap(%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%22Score%20Heatmap%20(Step%20x%20Vintage)%22%2C%0A%20%20%20%20)%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
665e4f2617102e7e85238a8864d8eeb4