%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%20How%20to%20Handle%20Missing%20Data%0A%0A%20%20%20%20Missing%20values%20are%20common%20in%20real-world%20time%20series.%20Yohou%20provides%0A%20%20%20%20temporal-aware%20imputation%20methods%20alongside%20sklearn%20wrappers.%0A%0A%20%20%20%20This%20notebook%20shows%20how%20to%20compare%20SimpleTimeImputer%2C%20SeasonalImputer%2C%20SimpleImputer%2C%20and%20TransformedSpaceKNNImputer%20on%20synthetic%20block%20and%20scattered%20gaps%20in%20monthly%20tourism%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%20import%20plotly.graph_objects%20as%20go%0A%20%20%20%20import%20polars%20as%20pl%0A%0A%20%20%20%20from%20yohou.datasets%20import%20fetch_tourism_monthly%0A%20%20%20%20from%20yohou.plotting%20import%20plot_time_series%0A%20%20%20%20from%20yohou.preprocessing%20import%20SeasonalImputer%2C%20SimpleImputer%2C%20SimpleTimeImputer%2C%20TransformedSpaceKNNImputer%0A%0A%20%20%20%20return%20(%0A%20%20%20%20%20%20%20%20SeasonalImputer%2C%0A%20%20%20%20%20%20%20%20SimpleImputer%2C%0A%20%20%20%20%20%20%20%20SimpleTimeImputer%2C%0A%20%20%20%20%20%20%20%20TransformedSpaceKNNImputer%2C%0A%20%20%20%20%20%20%20%20fetch_tourism_monthly%2C%0A%20%20%20%20%20%20%20%20go%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.%20Prepare%20the%20Data%3A%20Synthetic%20Gaps%20in%20Monthly%20Tourism%0A%0A%20%20%20%20We%20load%20the%20monthly%20tourism%20dataset%20with%20%60fetch_tourism_monthly()%60%2C%20pick%20a%0A%20%20%20%20single%20series%2C%20and%20punch%20holes%20in%20it%3A%20a%20contiguous%20block%20of%205%20missing%20months%0A%20%20%20%20plus%204%20scattered%20single-month%20gaps.%20This%20mix%20of%20gap%20patterns%20lets%20us%20compare%0A%20%20%20%20how%20each%20imputer%20handles%20short%20isolated%20gaps%20versus%20longer%20contiguous%20ones.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(fetch_tourism_monthly%2C%20mo%2C%20pl)%3A%0A%20%20%20%20tourism%20%3D%20(%0A%20%20%20%20%20%20%20%20fetch_tourism_monthly().frame.select(%22time%22%2C%20%22T1__tourists%22).drop_nulls().rename(%7B%22T1__tourists%22%3A%20%22tourists%22%7D)%0A%20%20%20%20)%0A%0A%20%20%20%20%23%20Create%20gaps%3A%20indices%2030-34%20(block)%2C%20and%20scattered%20singles%0A%20%20%20%20_gap_indices%20%3D%20list(range(30%2C%2035))%20%2B%20%5B50%2C%2070%2C%2090%2C%20110%5D%0A%20%20%20%20_mask%20%3D%20pl.Series(%5Bi%20in%20_gap_indices%20for%20i%20in%20range(len(tourism))%5D)%0A%20%20%20%20tourism_missing%20%3D%20tourism.with_columns(%0A%20%20%20%20%20%20%20%20pl.when(_mask).then(None).otherwise(pl.col(%22tourists%22)).alias(%22tourists%22)%2C%0A%20%20%20%20)%0A%20%20%20%20_n_missing%20%3D%20tourism_missing.null_count()%5B%22tourists%22%5D%5B0%5D%0A%0A%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20f%22**Original**%3A%20%7Blen(tourism)%7D%20months%2C%20no%20gaps%5Cn%5Cn%22%0A%20%20%20%20%20%20%20%20f%22**With%20gaps**%3A%20%7B_n_missing%7D%20missing%20values%20%22%0A%20%20%20%20%20%20%20%20f%22(block%20of%205%20%2B%204%20scattered)%22%0A%20%20%20%20)%0A%20%20%20%20return%20tourism%2C%20tourism_missing%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%20one%20or%20more%20numeric%20columns%20against%20the%20%60%22time%22%60%0A%20%20%20%20axis.%20Gaps%20in%20the%20data%20appear%20as%20breaks%20in%20the%20line%2C%20making%20missing%20regions%0A%20%20%20%20easy%20to%20spot%20visually.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(plot_time_series%2C%20tourism_missing)%3A%0A%20%20%20%20plot_time_series(tourism_missing%2C%20title%3D%22Monthly%20Tourism%3A%20With%20Synthetic%20Gaps%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.%20SimpleTimeImputer%3A%20Linear%20Interpolation%0A%0A%20%20%20%20%5B%60SimpleTimeImputer%60%5D(%2Fpages%2Fapi%2Fgenerated%2Fyohou.preprocessing.imputation.SimpleTimeImputer%2F)%20fills%20missing%20values%20using%20time-aware%20strategies.%0A%20%20%20%20With%20%60method%3D%22linear%22%60%2C%20it%20draws%20a%20straight%20line%20between%20the%20last%20known%0A%20%20%20%20value%20before%20a%20gap%20and%20the%20first%20known%20value%20after%20it%2C%20then%20places%20the%0A%20%20%20%20missing%20points%20along%20that%20line.%20This%20works%20well%20for%20short%2C%20smooth%20gaps.%0A%0A%20%20%20%20After%20imputation%20we%20use%20%5B%60plot_time_series%60%5D(%2Fpages%2Fapi%2Fgenerated%2Fyohou.plotting.exploration.plot_time_series%2F)%20to%20confirm%20the%20gaps%20are%20filled.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(SimpleTimeImputer%2C%20plot_time_series%2C%20tourism_missing)%3A%0A%20%20%20%20imp_linear%20%3D%20SimpleTimeImputer(method%3D%22linear%22)%0A%20%20%20%20imp_linear.fit(tourism_missing)%0A%20%20%20%20filled_linear%20%3D%20imp_linear.transform(tourism_missing)%0A%20%20%20%20_combined%20%3D%20filled_linear.rename(%7B%22tourists%22%3A%20%22imputed%22%7D).join(%0A%20%20%20%20%20%20%20%20tourism_missing.rename(%7B%22tourists%22%3A%20%22with%20gaps%22%7D)%2C%0A%20%20%20%20%20%20%20%20on%3D%22time%22%2C%0A%20%20%20%20)%0A%20%20%20%20plot_time_series(_combined%2C%20title%3D%22Linear%20Interpolation%22)%0A%20%20%20%20return%20(filled_linear%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.%20SimpleTimeImputer%3A%20Forward%20Fill%0A%0A%20%20%20%20Forward%20fill%20(%60method%3D%22forward%22%60)%20carries%20the%20last%20observed%20value%20forward%0A%20%20%20%20through%20each%20gap.%20It%20preserves%20the%20level%20before%20the%20gap%20but%20introduces%20a%0A%20%20%20%20flat%20segment%2C%20so%20it%20is%20most%20appropriate%20when%20gradual%20drift%20between%0A%20%20%20%20observations%20is%20unlikely.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(SimpleTimeImputer%2C%20plot_time_series%2C%20tourism_missing)%3A%0A%20%20%20%20imp_forward%20%3D%20SimpleTimeImputer(method%3D%22forward%22)%0A%20%20%20%20imp_forward.fit(tourism_missing)%0A%20%20%20%20filled_forward%20%3D%20imp_forward.transform(tourism_missing)%0A%20%20%20%20_combined%20%3D%20filled_forward.rename(%7B%22tourists%22%3A%20%22imputed%22%7D).join(%0A%20%20%20%20%20%20%20%20tourism_missing.rename(%7B%22tourists%22%3A%20%22with%20gaps%22%7D)%2C%0A%20%20%20%20%20%20%20%20on%3D%22time%22%2C%0A%20%20%20%20)%0A%20%20%20%20plot_time_series(_combined%2C%20title%3D%22Forward%20Fill%22)%0A%20%20%20%20return%20(filled_forward%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%204.%20SimpleTimeImputer%20with%20%60limit%60%0A%0A%20%20%20%20The%20%60limit%60%20parameter%20caps%20the%20maximum%20number%20of%20consecutive%20NaN%0A%20%20%20%20values%20that%20are%20filled.%20Useful%20when%20you%20don't%20want%20to%20interpolate%0A%20%20%20%20across%20long%20gaps.%20Here%20we%20set%20%60limit%3D2%60%2C%20so%20the%20block%20of%205%0A%20%20%20%20consecutive%20missing%20values%20is%20only%20partially%20filled%20while%20shorter%0A%20%20%20%20gaps%20are%20handled%20completely.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(SimpleTimeImputer%2C%20plot_time_series%2C%20tourism_missing)%3A%0A%20%20%20%20imp_limited%20%3D%20SimpleTimeImputer(method%3D%22forward%22%2C%20limit%3D2)%0A%20%20%20%20imp_limited.fit(tourism_missing)%0A%20%20%20%20filled_limited%20%3D%20imp_limited.transform(tourism_missing)%0A%20%20%20%20_combined%20%3D%20filled_limited.rename(%7B%22tourists%22%3A%20%22imputed%20(limit%3D2)%22%7D).join(%0A%20%20%20%20%20%20%20%20tourism_missing.rename(%7B%22tourists%22%3A%20%22with%20gaps%22%7D)%2C%0A%20%20%20%20%20%20%20%20on%3D%22time%22%2C%0A%20%20%20%20)%0A%20%20%20%20plot_time_series(_combined%2C%20title%3D%22Linear%20Interpolation%20with%20limit%3D2%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.%20SeasonalImputer%0A%0A%20%20%20%20%5B%60SeasonalImputer%60%5D(%2Fpages%2Fapi%2Fgenerated%2Fyohou.preprocessing.imputation.SeasonalImputer%2F)%20fills%20missing%20values%20by%20looking%20at%20the%20same%20position%20in%0A%20%20%20%20previous%20seasonal%20cycles.%20Setting%20%60period%3D12%60%20on%20monthly%20data%20means%20a%0A%20%20%20%20missing%20January%20is%20filled%20with%20the%20average%20of%20all%20other%20Januaries%20in%20the%0A%20%20%20%20series.%20The%20%60fill_method%60%20parameter%20controls%20the%20aggregation%3A%0A%20%20%20%20%60%22seasonal_mean%22%60%20uses%20the%20mean%2C%20while%20%60%22seasonal_median%22%60%20uses%20the%20median.%0A%20%20%20%20This%20approach%20is%20effective%20when%20the%20series%20has%20strong%2C%20repeating%20seasonal%0A%20%20%20%20patterns.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(SeasonalImputer%2C%20plot_time_series%2C%20tourism_missing)%3A%0A%20%20%20%20imp_seasonal%20%3D%20SeasonalImputer(period%3D12%2C%20fill_method%3D%22seasonal_mean%22)%0A%20%20%20%20imp_seasonal.fit(tourism_missing)%0A%20%20%20%20filled_seasonal%20%3D%20imp_seasonal.transform(tourism_missing)%0A%20%20%20%20_combined%20%3D%20filled_seasonal.rename(%7B%22tourists%22%3A%20%22imputed%22%7D).join(%0A%20%20%20%20%20%20%20%20tourism_missing.rename(%7B%22tourists%22%3A%20%22with%20gaps%22%7D)%2C%0A%20%20%20%20%20%20%20%20on%3D%22time%22%2C%0A%20%20%20%20)%0A%20%20%20%20plot_time_series(_combined%2C%20title%3D%22Seasonal%20Imputer%20(period%3D12%2C%20seasonal_mean)%22)%0A%20%20%20%20return%20(filled_seasonal%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.%20Sklearn-Wrapped%20Imputers%0A%0A%20%20%20%20Yohou%20wraps%20several%20Scikit-Learn%20imputers%20so%20they%20work%20seamlessly%20with%0A%20%20%20%20time-indexed%20Polars%20DataFrames.%0A%0A%20%20%20%20%5B%60SimpleImputer%60%5D(%2Fpages%2Fapi%2Fgenerated%2Fyohou.preprocessing.imputation.SimpleImputer%2F)%20replaces%20every%20missing%20value%20with%20a%20single%20summary%0A%20%20%20%20statistic%20of%20the%20column%20(controlled%20by%20the%20%60strategy%60%20parameter%3A%0A%20%20%20%20%60%22mean%22%60%2C%20%60%22median%22%60%2C%20or%20%60%22constant%22%60).%20It%20ignores%20temporal%20order%0A%20%20%20%20entirely%2C%20which%20makes%20it%20a%20useful%20baseline.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(SimpleImputer%2C%20plot_time_series%2C%20tourism_missing)%3A%0A%20%20%20%20imp_mean%20%3D%20SimpleImputer(strategy%3D%22mean%22)%0A%20%20%20%20imp_mean.fit(tourism_missing)%0A%20%20%20%20filled_mean%20%3D%20imp_mean.transform(tourism_missing)%0A%20%20%20%20_combined%20%3D%20filled_mean.rename(%7B%22tourists%22%3A%20%22imputed%22%7D).join(%0A%20%20%20%20%20%20%20%20tourism_missing.rename(%7B%22tourists%22%3A%20%22with%20gaps%22%7D)%2C%0A%20%20%20%20%20%20%20%20on%3D%22time%22%2C%0A%20%20%20%20)%0A%20%20%20%20plot_time_series(_combined%2C%20title%3D%22SimpleImputer%20(strategy%3D'mean')%22)%0A%20%20%20%20return%20(filled_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%5B%60TransformedSpaceKNNImputer%60%5D(%2Fpages%2Fapi%2Fgenerated%2Fyohou.preprocessing.imputation.TransformedSpaceKNNImputer%2F)%20estimates%20each%20missing%20value%20from%20its%0A%20%20%20%20%60n_neighbors%60%20nearest%20complete%20observations%20in%20feature%20space.%20This%0A%20%20%20%20produces%20locally%20adaptive%20fills%20that%20can%20follow%20nonlinear%20patterns%0A%20%20%20%20better%20than%20a%20global%20mean%20or%20median.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(TransformedSpaceKNNImputer%2C%20plot_time_series%2C%20tourism_missing)%3A%0A%20%20%20%20imp_knn%20%3D%20TransformedSpaceKNNImputer(n_neighbors%3D5)%0A%20%20%20%20imp_knn.fit(tourism_missing)%0A%20%20%20%20filled_knn%20%3D%20imp_knn.transform(tourism_missing)%0A%20%20%20%20_combined%20%3D%20filled_knn.rename(%7B%22tourists%22%3A%20%22imputed%22%7D).join(%0A%20%20%20%20%20%20%20%20tourism_missing.rename(%7B%22tourists%22%3A%20%22with%20gaps%22%7D)%2C%0A%20%20%20%20%20%20%20%20on%3D%22time%22%2C%0A%20%20%20%20)%0A%20%20%20%20plot_time_series(_combined%2C%20title%3D%22TransformedSpaceKNNImputer%20(n_neighbors%3D5)%22)%0A%20%20%20%20return%20(filled_knn%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%207.%20Compare%20Methods%0A%0A%20%20%20%20Because%20we%20created%20the%20gaps%20ourselves%2C%20we%20can%20measure%20each%20method's%0A%20%20%20%20quality%20both%20visually%20and%20numerically.%0A%0A%20%20%20%20First%20we%20overlay%20all%20five%20imputed%20series%20on%20a%20single%20interactive%20chart.%0A%20%20%20%20Click%20legend%20entries%20to%20toggle%20individual%20methods%20on%20and%20off%2C%20making%20it%0A%20%20%20%20easy%20to%20compare%20how%20each%20one%20fills%20the%20same%20gaps.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(%0A%20%20%20%20filled_forward%2C%0A%20%20%20%20filled_knn%2C%0A%20%20%20%20filled_linear%2C%0A%20%20%20%20filled_mean%2C%0A%20%20%20%20filled_seasonal%2C%0A%20%20%20%20plot_time_series%2C%0A%20%20%20%20tourism_missing%2C%0A)%3A%0A%20%20%20%20_all_methods%20%3D%20(%0A%20%20%20%20%20%20%20%20tourism_missing%0A%20%20%20%20%20%20%20%20.rename(%7B%22tourists%22%3A%20%22with%20gaps%22%7D)%0A%20%20%20%20%20%20%20%20.join(filled_linear.rename(%7B%22tourists%22%3A%20%22Linear%22%7D)%2C%20on%3D%22time%22)%0A%20%20%20%20%20%20%20%20.join(filled_forward.rename(%7B%22tourists%22%3A%20%22Forward%22%7D)%2C%20on%3D%22time%22)%0A%20%20%20%20%20%20%20%20.join(filled_seasonal.rename(%7B%22tourists%22%3A%20%22Seasonal%20(12)%22%7D)%2C%20on%3D%22time%22)%0A%20%20%20%20%20%20%20%20.join(filled_mean.rename(%7B%22tourists%22%3A%20%22Mean%22%7D)%2C%20on%3D%22time%22)%0A%20%20%20%20%20%20%20%20.join(filled_knn.rename(%7B%22tourists%22%3A%20%22KNN%20(5)%22%7D)%2C%20on%3D%22time%22)%0A%20%20%20%20)%0A%20%20%20%20plot_time_series(_all_methods%2C%20title%3D%22All%20Imputation%20Methods%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%20Next%20we%20compute%20the%20Mean%20Absolute%20Error%20(MAE)%20between%20the%20imputed%0A%20%20%20%20values%20and%20the%20original%20(pre-gap)%20values%20at%20the%20nine%20gap%20positions.%0A%20%20%20%20A%20grouped%20bar%20chart%20makes%20the%20ranking%20immediately%20visible.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(%0A%20%20%20%20filled_forward%2C%0A%20%20%20%20filled_knn%2C%0A%20%20%20%20filled_linear%2C%0A%20%20%20%20filled_mean%2C%0A%20%20%20%20filled_seasonal%2C%0A%20%20%20%20go%2C%0A%20%20%20%20tourism%2C%0A)%3A%0A%20%20%20%20_gap_indices%20%3D%20list(range(30%2C%2035))%20%2B%20%5B50%2C%2070%2C%2090%2C%20110%5D%0A%20%20%20%20_true_vals%20%3D%20tourism%5B%22tourists%22%5D.gather(_gap_indices)%0A%0A%20%20%20%20_methods%20%3D%20%7B%0A%20%20%20%20%20%20%20%20%22Linear%22%3A%20filled_linear%2C%0A%20%20%20%20%20%20%20%20%22Forward%22%3A%20filled_forward%2C%0A%20%20%20%20%20%20%20%20%22Seasonal%20(12)%22%3A%20filled_seasonal%2C%0A%20%20%20%20%20%20%20%20%22Mean%22%3A%20filled_mean%2C%0A%20%20%20%20%20%20%20%20%22KNN%20(5)%22%3A%20filled_knn%2C%0A%20%20%20%20%7D%0A%0A%20%20%20%20_scores%20%3D%20%7B%7D%0A%20%20%20%20for%20_name%2C%20_filled%20in%20_methods.items()%3A%0A%20%20%20%20%20%20%20%20_filled_vals%20%3D%20_filled%5B%22tourists%22%5D.gather(_gap_indices)%0A%20%20%20%20%20%20%20%20_scores%5B_name%5D%20%3D%20round(float((_filled_vals%20-%20_true_vals).abs().mean())%2C%202)%0A%0A%20%20%20%20_sorted%20%3D%20sorted(_scores.items()%2C%20key%3Dlambda%20x%3A%20x%5B1%5D)%0A%20%20%20%20_names%20%3D%20%5Bs%5B0%5D%20for%20s%20in%20_sorted%5D%0A%20%20%20%20_values%20%3D%20%5Bs%5B1%5D%20for%20s%20in%20_sorted%5D%0A%0A%20%20%20%20fig%20%3D%20go.Figure(%0A%20%20%20%20%20%20%20%20go.Bar(%0A%20%20%20%20%20%20%20%20%20%20%20%20x%3D_names%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20y%3D_values%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20text%3D%5Bf%22%7Bv%3A.2f%7D%22%20for%20v%20in%20_values%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20textposition%3D%22outside%22%2C%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20)%0A%20%20%20%20fig.update_layout(title%3D%22Imputation%20MAE%20at%20Gap%20Positions%22%2C%20yaxis_title%3D%22MAE%22)%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%20Alternative%3A%20Skip%20NaN%20Rows%20with%20%60nan_handling%60%0A%0A%20%20%20%20Instead%20of%20imputing%2C%20you%20can%20let%20the%20reduction%20forecaster%20drop%20any%0A%20%20%20%20tabularized%20training%20row%20that%20contains%20NaN.%20This%20is%20useful%20when%20gaps%0A%20%20%20%20are%20sparse%20and%20you%20want%20to%20avoid%20imputation%20artifacts.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(tourism_missing)%3A%0A%20%20%20%20from%20sklearn.ensemble%20import%20HistGradientBoostingRegressor%0A%20%20%20%20from%20sklearn.linear_model%20import%20Ridge%0A%0A%20%20%20%20from%20yohou.point%20import%20PointReductionForecaster%0A%0A%20%20%20%20%23%20Tree-based%3A%20drop%20NaN%20rows%20before%20fitting%0A%20%20%20%20tree_forecaster%20%3D%20PointReductionForecaster(%0A%20%20%20%20%20%20%20%20estimator%3DHistGradientBoostingRegressor()%2C%0A%20%20%20%20%20%20%20%20nan_handling%3D%22drop%22%2C%0A%20%20%20%20%20%20%20%20reduction_strategy%3D%22direct%22%2C%0A%20%20%20%20)%0A%20%20%20%20tree_forecaster.fit(y%3Dtourism_missing%2C%20forecasting_horizon%3D3)%0A%20%20%20%20tree_pred%20%3D%20tree_forecaster.predict(forecasting_horizon%3D3)%0A%0A%20%20%20%20%23%20Linear%3A%20drop%20NaN%20rows%20before%20fitting%0A%20%20%20%20linear_forecaster%20%3D%20PointReductionForecaster(%0A%20%20%20%20%20%20%20%20estimator%3DRidge()%2C%0A%20%20%20%20%20%20%20%20nan_handling%3D%22drop%22%2C%0A%20%20%20%20%20%20%20%20reduction_strategy%3D%22direct%22%2C%0A%20%20%20%20)%0A%20%20%20%20linear_forecaster.fit(y%3Dtourism_missing%2C%20forecasting_horizon%3D3)%0A%20%20%20%20linear_pred%20%3D%20linear_forecaster.predict(forecasting_horizon%3D3)%0A%20%20%20%20return%20linear_pred%2C%20tree_pred%0A%0A%0A%40app.cell%0Adef%20_(linear_pred%2C%20pl%2C%20tree_pred)%3A%0A%20%20%20%20comparison%20%3D%20pl.DataFrame(%7B%0A%20%20%20%20%20%20%20%20%22time%22%3A%20tree_pred%5B%22time%22%5D%2C%0A%20%20%20%20%20%20%20%20%22tree_prediction%22%3A%20tree_pred%5B%22tourists%22%5D%2C%0A%20%20%20%20%20%20%20%20%22linear_prediction%22%3A%20linear_pred%5B%22tourists%22%5D%2C%0A%20%20%20%20%7D)%0A%20%20%20%20comparison%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%5BHandle%20Missing%20Data%5D(%2Fpages%2Fhow-to%2Fhandle-missing-data%2F)%20for%20the%20full%20guide%0A%20%20%20%20-%20%5BClean%20and%20Resample%20Time%20Series%5D(%2Fpages%2Fhow-to%2Fclean-and-resample%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
c45d98aa3e138562b1af568db3f126a0