%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%20Resampling%20Time%20Series%0A%0A%20%20%20%20Time%20series%20data%20often%20needs%20frequency%20changes%2C%20downsampling%20(e.g.%2C%20hourly%20%E2%86%92%20daily)%0A%20%20%20%20or%20upsampling%20(e.g.%2C%20monthly%20%E2%86%92%20weekly).%20Yohou%20provides%20%5B%60Downsampler%60%5D(%2Fpages%2Fapi%2Fgenerated%2Fyohou.preprocessing.resampling.Downsampler%2F)%20and%20%5B%60Upsampler%60%5D(%2Fpages%2Fapi%2Fgenerated%2Fyohou.preprocessing.resampling.Upsampler%2F).%0A%0A%20%20%20%20This%20notebook%20shows%20how%20to%20demonstrate%20Downsampler%20and%20Upsampler%20for%20changing%20time%20series%20frequency%2C%20including%20multivariate%20support%2C%20boundary%20settings%2C%20and%20round-trip%20information%20loss.%0A%0A%20%20%20%20**Prerequisites%3A**%20None.%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_electricity_demand%0A%20%20%20%20from%20yohou.plotting%20import%20plot_time_series%0A%20%20%20%20from%20yohou.preprocessing%20import%20Downsampler%2C%20Upsampler%0A%0A%20%20%20%20return%20(%0A%20%20%20%20%20%20%20%20Downsampler%2C%0A%20%20%20%20%20%20%20%20Upsampler%2C%0A%20%20%20%20%20%20%20%20fetch_electricity_demand%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.%20Load%20High-Frequency%20Data%0A%0A%20%20%20%20The%20Electricity%20Demand%20dataset%20contains%2030-minute%20interval%20readings%20from%0A%20%20%20%20Australian%20states.%20We%20select%20the%20Victorian%20demand%20column%20and%20take%20a%0A%20%20%20%2030-day%20subset%20(about%201%20440%20observations)%20for%20a%20manageable%20demonstration.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(fetch_electricity_demand%2C%20pl%2C%20plot_time_series)%3A%0A%20%20%20%20raw%20%3D%20fetch_electricity_demand().frame.select(%22time%22%2C%20pl.col(%22vic__demand%22).alias(%22demand%22))%0A%0A%20%20%20%20%23%20Use%20a%20subset%20for%20speed%0A%20%20%20%20y_hf%20%3D%20raw.head(48%20*%2030)%20%20%23%20~30%20days%20of%2030-min%20data%0A%20%20%20%20plot_time_series(y_hf%2C%20title%3D%22Half-Hourly%20Electricity%20Demand%20(30%20days)%22)%0A%20%20%20%20return%20(y_hf%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%202.%20Downsampler%0A%0A%20%20%20%20%5B%60Downsampler%60%5D(%2Fpages%2Fapi%2Fgenerated%2Fyohou.preprocessing.resampling.Downsampler%2F)%20aggregates%20a%20time%20series%20to%20a%20coarser%20frequency.%20You%0A%20%20%20%20specify%20the%20target%20%60interval%60%20(e.g.%20%60%221d%22%60%20for%20daily)%20and%20the%0A%20%20%20%20%60aggregation%60%20method%3A%20%60%22mean%22%60%2C%20%60%22sum%22%60%2C%20%60%22min%22%60%2C%20%60%22max%22%60%2C%20%60%22first%22%60%2C%0A%20%20%20%20%60%22last%22%60%2C%20or%20%60%22median%22%60.%20Below%20we%20produce%20daily%20mean%20and%20daily%20sum%0A%20%20%20%20versions%20of%20the%20half-hourly%20demand%20data.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(Downsampler%2C%20plot_time_series%2C%20y_hf)%3A%0A%20%20%20%20daily_mean%20%3D%20Downsampler(interval%3D%221d%22%2C%20aggregation%3D%22mean%22)%0A%20%20%20%20daily_mean.fit(y_hf)%0A%20%20%20%20y_daily_mean%20%3D%20daily_mean.transform(y_hf)%0A%20%20%20%20plot_time_series(y_daily_mean%2C%20title%3D%22Hourly%20-%3E%20Daily%20(mean%20aggregation)%22)%0A%20%20%20%20return%20(y_daily_mean%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%23%20Comparing%20Aggregations%0A%0A%20%20%20%20Different%20aggregation%20strategies%20summarise%20each%20interval%20differently.%0A%20%20%20%20Joining%20the%20results%20into%20one%20DataFrame%20lets%20us%20overlay%20mean%2C%20min%2C%20max%2C%0A%20%20%20%20and%20median%20on%20a%20single%20%5B%60plot_time_series%60%5D(%2Fpages%2Fapi%2Fgenerated%2Fyohou.plotting.exploration.plot_time_series%2F)%20chart%20so%20the%20differences%20are%0A%20%20%20%20immediately%20visible.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(Downsampler%2C%20plot_time_series%2C%20y_hf)%3A%0A%20%20%20%20_agg_dfs%20%3D%20%7B%7D%0A%20%20%20%20for%20_agg%20in%20%5B%22mean%22%2C%20%22min%22%2C%20%22max%22%2C%20%22median%22%5D%3A%0A%20%20%20%20%20%20%20%20_ds%20%3D%20Downsampler(interval%3D%221d%22%2C%20aggregation%3D_agg)%0A%20%20%20%20%20%20%20%20_ds.fit(y_hf)%0A%20%20%20%20%20%20%20%20_agg_dfs%5B_agg%5D%20%3D%20_ds.transform(y_hf).rename(%7B%22demand%22%3A%20_agg%7D)%0A%0A%20%20%20%20_combined%20%3D%20_agg_dfs%5B%22mean%22%5D%0A%20%20%20%20for%20_agg%20in%20%5B%22min%22%2C%20%22max%22%2C%20%22median%22%5D%3A%0A%20%20%20%20%20%20%20%20_combined%20%3D%20_combined.join(_agg_dfs%5B_agg%5D%2C%20on%3D%22time%22)%0A%20%20%20%20plot_time_series(_combined%2C%20title%3D%22Daily%20Downsampling%3A%20Aggregation%20Comparison%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.%20Upsampler%0A%0A%20%20%20%20%5B%60Upsampler%60%5D(%2Fpages%2Fapi%2Fgenerated%2Fyohou.preprocessing.resampling.Upsampler%2F)%20increases%20the%20frequency%20by%20interpolating%20between%20existing%0A%20%20%20%20observations.%20The%20%60interpolation%60%20parameter%20controls%20the%20method%20used%20to%0A%20%20%20%20fill%20in%20the%20new%20time%20steps%3A%20%60%22linear%22%60%20draws%20straight%20lines%20between%0A%20%20%20%20neighbours%2C%20%60%22nearest%22%60%20copies%20the%20closest%20value%2C%20and%20%60%22forward%22%60%20%2F%0A%20%20%20%20%60%22backward%22%60%20carry%20the%20previous%20or%20next%20value.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(Upsampler%2C%20plot_time_series%2C%20y_daily_mean)%3A%0A%20%20%20%20hourly%20%3D%20Upsampler(interval%3D%221h%22%2C%20interpolation%3D%22linear%22)%0A%20%20%20%20hourly.fit(y_daily_mean)%0A%20%20%20%20y_hourly%20%3D%20hourly.transform(y_daily_mean)%0A%20%20%20%20plot_time_series(y_hourly%2C%20title%3D%22Daily%20%E2%86%92%20Hourly%20(linear%20interpolation)%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%23%20Comparing%20Interpolation%20Methods%0A%0A%20%20%20%20Each%20interpolation%20strategy%20fills%20in%20new%20time%20steps%20differently.%20We%0A%20%20%20%20overlay%20%60linear%60%2C%20%60nearest%60%2C%20%60forward%60%2C%20and%20%60backward%60%20on%20one%20chart%0A%20%20%20%20so%20you%20can%20see%20how%20the%20shape%20and%20step%20patterns%20compare.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(Upsampler%2C%20plot_time_series%2C%20y_daily_mean)%3A%0A%20%20%20%20_interp_dfs%20%3D%20%7B%7D%0A%20%20%20%20for%20_method%20in%20%5B%22linear%22%2C%20%22nearest%22%2C%20%22forward%22%2C%20%22backward%22%5D%3A%0A%20%20%20%20%20%20%20%20_up%20%3D%20Upsampler(interval%3D%221h%22%2C%20interpolation%3D_method)%0A%20%20%20%20%20%20%20%20_up.fit(y_daily_mean)%0A%20%20%20%20%20%20%20%20_interp_dfs%5B_method%5D%20%3D%20_up.transform(y_daily_mean).rename(%7B%22demand%22%3A%20_method%7D)%0A%0A%20%20%20%20_combined%20%3D%20_interp_dfs%5B%22linear%22%5D%0A%20%20%20%20for%20_method%20in%20%5B%22nearest%22%2C%20%22forward%22%2C%20%22backward%22%5D%3A%0A%20%20%20%20%20%20%20%20_combined%20%3D%20_combined.join(_interp_dfs%5B_method%5D%2C%20on%3D%22time%22)%0A%20%20%20%20plot_time_series(_combined%2C%20title%3D%22Hourly%20Upsampling%3A%20Interpolation%20Comparison%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.%20Downsampling%20to%20Weekly%0A%0A%20%20%20%20%5B%60Downsampler%60%5D(%2Fpages%2Fapi%2Fgenerated%2Fyohou.preprocessing.resampling.Downsampler%2F)%20accepts%20any%20polars%20duration%20string%20as%20the%20%60interval%60%0A%20%20%20%20parameter.%20Here%20we%20aggregate%20the%20half-hourly%20data%20to%20weekly%20mean%20values%0A%20%20%20%20and%20visualise%20the%20result%20with%20%5B%60plot_time_series%60%5D(%2Fpages%2Fapi%2Fgenerated%2Fyohou.plotting.exploration.plot_time_series%2F).%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(Downsampler%2C%20plot_time_series%2C%20y_hf)%3A%0A%20%20%20%20weekly%20%3D%20Downsampler(interval%3D%221w%22%2C%20aggregation%3D%22mean%22)%0A%20%20%20%20weekly.fit(y_hf)%0A%20%20%20%20y_weekly%20%3D%20weekly.transform(y_hf)%0A%20%20%20%20plot_time_series(y_weekly%2C%20title%3D%22Weekly%20Mean%20Electricity%20Demand%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.%20Multivariate%20Resampling%0A%0A%20%20%20%20%5B%60Downsampler%60%5D(%2Fpages%2Fapi%2Fgenerated%2Fyohou.preprocessing.resampling.Downsampler%2F)%20and%20%5B%60Upsampler%60%5D(%2Fpages%2Fapi%2Fgenerated%2Fyohou.preprocessing.resampling.Upsampler%2F)%20handle%20multivariate%20DataFrames%0A%20%20%20%20correctly%3A%20all%20non-time%20columns%20are%20processed%20with%20the%20same%20settings.%0A%20%20%20%20Here%20we%20load%20two%20demand%20columns%20(Victoria%20and%20New%20South%20Wales)%20and%0A%20%20%20%20downsample%20both%20to%20daily%20resolution%20in%20a%20single%20call.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(Downsampler%2C%20fetch_electricity_demand%2C%20pl%2C%20plot_time_series)%3A%0A%20%20%20%20_elec_mv%20%3D%20(%0A%20%20%20%20%20%20%20%20fetch_electricity_demand()%0A%20%20%20%20%20%20%20%20.frame.head(48%20*%2014)%0A%20%20%20%20%20%20%20%20.select(%0A%20%20%20%20%20%20%20%20%20%20%20%20%22time%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20pl.col(%22vic__demand%22).alias(%22demand%22)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20pl.col(%22nsw__demand%22).alias(%22nsw_demand%22)%2C%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20)%0A%20%20%20%20_ds_mv%20%3D%20Downsampler(interval%3D%221d%22%2C%20aggregation%3D%22mean%22)%0A%20%20%20%20_ds_mv.fit(_elec_mv)%0A%20%20%20%20_y_daily_mv%20%3D%20_ds_mv.transform(_elec_mv)%0A%20%20%20%20plot_time_series(_y_daily_mv%2C%20title%3D%22Daily%20Mean%3A%20Multivariate%20(VIC%20%2B%20NSW)%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.%20Boundary%20Settings%0A%0A%20%20%20%20The%20%60closed%60%20parameter%20controls%20which%20side%20of%20the%20interval%20boundary%20is%0A%20%20%20%20inclusive%2C%20and%20%60label%60%20controls%20which%20boundary%20timestamp%20represents%20the%0A%20%20%20%20bin.%20The%20chart%20below%20overlays%206-hour%20downsampling%20with%20%60left%2Fleft%60%20vs%0A%20%20%20%20%60right%2Fright%60%20settings%20so%20you%20can%20see%20the%20timestamp%20shift.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(Downsampler%2C%20plot_time_series%2C%20y_hf)%3A%0A%20%20%20%20_ds_left%20%3D%20Downsampler(interval%3D%226h%22%2C%20aggregation%3D%22mean%22%2C%20closed%3D%22left%22%2C%20label%3D%22left%22)%0A%20%20%20%20_ds_left.fit(y_hf)%0A%20%20%20%20_out_left%20%3D%20_ds_left.transform(y_hf)%0A%0A%20%20%20%20_ds_right%20%3D%20Downsampler(interval%3D%226h%22%2C%20aggregation%3D%22mean%22%2C%20closed%3D%22right%22%2C%20label%3D%22right%22)%0A%20%20%20%20_ds_right.fit(y_hf)%0A%20%20%20%20_out_right%20%3D%20_ds_right.transform(y_hf)%0A%0A%20%20%20%20_combined%20%3D%20(%0A%20%20%20%20%20%20%20%20_out_left%0A%20%20%20%20%20%20%20%20.select(%22time%22%2C%20%22demand%22)%0A%20%20%20%20%20%20%20%20.rename(%7B%22demand%22%3A%20%22left%2Fleft%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_out_right.select(%22time%22%2C%20%22demand%22).rename(%7B%22demand%22%3A%20%22right%2Fright%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%20%20%20%20how%3D%22full%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20coalesce%3DTrue%2C%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20.sort(%22time%22)%0A%20%20%20%20)%0A%20%20%20%20plot_time_series(%0A%20%20%20%20%20%20%20%20_combined.head(28)%2C%0A%20%20%20%20%20%20%20%20title%3D%226h%20Downsampling%3A%20Boundary%20Settings%20(first%204%20days)%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%207.%20Round-Trip%3A%20Downsample%20Then%20Upsample%0A%0A%20%20%20%20Downsampling%20discards%20within-interval%20variation%2C%20so%20upsampling%20back%20to%0A%20%20%20%20the%20original%20frequency%20cannot%20recover%20the%20lost%20detail.%20Here%20we%0A%20%20%20%20downsample%20to%20daily%20mean%20and%20then%20upsample%20back%20to%20half-hourly%20with%0A%20%20%20%20linear%20interpolation.%20Overlaying%20the%20original%20and%20round-trip%20versions%0A%20%20%20%20shows%20the%20amount%20of%20information%20lost.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(Upsampler%2C%20plot_time_series%2C%20y_daily_mean%2C%20y_hf)%3A%0A%20%20%20%20_us_rt%20%3D%20Upsampler(interval%3D%2230m%22%2C%20interpolation%3D%22linear%22)%0A%20%20%20%20_us_rt.fit(y_daily_mean)%0A%20%20%20%20_y_roundtrip%20%3D%20_us_rt.transform(y_daily_mean)%0A%20%20%20%20_n%20%3D%20min(len(y_hf)%2C%20len(_y_roundtrip))%0A%20%20%20%20_combined%20%3D%20(%0A%20%20%20%20%20%20%20%20y_hf%0A%20%20%20%20%20%20%20%20.head(_n)%0A%20%20%20%20%20%20%20%20.rename(%7B%22demand%22%3A%20%22original%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_y_roundtrip.head(_n).rename(%7B%22demand%22%3A%20%22round-trip%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(%0A%20%20%20%20%20%20%20%20_combined.head(96)%2C%0A%20%20%20%20%20%20%20%20title%3D%22Round-Trip%3A%20Original%20vs%20Down-then-Up%20(first%202%20days)%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%20Next%20Steps%0A%0A%20%20%20%20-%20%5BClean%20and%20Resample%20Time%20Series%5D(%2Fpages%2Fhow-to%2Fclean-and-resample%2F)%20for%20the%20full%20guide%0A%20%20%20%20-%20%5BHandle%20Missing%20Data%5D(%2Fpages%2Fhow-to%2Fhandle-missing-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
fcc96fcc37692bc836cc79e71a537440