%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%20Preprocessing%0A%0A%20%20%20%20Yohou%20transformers%20detect%20panel%20columns%20(containing%20%60__%60)%20and%20apply%0A%20%20%20%20operations%20per-group%20automatically.%20This%20notebook%20shows%20the%20automatic%0A%20%20%20%20path%2C%20then%20the%20manual%20per-group%20workflow%20using%20%5B%60get_group_df%60%5D(%2Fpages%2Fapi%2Fgenerated%2Fyohou.utils.panel.get_group_df%2F)%20and%0A%20%20%20%20%5B%60dict_to_panel%60%5D(%2Fpages%2Fapi%2Fgenerated%2Fyohou.utils.panel.dict_to_panel%2F).%0A%0A%20%20%20%20This%20notebook%20shows%20how%20to%20perform%20automatic%20panel-aware%20transformation%20(StandardScaler%2C%20rolling%20stats%2C%20imputation)%20plus%20manual%20per-group%20workflows%20with%20get_group_df%20and%20dict_to_panel.%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.datasets%20import%20fetch_pedestrian_counts%2C%20fetch_tourism_quarterly%0A%20%20%20%20from%20yohou.plotting%20import%20plot_time_series%0A%20%20%20%20from%20yohou.preprocessing%20import%20(%0A%20%20%20%20%20%20%20%20LagTransformer%2C%0A%20%20%20%20%20%20%20%20RollingStatisticsTransformer%2C%0A%20%20%20%20%20%20%20%20SimpleTimeImputer%2C%0A%20%20%20%20%20%20%20%20StandardScaler%2C%0A%20%20%20%20)%0A%20%20%20%20from%20yohou.utils.panel%20import%20dict_to_panel%2C%20get_group_df%2C%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%20RollingStatisticsTransformer%2C%0A%20%20%20%20%20%20%20%20SimpleTimeImputer%2C%0A%20%20%20%20%20%20%20%20StandardScaler%2C%0A%20%20%20%20%20%20%20%20dict_to_panel%2C%0A%20%20%20%20%20%20%20%20fetch_pedestrian_counts%2C%0A%20%20%20%20%20%20%20%20fetch_tourism_quarterly%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_time_series%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.%20Inspect%20Panel%20Structure%0A%0A%20%20%20%20%5B%60inspect_panel%60%5D(%2Fpages%2Fapi%2Fgenerated%2Fyohou.utils.panel.inspect_panel%2F)%20splits%20the%20columns%20of%20a%20DataFrame%20into%20global%20columns%0A%20%20%20%20(no%20%60__%60%20separator)%20and%20panel%20groups%20(columns%20prefixed%20with%0A%20%20%20%20%60%3Cgroup%3E__%3Cmember%3E%60).%20This%20is%20the%20starting%20point%20for%20any%20panel-aware%0A%20%20%20%20workflow.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(fetch_tourism_quarterly%2C%20inspect_panel%2C%20mo)%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%20panel_groups%20%3D%20inspect_panel(tourism)%0A%0A%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20f%22**Shape**%3A%20%7Btourism.shape%7D%5Cn%5Cn%22%0A%20%20%20%20%20%20%20%20f%22**Global%20columns**%3A%20%7B_globals%7D%5Cn%5Cn%22%0A%20%20%20%20%20%20%20%20f%22**Panel%20groups**%3A%20%7Blist(panel_groups.keys())%7D%5Cn%5Cn%22%0A%20%20%20%20%20%20%20%20f%22**Columns%20per%20group**%3A%20%7B%5Blen(v)%20for%20v%20in%20panel_groups.values()%5D%7D%22%0A%20%20%20%20)%0A%20%20%20%20return%20(tourism%2C)%0A%0A%0A%40app.cell%0Adef%20_(plot_time_series%2C%20tourism)%3A%0A%20%20%20%20plot_time_series(tourism%2C%20title%3D%22Panel%20Tourism%20Data%20(8%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%20The%20same%20structure%20applies%20to%20any%20panel%20dataset.%20Below%20we%20show%0A%20%20%20%20%5B%60fetch_pedestrian_counts%60%5D(%2Fpages%2Fapi%2Fgenerated%2Fyohou.datasets._fetchers.fetch_pedestrian_counts%2F)%0A%20%20%20%20(a%20high-frequency%20sensor%20panel%20with%20hourly%20Melbourne%20pedestrian%20counts%0A%20%20%20%20across%20multiple%20sensor%20locations).%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(fetch_pedestrian_counts%2C%20inspect_panel%2C%20mo)%3A%0A%20%20%20%20_bunch%20%3D%20fetch_pedestrian_counts()%0A%20%20%20%20_selected_sensors%20%3D%20%5B%22Bourke_Street_Mall__pedestrians%22%2C%20%22Southern_Cross_Station__pedestrians%22%2C%20%22Flagstaff_Station__pedestrians%22%5D%0A%20%20%20%20pedestrian%20%3D%20_bunch.frame.select(%22time%22%2C%20*%5Bc%20for%20c%20in%20_bunch.frame.columns%20if%20any(s%20in%20c%20for%20s%20in%20%5B%22Bourke%22%2C%20%22Southern_Cross%22%2C%20%22Flagstaff%22%5D)%5D).drop_nulls().head(500)%0A%20%20%20%20_globals_p%2C%20panel_groups_p%20%3D%20inspect_panel(pedestrian)%0A%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20f%22**Shape**%3A%20%7Bpedestrian.shape%7D%5Cn%5Cn%22%0A%20%20%20%20%20%20%20%20f%22**Panel%20groups**%3A%20%7Blist(panel_groups_p.keys())%7D%5Cn%5Cn%22%0A%20%20%20%20%20%20%20%20f%22Hourly%20sensor%20data%20follows%20the%20same%20%60GROUP__MEMBER%60%20convention%20%E2%80%94%20%22%0A%20%20%20%20%20%20%20%20f%22the%20same%20transforms%20from%20section%202%20onwards%20apply%20unchanged.%22%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%202.%20Automatic%20Panel-Aware%20Transformers%0A%0A%20%20%20%20When%20a%20transformer%20receives%20panel%20data%2C%20it%20detects%20the%20%60__%60%20separator%0A%20%20%20%20and%20processes%20each%20group%20independently%20while%20preserving%20the%20naming%0A%20%20%20%20convention.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(StandardScaler%2C%20plot_time_series%2C%20tourism)%3A%0A%20%20%20%20scaler%20%3D%20StandardScaler()%0A%20%20%20%20scaler.fit(tourism)%0A%20%20%20%20tourism_scaled%20%3D%20scaler.transform(tourism)%0A%20%20%20%20plot_time_series(tourism_scaled%2C%20title%3D%22StandardScaler%3A%20Panel%20Data%20(z-scored%20per%20group)%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(RollingStatisticsTransformer%2C%20plot_time_series%2C%20tourism)%3A%0A%20%20%20%20rolling%20%3D%20RollingStatisticsTransformer(window_size%3D4%2C%20statistics%3D%5B%22mean%22%2C%20%22std%22%5D)%0A%20%20%20%20rolling.fit(tourism)%0A%20%20%20%20tourism_rolling%20%3D%20rolling.transform(tourism)%0A%20%20%20%20plot_time_series(tourism_rolling%2C%20title%3D%22Rolling%20Mean%20%26%20Std%20(window%3D4%2C%20per%20group)%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.%20Lag%20Transformer%20on%20Panel%20Data%0A%0A%20%20%20%20%5B%60LagTransformer%60%5D(%2Fpages%2Fapi%2Fgenerated%2Fyohou.preprocessing.window.LagTransformer%2F)%20creates%20lagged%20copies%20of%20every%20panel%20column%0A%20%20%20%20independently.%20The%20output%20columns%20follow%20the%20pattern%0A%20%20%20%20%60%3Cgroup%3E__%3Cmember%3E_lag_%3Ck%3E%60%2C%20preserving%20the%20panel%20naming%20convention%20so%0A%20%20%20%20downstream%20forecasters%20can%20still%20identify%20groups.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(LagTransformer%2C%20plot_time_series%2C%20tourism)%3A%0A%20%20%20%20lag_tf%20%3D%20LagTransformer(lag%3D%5B1%2C%204%5D)%0A%20%20%20%20lag_tf.fit(tourism)%0A%20%20%20%20tourism_lagged%20%3D%20lag_tf.transform(tourism)%0A%20%20%20%20plot_time_series(tourism_lagged%2C%20title%3D%22Lag%20Transformer%20(lag%3D%5B1%2C%204%5D%2C%20per%20group)%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%204.%20Manual%20Per-Group%20Extraction%0A%0A%20%20%20%20Use%20%5B%60get_group_df%60%5D(%2Fpages%2Fapi%2Fgenerated%2Fyohou.utils.panel.get_group_df%2F)%20to%20extract%20a%20single%20group%20as%20a%20standard%20DataFrame%0A%20%20%20%20with%20unprefixed%20column%20names.%20Useful%20for%20custom%20per-group%20analysis.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(get_group_df%2C%20mo%2C%20pl%2C%20tourism)%3A%0A%20%20%20%20_schema%20%3D%20%7B%22tourists%22%3A%20pl.Float64%7D%0A%20%20%20%20t3_df%20%3D%20get_group_df(tourism%2C%20%22T3%22%2C%20_schema)%0A%0A%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20f%22**T3%20group%20shape**%3A%20%7Bt3_df.shape%7D%5Cn%5Cn%22%0A%20%20%20%20%20%20%20%20f%22**Columns**%3A%20%7Bt3_df.columns%7D%5Cn%5Cn%22%0A%20%20%20%20%20%20%20%20f%22Note%3A%20column%20is%20now%20%60tourists%60%20(unprefixed)%2C%20not%20%60T3__tourists%60.%22%0A%20%20%20%20)%0A%20%20%20%20return%20(t3_df%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_time_series%60%5D(%2Fpages%2Fapi%2Fgenerated%2Fyohou.plotting.exploration.plot_time_series%2F)%20renders%20the%20extracted%20group%20as%20a%20standard%20(non-panel)%0A%20%20%20%20time%20series%2C%20since%20the%20column%20names%20no%20longer%20contain%20a%20%60__%60%20prefix.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(plot_time_series%2C%20t3_df)%3A%0A%20%20%20%20plot_time_series(t3_df%2C%20title%3D%22T3%3A%20Tourists%20(Extracted%20Group)%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%205.%20dict_to_panel%3A%20Reassemble%20Panel%20Data%0A%0A%20%20%20%20After%20per-group%20processing%2C%20recombine%20results%20into%20a%20single%20panel%0A%20%20%20%20DataFrame%20using%20%5B%60dict_to_panel%60%5D(%2Fpages%2Fapi%2Fgenerated%2Fyohou.utils.panel.dict_to_panel%2F).%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(%0A%20%20%20%20StandardScaler%2C%0A%20%20%20%20dict_to_panel%2C%0A%20%20%20%20get_group_df%2C%0A%20%20%20%20pl%2C%0A%20%20%20%20plot_time_series%2C%0A%20%20%20%20tourism%2C%0A)%3A%0A%20%20%20%20_schema%20%3D%20%7B%22tourists%22%3A%20pl.Float64%7D%0A%20%20%20%20_groups%20%3D%20%7B%7D%0A%20%20%20%20for%20_group%20in%20%5B%22T3%22%2C%20%22T4%22%2C%20%22T5%22%5D%3A%0A%20%20%20%20%20%20%20%20_df%20%3D%20get_group_df(tourism%2C%20_group%2C%20_schema)%0A%20%20%20%20%20%20%20%20_sc%20%3D%20StandardScaler()%0A%20%20%20%20%20%20%20%20_sc.fit(_df)%0A%20%20%20%20%20%20%20%20_groups%5B_group%5D%20%3D%20_sc.transform(_df)%0A%0A%20%20%20%20panel_again%20%3D%20dict_to_panel(_groups)%0A%20%20%20%20plot_time_series(panel_again%2C%20title%3D%22Reassembled%20Panel%20(T3%2C%20T4%2C%20T5%20z-scored)%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%206.%20Handling%20Missing%20Values%20in%20Panel%20Data%0A%0A%20%20%20%20%5B%60SimpleTimeImputer%60%5D(%2Fpages%2Fapi%2Fgenerated%2Fyohou.preprocessing.imputation.SimpleTimeImputer%2F)%20fills%20missing%20values%20per-group%20when%20working%0A%20%20%20%20with%20panel%20data.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(SimpleTimeImputer%2C%20pl%2C%20plot_time_series%2C%20tourism)%3A%0A%20%20%20%20%23%20Inject%20some%20nulls%20for%20demonstration%0A%20%20%20%20_mask%20%3D%20pl.Series(%5Bi%20%25%207%20%3D%3D%200%20for%20i%20in%20range(len(tourism))%5D)%0A%20%20%20%20_tourism_missing%20%3D%20tourism.with_columns(%0A%20%20%20%20%20%20%20%20pl.when(_mask).then(None).otherwise(pl.col(%22T3__tourists%22)).alias(%22T3__tourists%22)%2C%0A%20%20%20%20%20%20%20%20pl.when(_mask).then(None).otherwise(pl.col(%22T4__tourists%22)).alias(%22T4__tourists%22)%2C%0A%20%20%20%20)%0A%0A%20%20%20%20imputer%20%3D%20SimpleTimeImputer(method%3D%22linear%22)%0A%20%20%20%20imputer.fit(_tourism_missing)%0A%20%20%20%20_tourism_filled%20%3D%20imputer.transform(_tourism_missing)%0A%0A%20%20%20%20%23%20Show%20T3%20before%20and%20after%20imputation%0A%20%20%20%20_combined%20%3D%20(%0A%20%20%20%20%20%20%20%20_tourism_filled%0A%20%20%20%20%20%20%20%20.select(%22time%22%2C%20%22T3__tourists%22)%0A%20%20%20%20%20%20%20%20.rename(%7B%22T3__tourists%22%3A%20%22imputed%22%7D)%0A%20%20%20%20%20%20%20%20.join(%0A%20%20%20%20%20%20%20%20%20%20%20%20_tourism_missing.select(%22time%22%2C%20%22T3__tourists%22).rename(%7B%22T3__tourists%22%3A%20%22with%20gaps%22%7D)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20on%3D%22time%22%2C%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20)%0A%20%20%20%20plot_time_series(_combined%2C%20title%3D%22T3%3A%20Panel%20Imputation%20(linear%2C%20per%20group)%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%20Preprocessing%5D(%2Fpages%2Fhow-to%2Fpanel-data%2F)%20for%20the%20full%20guide%0A%20%20%20%20-%20%5BPanel%20Data%20Forecasting%20Tutorial%5D(%2Fpages%2Ftutorials%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
a3a88a4f94bdb79373fadb617e51e73d