%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%20Stationarity%20Transformers%0A%0A%20%20%20%20Many%20forecasting%20methods%20assume%20the%20time%20series%20is%20**stationary**%20(constant%20mean%0A%20%20%20%20and%20variance).%20Yohou%20provides%20invertible%20transformers%20to%20stabilize%20variance%0A%20%20%20%20and%20remove%20trends.%0A%0A%20%20%20%20This%20notebook%20shows%20how%20to%20use%20the%20catalogue%20of%20variance-stabilising%20and%20detrending%20transforms%3A%20LogTransformer%2C%20BoxCox%2C%20SeasonalDifferencing%2C%20SeasonalReturn%2C%20and%20ASinh%20with%20inverse%20verification.%0A%0A%20%20%20%20**Prerequisites%3A**%20Understanding%20of%20stationarity%20concepts%20in%20time%20series.%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_tourism_monthly%0A%20%20%20%20from%20yohou.plotting%20import%20plot_time_series%0A%20%20%20%20from%20yohou.stationarity%20import%20(%0A%20%20%20%20%20%20%20%20AbsoluteSeasonalReturn%2C%0A%20%20%20%20%20%20%20%20ASinhTransformer%2C%0A%20%20%20%20%20%20%20%20BoxCoxTransformer%2C%0A%20%20%20%20%20%20%20%20LogTransformer%2C%0A%20%20%20%20%20%20%20%20SeasonalDifferencing%2C%0A%20%20%20%20%20%20%20%20SeasonalLogDifferencing%2C%0A%20%20%20%20%20%20%20%20SeasonalReturn%2C%0A%20%20%20%20)%0A%0A%20%20%20%20return%20(%0A%20%20%20%20%20%20%20%20ASinhTransformer%2C%0A%20%20%20%20%20%20%20%20AbsoluteSeasonalReturn%2C%0A%20%20%20%20%20%20%20%20BoxCoxTransformer%2C%0A%20%20%20%20%20%20%20%20LogTransformer%2C%0A%20%20%20%20%20%20%20%20SeasonalDifferencing%2C%0A%20%20%20%20%20%20%20%20SeasonalLogDifferencing%2C%0A%20%20%20%20%20%20%20%20SeasonalReturn%2C%0A%20%20%20%20%20%20%20%20fetch_tourism_monthly%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%20Data%0A%0A%20%20%20%20Monthly%20Tourism%20has%20seasonality%20and%20an%20upward%20trend%2C%0A%20%20%20%20clearly%20non-stationary.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(fetch_tourism_monthly%2C%20plot_time_series)%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%20%20%20%20plot_time_series(y%2C%20title%3D%22Monthly%20Tourism%20(Non-Stationary)%22)%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%20%23%23%202.%20LogTransformer%0A%0A%20%20%20%20Takes%20the%20natural%20log%20to%20stabilize%20multiplicative%20variance.%0A%20%20%20%20Stateless%20and%20invertible.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(LogTransformer%2C%20y)%3A%0A%20%20%20%20log_tf%20%3D%20LogTransformer(offset%3D0.0)%0A%20%20%20%20log_tf.fit(y)%0A%20%20%20%20y_log%20%3D%20log_tf.transform(y)%0A%0A%20%20%20%20data_col%20%3D%20%5Bc%20for%20c%20in%20y_log.columns%20if%20c%20!%3D%20%22time%22%5D%5B0%5D%0A%20%20%20%20print(f%22observation_horizon%3A%20%7Blog_tf.observation_horizon%7D%22)%0A%20%20%20%20print(f%22Original%20range%3A%20%5B%7By%5B'tourists'%5D.min()%7D%2C%20%7By%5B'tourists'%5D.max()%7D%5D%22)%0A%20%20%20%20print(f%22Log%20range%3A%20%5B%7By_log%5Bdata_col%5D.min()%3A.2f%7D%2C%20%7By_log%5Bdata_col%5D.max()%3A.2f%7D%5D%22)%0A%0A%20%20%20%20%23%20Verify%20inverse%20transform%20recovers%20original%20(X_p%3DNone%20for%20stateless%20transforms)%0A%20%20%20%20y_recovered%20%3D%20log_tf.inverse_transform(y_log%2C%20X_p%3DNone)%0A%20%20%20%20rec_col%20%3D%20%5Bc%20for%20c%20in%20y_recovered.columns%20if%20c%20!%3D%20%22time%22%5D%5B0%5D%0A%20%20%20%20log_inv_err%20%3D%20(y%5B%22tourists%22%5D%20-%20y_recovered%5Brec_col%5D).abs().max()%0A%20%20%20%20print(f%22Max%20inverse%20error%3A%20%7Blog_inv_err%3A.10f%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%20%23%23%203.%20BoxCoxTransformer%0A%0A%20%20%20%20Generalization%20of%20LogTransformer.%20%60lmbda%3D0%60%20is%20log%2C%20%60lmbda%3D1%60%20is%20identity%2C%0A%20%20%20%20%60lmbda%3D0.5%60%20is%20square%20root.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(BoxCoxTransformer%2C%20y)%3A%0A%20%20%20%20for%20_lmbda%20in%20%5B0.0%2C%200.25%2C%200.5%2C%201.0%5D%3A%0A%20%20%20%20%20%20%20%20_bc%20%3D%20BoxCoxTransformer(lmbda%3D_lmbda%2C%20offset%3D0.0)%0A%20%20%20%20%20%20%20%20_bc.fit(y)%0A%20%20%20%20%20%20%20%20_y_bc%20%3D%20_bc.transform(y)%0A%20%20%20%20%20%20%20%20_data_col%20%3D%20%5Bc%20for%20c%20in%20_y_bc.columns%20if%20c%20!%3D%20%22time%22%5D%5B0%5D%0A%20%20%20%20%20%20%20%20_rng%20%3D%20_y_bc%5B_data_col%5D.max()%20-%20_y_bc%5B_data_col%5D.min()%0A%20%20%20%20%20%20%20%20print(f%22lmbda%3D%7B_lmbda%3A.2f%7D%20%20range%3D%7B_rng%3A.2f%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%20%23%23%204.%20SeasonalDifferencing%0A%0A%20%20%20%20Removes%20seasonal%20patterns%20by%20computing%20%60y_t%20-%20y_%7Bt-s%7D%60.%0A%20%20%20%20Stateful%3A%20%60observation_horizon%20%3D%20seasonality%60.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(SeasonalDifferencing%2C%20y)%3A%0A%20%20%20%20diff_tf%20%3D%20SeasonalDifferencing(seasonality%3D12)%0A%20%20%20%20diff_tf.fit(y)%0A%20%20%20%20y_diff%20%3D%20diff_tf.transform(y)%0A%0A%20%20%20%20print(f%22observation_horizon%3A%20%7Bdiff_tf.observation_horizon%7D%22)%0A%20%20%20%20print(f%22Original%3A%20%7Blen(y)%7D%20rows%20%E2%86%92%20Differenced%3A%20%7Blen(y_diff)%7D%20rows%22)%0A%20%20%20%20y_diff.head()%0A%20%20%20%20return%20diff_tf%2C%20y_diff%0A%0A%0A%40app.cell%0Adef%20_(diff_tf%2C%20y%2C%20y_diff)%3A%0A%20%20%20%20%23%20Inverse%20transform%20requires%20X_p%20(the%20observation_horizon%20prefix%20from%20original%20data)%0A%20%20%20%20y_prefix%20%3D%20y.head(diff_tf.observation_horizon)%0A%20%20%20%20y_inv%20%3D%20diff_tf.inverse_transform(y_diff%2C%20X_p%3Dy_prefix)%0A%20%20%20%20_inv_col%20%3D%20%5Bc%20for%20c%20in%20y_inv.columns%20if%20c%20!%3D%20%22time%22%5D%5B0%5D%0A%20%20%20%20diff_inv_err%20%3D%20(y%5B%22tourists%22%5D.tail(len(y_inv))%20-%20y_inv%5B_inv_col%5D).abs().max()%0A%20%20%20%20print(f%22SeasonalDifferencing%20inverse%20error%3A%20%7Bdiff_inv_err%3A.10f%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%20%23%23%205.%20SeasonalLogDifferencing%0A%0A%20%20%20%20Combines%20log%20transform%20and%20seasonal%20differencing%20in%20one%20step%3A%0A%20%20%20%20%60log(y_t)%20-%20log(y_%7Bt-s%7D)%60.%20Particularly%20effective%20for%20multiplicative%20seasonality.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(SeasonalLogDifferencing%2C%20plot_time_series%2C%20y)%3A%0A%20%20%20%20sld%20%3D%20SeasonalLogDifferencing(seasonality%3D12%2C%20offset%3D0.0)%0A%20%20%20%20sld.fit(y)%0A%20%20%20%20y_sld%20%3D%20sld.transform(y)%0A%0A%20%20%20%20print(f%22observation_horizon%3A%20%7Bsld.observation_horizon%7D%22)%0A%20%20%20%20plot_time_series(y_sld%2C%20title%3D%22Seasonal%20Log%20Differenced%20(should%20look%20stationary)%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.%20Seasonal%20Returns%0A%0A%20%20%20%20-%20%5B%60SeasonalReturn%60%5D(%2Fpages%2Fapi%2Fgenerated%2Fyohou.stationarity.transformers.SeasonalReturn%2F)%3A%20Percentage%20change%20%60(y_t%20%2F%20y_%7Bt-s%7D)%20-%201%60%0A%20%20%20%20-%20%5B%60AbsoluteSeasonalReturn%60%5D(%2Fpages%2Fapi%2Fgenerated%2Fyohou.stationarity.transformers.AbsoluteSeasonalReturn%2F)%3A%20Absolute%20change%20%60y_t%20-%20y_%7Bt-s%7D%60%0A%0A%20%20%20%20Both%20are%20invertible%20and%20stateful.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(AbsoluteSeasonalReturn%2C%20SeasonalReturn%2C%20y)%3A%0A%20%20%20%20sr%20%3D%20SeasonalReturn(seasonality%3D12%2C%20offset%3D0.0)%0A%20%20%20%20sr.fit(y)%0A%20%20%20%20y_sr%20%3D%20sr.transform(y)%0A%20%20%20%20_sr_col%20%3D%20%5Bc%20for%20c%20in%20y_sr.columns%20if%20c%20!%3D%20%22time%22%5D%5B0%5D%0A%20%20%20%20print(f%22SeasonalReturn%20(first%205%20values)%3A%20%7By_sr%5B_sr_col%5D.head(5).to_list()%7D%22)%0A%0A%20%20%20%20asr%20%3D%20AbsoluteSeasonalReturn(seasonality%3D12)%0A%20%20%20%20asr.fit(y)%0A%20%20%20%20y_asr%20%3D%20asr.transform(y)%0A%20%20%20%20_asr_col%20%3D%20%5Bc%20for%20c%20in%20y_asr.columns%20if%20c%20!%3D%20%22time%22%5D%5B0%5D%0A%20%20%20%20print(f%22AbsoluteSeasonalReturn%20(first%205%20values)%3A%20%7By_asr%5B_asr_col%5D.head(5).to_list()%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%20%23%23%207.%20ASinhTransformer%0A%0A%20%20%20%20Inverse%20hyperbolic%20sine%3A%20%60asinh((x%20-%20median)%20%2F%20MAD%20*%20scale)%60.%0A%20%20%20%20Robust%2C%20symmetric%2C%20works%20with%20zero%20and%20negative%20values.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(ASinhTransformer%2C%20y)%3A%0A%20%20%20%20asinh_tf%20%3D%20ASinhTransformer(scale%3D1.4826)%0A%20%20%20%20asinh_tf.fit(y)%0A%20%20%20%20y_asinh%20%3D%20asinh_tf.transform(y)%0A%0A%20%20%20%20_asinh_col%20%3D%20%5Bc%20for%20c%20in%20y_asinh.columns%20if%20c%20!%3D%20%22time%22%5D%5B0%5D%0A%20%20%20%20print(f%22observation_horizon%3A%20%7Basinh_tf.observation_horizon%7D%22)%0A%20%20%20%20print(f%22ASinh%20range%3A%20%5B%7By_asinh%5B_asinh_col%5D.min()%3A.2f%7D%2C%20%7By_asinh%5B_asinh_col%5D.max()%3A.2f%7D%5D%22)%0A%0A%20%20%20%20%23%20Verify%20invertibility%20(X_p%3DNone%20for%20stateless)%0A%20%20%20%20y_back%20%3D%20asinh_tf.inverse_transform(y_asinh%2C%20X_p%3DNone)%0A%20%20%20%20_back_col%20%3D%20%5Bc%20for%20c%20in%20y_back.columns%20if%20c%20!%3D%20%22time%22%5D%5B0%5D%0A%20%20%20%20asinh_inv_err%20%3D%20(y%5B%22tourists%22%5D%20-%20y_back%5B_back_col%5D).abs().max()%0A%20%20%20%20print(f%22Inverse%20error%3A%20%7Basinh_inv_err%3A.10f%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%20%23%23%208.%20Comparison%20Summary%0A%0A%20%20%20%20We%20bring%20together%20the%20results%20from%20all%20transforms%20to%20compare%20their%20effectiveness%20at%20inducing%20stationarity.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(%0A%20%20%20%20ASinhTransformer%2C%0A%20%20%20%20BoxCoxTransformer%2C%0A%20%20%20%20LogTransformer%2C%0A%20%20%20%20SeasonalDifferencing%2C%0A%20%20%20%20SeasonalLogDifferencing%2C%0A%20%20%20%20y%2C%0A)%3A%0A%20%20%20%20transformers%20%3D%20%5B%0A%20%20%20%20%20%20%20%20(%22Log%22%2C%20LogTransformer(offset%3D0.0))%2C%0A%20%20%20%20%20%20%20%20(%22BoxCox(0.5)%22%2C%20BoxCoxTransformer(lmbda%3D0.5))%2C%0A%20%20%20%20%20%20%20%20(%22SeasonalDiff(12)%22%2C%20SeasonalDifferencing(seasonality%3D12))%2C%0A%20%20%20%20%20%20%20%20(%22SeasonalLogDiff(12)%22%2C%20SeasonalLogDifferencing(seasonality%3D12))%2C%0A%20%20%20%20%20%20%20%20(%22ASinh%22%2C%20ASinhTransformer())%2C%0A%20%20%20%20%5D%0A%0A%20%20%20%20print(f%22%7B'Transformer'%3A%3C22s%7D%20%20%7B'obs_horizon'%3A%3E11s%7D%20%20%7B'invertible'%3A%3E10s%7D%22)%0A%20%20%20%20print(%22-%22%20*%2048)%0A%20%20%20%20for%20_name%2C%20_tf%20in%20transformers%3A%0A%20%20%20%20%20%20%20%20_tf.fit(y)%0A%20%20%20%20%20%20%20%20_y_t%20%3D%20_tf.transform(y)%0A%20%20%20%20%20%20%20%20_has_inv%20%3D%20hasattr(_tf%2C%20%22inverse_transform%22)%0A%20%20%20%20%20%20%20%20print(f%22%7B_name%3A%3C22s%7D%20%20%7B_tf.observation_horizon%3A%3E11d%7D%20%20%7Bstr(_has_inv)%3A%3E10s%7D%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
525f002e8711ecad7c3061a1dd566095