%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%20Data%20Cleaning%3A%20Imputation%20and%20Outlier%20Handling%0A%0A%20%20%20%20This%20notebook%20shows%20how%20to%20clean%20time%20series%20data%20by%20filling%0A%20%20%20%20missing%20values%20with%20temporal%20imputers%20and%20handling%20outliers%0A%20%20%20%20with%20threshold-based%20handlers.%0A%0A%20%20%20%20**Prerequisites%3A**%20This%20is%20a%20standalone%20preprocessing%20guide.%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%20FeaturePipeline%0A%20%20%20%20from%20yohou.datasets%20import%20fetch_tourism_monthly%0A%20%20%20%20from%20yohou.plotting%20import%20plot_missing_data%2C%20plot_time_series%0A%20%20%20%20from%20yohou.preprocessing%20import%20(%0A%20%20%20%20%20%20%20%20OutlierPercentileHandler%2C%0A%20%20%20%20%20%20%20%20OutlierThresholdHandler%2C%0A%20%20%20%20%20%20%20%20SeasonalImputer%2C%0A%20%20%20%20%20%20%20%20SimpleTimeImputer%2C%0A%20%20%20%20)%0A%0A%20%20%20%20return%20(%0A%20%20%20%20%20%20%20%20FeaturePipeline%2C%0A%20%20%20%20%20%20%20%20OutlierPercentileHandler%2C%0A%20%20%20%20%20%20%20%20OutlierThresholdHandler%2C%0A%20%20%20%20%20%20%20%20SeasonalImputer%2C%0A%20%20%20%20%20%20%20%20SimpleTimeImputer%2C%0A%20%20%20%20%20%20%20%20fetch_tourism_monthly%2C%0A%20%20%20%20%20%20%20%20pl%2C%0A%20%20%20%20%20%20%20%20plot_missing_data%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.%20Create%20Data%20with%20Missing%20Values%0A%0A%20%20%20%20Load%20a%20single%20monthly%20tourism%20series%20and%20randomly%20remove%2015%0A%20%20%20%20observations%20to%20simulate%20sensor%20dropouts%20or%20data%20pipeline%20failures.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(fetch_tourism_monthly%2C%20pl)%3A%0A%20%20%20%20y%20%3D%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%0A%20%20%20%20%23%20Introduce%20missing%20values%20at%20random%20positions%0A%20%20%20%20import%20random%0A%0A%20%20%20%20random.seed(42)%0A%20%20%20%20null_indices%20%3D%20sorted(random.sample(range(len(y))%2C%2015))%0A%0A%20%20%20%20y_missing%20%3D%20(%0A%20%20%20%20%20%20%20%20y%0A%20%20%20%20%20%20%20%20.with_row_index(%22idx%22)%0A%20%20%20%20%20%20%20%20.with_columns(%0A%20%20%20%20%20%20%20%20%20%20%20%20pl.when(pl.col(%22idx%22).is_in(null_indices)).then(None).otherwise(pl.col(%22tourists%22)).alias(%22tourists%22)%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20.drop(%22idx%22)%0A%20%20%20%20)%0A%0A%20%20%20%20null_count%20%3D%20y_missing%5B%22tourists%22%5D.null_count()%0A%20%20%20%20print(f%22Introduced%20%7Bnull_count%7D%20missing%20values%22)%0A%20%20%20%20return%20y%2C%20y_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%20Use%20%5B%60plot_missing_data%60%5D(%2Fpages%2Fapi%2Fgenerated%2Fyohou.plotting.exploration.plot_missing_data%2F)%20to%20inspect%20which%20time%20steps%20have%20null%20values.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(plot_missing_data%2C%20y_missing)%3A%0A%20%20%20%20plot_missing_data(y_missing%2C%20title%3D%22Missing%20Data%20Pattern%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.%20Fill%20Gaps%20with%20SimpleTimeImputer%0A%0A%20%20%20%20Use%20%5B%60SimpleTimeImputer%60%5D(%2Fpages%2Fapi%2Fgenerated%2Fyohou.preprocessing.imputation.SimpleTimeImputer%2F)%20to%20fill%20missing%20values%20with%20time-aware%0A%20%20%20%20interpolation.%20Supported%20methods%3A%20%60%22linear%22%60%2C%20%60%22forward%22%60%2C%20%60%22backward%22%60%2C%0A%20%20%20%20and%20%60%22nearest%22%60.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(SimpleTimeImputer%2C%20y_missing)%3A%0A%20%20%20%20for%20method%20in%20%5B%22linear%22%2C%20%22forward%22%2C%20%22backward%22%2C%20%22nearest%22%5D%3A%0A%20%20%20%20%20%20%20%20imputer%20%3D%20SimpleTimeImputer(method%3Dmethod)%0A%20%20%20%20%20%20%20%20imputer.fit(y_missing)%0A%20%20%20%20%20%20%20%20filled%20%3D%20imputer.transform(y_missing)%0A%20%20%20%20%20%20%20%20remaining_nulls%20%3D%20filled%5B%22tourists%22%5D.null_count()%0A%20%20%20%20%20%20%20%20print(f%22method%3D%7Bmethod%3A%3E10s%7D%20%20remaining%20nulls%3A%20%7Bremaining_nulls%7D%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%20Verify%20that%20linear%20imputation%20filled%20the%20gaps%20smoothly.%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%20y_missing)%3A%0A%20%20%20%20linear_imputer%20%3D%20SimpleTimeImputer(method%3D%22linear%22)%0A%20%20%20%20linear_imputer.fit(y_missing)%0A%20%20%20%20y_filled%20%3D%20linear_imputer.transform(y_missing)%0A%0A%20%20%20%20_combined%20%3D%20y_filled.rename(%7B%22tourists%22%3A%20%22imputed%22%7D).join(%0A%20%20%20%20%20%20%20%20y_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%22After%20Linear%20Imputation%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.%20Fill%20Gaps%20with%20SeasonalImputer%0A%0A%20%20%20%20Use%20%5B%60SeasonalImputer%60%5D(%2Fpages%2Fapi%2Fgenerated%2Fyohou.preprocessing.imputation.SeasonalImputer%2F)%20to%20replace%20each%20missing%20value%20with%20the%20average%0A%20%20%20%20at%20the%20same%20seasonal%20position.%20Set%20%60period%3D12%60%20for%20monthly%20data%20so%20a%0A%20%20%20%20missing%20January%20is%20filled%20using%20Januaries%20from%20other%20years.%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%20y_missing)%3A%0A%20%20%20%20seasonal_imp%20%3D%20SeasonalImputer(period%3D12)%0A%20%20%20%20seasonal_imp.fit(y_missing)%0A%20%20%20%20y_seasonal%20%3D%20seasonal_imp.transform(y_missing)%0A%0A%20%20%20%20_combined%20%3D%20y_seasonal.rename(%7B%22tourists%22%3A%20%22imputed%22%7D).join(%0A%20%20%20%20%20%20%20%20y_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%22After%20Seasonal%20Imputation%20(period%3D12)%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.%20Handle%20Outliers%0A%0A%20%20%20%20Use%20%5B%60OutlierThresholdHandler%60%5D(%2Fpages%2Fapi%2Fgenerated%2Fyohou.preprocessing.outlier.OutlierThresholdHandler%2F)%20with%20fixed%20bounds%20or%0A%20%20%20%20%5B%60OutlierPercentileHandler%60%5D(%2Fpages%2Fapi%2Fgenerated%2Fyohou.preprocessing.outlier.OutlierPercentileHandler%2F)%20with%20data-driven%20percentile%20bounds.%0A%20%20%20%20Both%20support%20%60%22clip%22%60%20(cap%20at%20threshold)%20and%20%60%22nan%22%60%20(replace%20with%20null)%0A%20%20%20%20strategies.%0A%0A%20%20%20%20Inject%20synthetic%20spikes%20and%20dips%20to%20test%20both%20handlers.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(pl%2C%20y)%3A%0A%20%20%20%20%23%20Add%20outliers%0A%20%20%20%20y_outlier%20%3D%20(%0A%20%20%20%20%20%20%20%20y%0A%20%20%20%20%20%20%20%20.with_row_index(%22idx%22)%0A%20%20%20%20%20%20%20%20.with_columns(%0A%20%20%20%20%20%20%20%20%20%20%20%20pl%0A%20%20%20%20%20%20%20%20%20%20%20%20.when(pl.col(%22idx%22).is_in(%5B20%2C%2050%2C%2080%2C%20110%5D))%0A%20%20%20%20%20%20%20%20%20%20%20%20.then(pl.col(%22tourists%22)%20*%203)%20%20%23%20Spike%20outliers%0A%20%20%20%20%20%20%20%20%20%20%20%20.when(pl.col(%22idx%22).is_in(%5B30%2C%2070%2C%20100%5D))%0A%20%20%20%20%20%20%20%20%20%20%20%20.then(pl.col(%22tourists%22)%20*%200.2)%20%20%23%20Dip%20outliers%0A%20%20%20%20%20%20%20%20%20%20%20%20.otherwise(pl.col(%22tourists%22))%0A%20%20%20%20%20%20%20%20%20%20%20%20.alias(%22tourists%22)%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20.drop(%22idx%22)%0A%20%20%20%20)%0A%0A%20%20%20%20print(%22Added%204%20spikes%20and%203%20dips%22)%0A%20%20%20%20return%20(y_outlier%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%20OutlierThresholdHandler%0A%0A%20%20%20%20%5B%60OutlierThresholdHandler%60%5D(%2Fpages%2Fapi%2Fgenerated%2Fyohou.preprocessing.outlier.OutlierThresholdHandler%2F)%20clips%20or%20nullifies%20values%20that%20fall%20outside%0A%20%20%20%20user-specified%20%60low%60%20and%20%60high%60%20bounds.%20Use%20this%20when%20you%20know%20the%0A%20%20%20%20valid%20range%20of%20your%20data%20in%20advance.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(OutlierThresholdHandler%2C%20plot_time_series%2C%20y_outlier)%3A%0A%20%20%20%20clip_handler%20%3D%20OutlierThresholdHandler(low%3D100%2C%20high%3D500%2C%20strategy%3D%22clip%22)%0A%20%20%20%20clip_handler.fit(y_outlier)%0A%20%20%20%20y_clipped%20%3D%20clip_handler.transform(y_outlier)%0A%0A%20%20%20%20_combined%20%3D%20y_clipped.rename(%7B%22tourists%22%3A%20%22clipped%22%7D).join(%0A%20%20%20%20%20%20%20%20y_outlier.rename(%7B%22tourists%22%3A%20%22with%20outliers%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%22OutlierThresholdHandler%20(clip%20to%20%5B100%2C%20500%5D)%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(OutlierThresholdHandler%2C%20plot_time_series%2C%20y_outlier)%3A%0A%20%20%20%20nan_handler%20%3D%20OutlierThresholdHandler(low%3D100%2C%20high%3D500%2C%20strategy%3D%22nan%22)%0A%20%20%20%20nan_handler.fit(y_outlier)%0A%20%20%20%20y_nanified%20%3D%20nan_handler.transform(y_outlier)%0A%0A%20%20%20%20_combined%20%3D%20y_nanified.rename(%7B%22tourists%22%3A%20%22nullified%22%7D).join(%0A%20%20%20%20%20%20%20%20y_outlier.rename(%7B%22tourists%22%3A%20%22with%20outliers%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%22OutlierThresholdHandler%20(nan%20outside%20%5B100%2C%20500%5D)%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%20OutlierPercentileHandler%0A%0A%20%20%20%20%5B%60OutlierPercentileHandler%60%5D(%2Fpages%2Fapi%2Fgenerated%2Fyohou.preprocessing.outlier.OutlierPercentileHandler%2F)%20learns%20thresholds%20from%20the%20data%20at%20fit%20time.%0A%20%20%20%20The%20%60low%60%20and%20%60high%60%20parameters%20specify%20the%20percentile%20values%20(e.g.%205%20and%0A%20%20%20%2095)%20used%20as%20bounds.%20This%20is%20useful%20when%20you%20do%20not%20have%20domain%20knowledge%0A%20%20%20%20about%20the%20valid%20range%20and%20want%20the%20data%20itself%20to%20define%20what%20counts%20as%0A%20%20%20%20extreme.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(OutlierPercentileHandler%2C%20plot_time_series%2C%20y_outlier)%3A%0A%20%20%20%20pct_handler%20%3D%20OutlierPercentileHandler(low%3D5%2C%20high%3D95%2C%20strategy%3D%22clip%22)%0A%20%20%20%20pct_handler.fit(y_outlier)%0A%20%20%20%20y_pct%20%3D%20pct_handler.transform(y_outlier)%0A%0A%20%20%20%20_combined%20%3D%20y_pct.rename(%7B%22tourists%22%3A%20%22clipped%22%7D).join(%0A%20%20%20%20%20%20%20%20y_outlier.rename(%7B%22tourists%22%3A%20%22with%20outliers%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%22OutlierPercentileHandler%20(clip%20to%20%5B5th%2C%2095th%5D)%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.%20Chain%20Outlier%20Removal%20and%20Imputation%0A%0A%20%20%20%20Use%20%5B%60FeaturePipeline%60%5D(%2Fpages%2Fapi%2Fgenerated%2Fyohou.compose.feature_pipeline.FeaturePipeline%2F)%20to%20compose%20an%20outlier%20handler%20and%20imputer%0A%20%20%20%20into%20a%20single%20reusable%20pipeline.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(%0A%20%20%20%20FeaturePipeline%2C%0A%20%20%20%20OutlierPercentileHandler%2C%0A%20%20%20%20SimpleTimeImputer%2C%0A%20%20%20%20plot_time_series%2C%0A%20%20%20%20y_outlier%2C%0A)%3A%0A%20%20%20%20pipeline%20%3D%20FeaturePipeline(%0A%20%20%20%20%20%20%20%20steps%3D%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20(%22outlier%22%2C%20OutlierPercentileHandler(low%3D5%2C%20high%3D95%2C%20strategy%3D%22nan%22))%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20(%22impute%22%2C%20SimpleTimeImputer(method%3D%22linear%22))%2C%0A%20%20%20%20%20%20%20%20%5D%0A%20%20%20%20)%0A%20%20%20%20y_clean%20%3D%20pipeline.fit_transform(y_outlier)%0A%0A%20%20%20%20_combined%20%3D%20y_clean.rename(%7B%22tourists%22%3A%20%22cleaned%22%7D).join(%0A%20%20%20%20%20%20%20%20y_outlier.rename(%7B%22tourists%22%3A%20%22with%20outliers%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%22FeaturePipeline%3A%20Outlier%20Removal%20%2B%20Linear%20Imputation%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%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
beb33d5459d7bf67a665b15adf356ecb