%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%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%20Data%20Forecasting%0A%0A%20%20%20%20In%20this%20notebook%2C%20we%20will%20forecast%20multiple%20related%20time%20series%0A%20%20%20%20simultaneously%20using%20panel%20data.%20We%20will%20load%20a%20multi-series%20tourism%0A%20%20%20%20dataset%2C%20understand%20the%20%60__%60%20column%20naming%20convention%2C%20fit%20a%0A%20%20%20%20%5B%60LocalPanelForecaster%60%5D(%2Fpages%2Fapi%2Fgenerated%2Fyohou.compose.local_panel_forecaster.LocalPanelForecaster%2F)%20that%20clones%20a%20forecaster%20per%20group%2C%20and%20score%0A%20%20%20%20the%20results%20with%20both%20aggregate%20and%20per-group%20metrics.%0A%0A%20%20%20%20**Prerequisites%3A**%20Completed%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%0A%20%20%20%20from%20yohou.compose%20import%20LocalPanelForecaster%0A%20%20%20%20from%20yohou.datasets%20import%20fetch_tourism_monthly%0A%20%20%20%20from%20yohou.metrics%20import%20MeanAbsoluteError%0A%20%20%20%20from%20yohou.plotting%20import%20plot_forecast%0A%20%20%20%20from%20yohou.point%20import%20SeasonalNaive%0A%20%20%20%20from%20yohou.utils.panel%20import%20get_group_df%2C%20inspect_panel%0A%0A%20%20%20%20return%20(%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%20SeasonalNaive%2C%0A%20%20%20%20%20%20%20%20fetch_tourism_monthly%2C%0A%20%20%20%20%20%20%20%20get_group_df%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)%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%20a%20Panel%20Dataset%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(fetch_tourism_monthly)%3A%0A%20%20%20%20bunch%20%3D%20fetch_tourism_monthly()%0A%20%20%20%20y%20%3D%20bunch.frame.select(%0A%20%20%20%20%20%20%20%20%5B%22time%22%2C%20%22T187__tourists%22%2C%20%22T188__tourists%22%2C%20%22T189__tourists%22%5D%0A%20%20%20%20).drop_nulls()%0A%20%20%20%20y.head()%0A%20%20%20%20return%20(y%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%20Notice%20the%20column%20names%20use%20the%20%60%7Bgroup%7D__%7Bvariable%7D%60%20convention.%0A%20%20%20%20The%20text%20before%20%60__%60%20identifies%20the%20panel%20group%20(a%20region)%3B%20the%20text%0A%20%20%20%20after%20%60__%60%20is%20the%20variable%20name.%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%202.%20Inspect%20Panel%20Structure%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(inspect_panel%2C%20y)%3A%0A%20%20%20%20global_names%2C%20panel_groups%20%3D%20inspect_panel(y)%0A%20%20%20%20panel_groups%0A%20%20%20%20return%20(panel_groups%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.%20Train%2FTest%20Split%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(y)%3A%0A%20%20%20%20from%20yohou.model_selection%20import%20train_test_split%0A%0A%20%20%20%20forecasting_horizon%20%3D%2012%0A%20%20%20%20y_train%2C%20y_test%20%3D%20train_test_split(y%2C%20test_size%3Dforecasting_horizon)%0A%20%20%20%20y_train.shape%2C%20y_test.shape%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%204.%20Fit%20a%20LocalPanelForecaster%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(LocalPanelForecaster%2C%20SeasonalNaive%2C%20forecasting_horizon%2C%20y_train)%3A%0A%20%20%20%20forecaster%20%3D%20LocalPanelForecaster(%0A%20%20%20%20%20%20%20%20forecaster%3DSeasonalNaive(seasonality%3D12)%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%205.%20Predict%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%20%3D%20forecaster.predict(forecasting_horizon%3Dforecasting_horizon)%0A%20%20%20%20y_pred.head()%0A%20%20%20%20return%20(y_pred%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.%20Score%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(MeanAbsoluteError%2C%20y_pred%2C%20y_test%2C%20y_train)%3A%0A%20%20%20%20scorer%20%3D%20MeanAbsoluteError()%0A%20%20%20%20scorer.fit(y_train)%0A%20%20%20%20score%20%3D%20scorer.score(y_test%2C%20y_pred)%0A%20%20%20%20score%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(MeanAbsoluteError%2C%20y_pred%2C%20y_test%2C%20y_train)%3A%0A%20%20%20%20groupwise_scorer%20%3D%20MeanAbsoluteError(aggregation_method%3D%22groupwise%22)%0A%20%20%20%20groupwise_scorer.fit(y_train)%0A%20%20%20%20per_group%20%3D%20groupwise_scorer.score(y_test%2C%20y_pred)%0A%20%20%20%20per_group%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%207.%20Plot%20a%20Single%20Group%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(get_group_df%2C%20panel_groups%2C%20pl%2C%20plot_forecast%2C%20y_pred%2C%20y_test%2C%20y_train)%3A%0A%20%20%20%20first_group%20%3D%20list(panel_groups.keys())%5B0%5D%0A%20%20%20%20schema%20%3D%20%7Bcol.split(%22__%22)%5B1%5D%3A%20pl.Float64%20for%20col%20in%20panel_groups%5Bfirst_group%5D%7D%0A%0A%20%20%20%20fig%20%3D%20plot_forecast(%0A%20%20%20%20%20%20%20%20y_train%3Dget_group_df(y_train%2C%20first_group%2C%20schema)%2C%0A%20%20%20%20%20%20%20%20y_test%3Dget_group_df(y_test%2C%20first_group%2C%20schema)%2C%0A%20%20%20%20%20%20%20%20y_pred%3Dget_group_df(y_pred%2C%20first_group%2C%20schema)%2C%0A%20%20%20%20)%0A%20%20%20%20fig%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%20What%20We%20Built%0A%0A%20%20%20%20We%20loaded%20a%20multi-series%20tourism%20dataset%2C%20inspected%20its%20panel%20structure%0A%20%20%20%20using%20the%20%60__%60%20naming%20convention%2C%20fit%20a%20%5B%60LocalPanelForecaster%60%5D(%2Fpages%2Fapi%2Fgenerated%2Fyohou.compose.local_panel_forecaster.LocalPanelForecaster%2F)%20that%20clones%0A%20%20%20%20a%20seasonal%20baseline%20per%20region%2C%20scored%20the%20results%20with%20both%20aggregate%20and%0A%20%20%20%20per-group%20metrics%2C%20and%20plotted%20forecasts%20for%20individual%20groups.%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%20Next%20Steps%0A%0A%20%20%20%20-%20%5BPanel%20Data%20Forecasting%5D(%2Fpages%2Ftutorials%2Fpanel-data%2F)%20for%20the%20full%20guide%0A%20%20%20%20-%20%5BPanel%20Data%20How-To%5D(%2Fpages%2Fhow-to%2Fpanel-data%2F)%20for%20related%20techniques%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
0b91cef34cd130e7b46f4e6dd10d7a12