%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%22scikit-learn%22%2C%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%20Fourier%20Seasonality%20Tuning%0A%0A%20%20%20%20%5B%60FourierSeasonalityForecaster%60%5D(%2Fpages%2Fapi%2Fgenerated%2Fyohou.stationarity.seasonality.FourierSeasonalityForecaster%2F)%20models%20seasonality%20using%20Fourier%0A%20%20%20%20terms%20(sine%2Fcosine%20pairs).%20Each%20harmonic%20adds%20a%20sine%2Fcosine%20pair%0A%20%20%20%20at%20a%20specific%20frequency%2C%20and%20the%20number%20of%20harmonics%20controls%20how%0A%20%20%20%20closely%20the%20model%20can%20approximate%20complex%20seasonal%20shapes.%0A%0A%20%20%20%20%23%23%201.%20Load%20Data%0A%0A%20%20%20%20Use%20electricity%20demand%20data%20with%20daily%20aggregation%20for%20clear%0A%20%20%20%20weekly%20seasonality.%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%20from%20copy%20import%20deepcopy%0A%0A%20%20%20%20import%20polars%20as%20pl%0A%20%20%20%20from%20sklearn.base%20import%20clone%0A%20%20%20%20from%20sklearn.linear_model%20import%20ElasticNet%2C%20Ridge%0A%0A%20%20%20%20from%20yohou.compose%20import%20DecompositionPipeline%0A%20%20%20%20from%20yohou.datasets%20import%20fetch_electricity_demand%0A%20%20%20%20from%20yohou.metrics%20import%20MeanAbsoluteError%0A%20%20%20%20from%20yohou.model_selection%20import%20(%0A%20%20%20%20%20%20%20%20ExpandingWindowSplitter%2C%0A%20%20%20%20%20%20%20%20GridSearchCV%2C%0A%20%20%20%20%20%20%20%20train_test_split%2C%0A%20%20%20%20)%0A%20%20%20%20from%20yohou.plotting%20import%20plot_forecast%2C%20plot_score_per_vintage%0A%20%20%20%20from%20yohou.point%20import%20PointReductionForecaster%0A%20%20%20%20from%20yohou.preprocessing%20import%20LagTransformer%0A%20%20%20%20from%20yohou.stationarity.seasonality%20import%20(%0A%20%20%20%20%20%20%20%20FourierSeasonalityForecaster%2C%0A%20%20%20%20%20%20%20%20PatternSeasonalityForecaster%2C%0A%20%20%20%20)%0A%0A%20%20%20%20return%20(%0A%20%20%20%20%20%20%20%20DecompositionPipeline%2C%0A%20%20%20%20%20%20%20%20ElasticNet%2C%0A%20%20%20%20%20%20%20%20ExpandingWindowSplitter%2C%0A%20%20%20%20%20%20%20%20FourierSeasonalityForecaster%2C%0A%20%20%20%20%20%20%20%20GridSearchCV%2C%0A%20%20%20%20%20%20%20%20LagTransformer%2C%0A%20%20%20%20%20%20%20%20MeanAbsoluteError%2C%0A%20%20%20%20%20%20%20%20PatternSeasonalityForecaster%2C%0A%20%20%20%20%20%20%20%20PointReductionForecaster%2C%0A%20%20%20%20%20%20%20%20Ridge%2C%0A%20%20%20%20%20%20%20%20clone%2C%0A%20%20%20%20%20%20%20%20deepcopy%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_forecast%2C%0A%20%20%20%20%20%20%20%20plot_score_per_vintage%2C%0A%20%20%20%20%20%20%20%20train_test_split%2C%0A%20%20%20%20)%0A%0A%0A%40app.cell%0Adef%20_(fetch_electricity_demand%2C%20mo%2C%20pl%2C%20train_test_split)%3A%0A%20%20%20%20_elec%20%3D%20fetch_electricity_demand().frame%0A%20%20%20%20%23%20Resample%20to%20daily%20for%20clearer%20weekly%20patterns%20(drop%20trailing%20all-null%20days)%0A%20%20%20%20elec_daily%20%3D%20(%0A%20%20%20%20%20%20%20%20_elec.group_by_dynamic(%22time%22%2C%20every%3D%221d%22).agg(pl.col(%22vic__demand%22).mean().alias(%22demand%22)).drop_nulls()%0A%20%20%20%20)%0A%20%20%20%20y_train%2C%20y_test%20%3D%20train_test_split(elec_daily%2C%20test_size%3D0.15)%0A%20%20%20%20horizon%20%3D%20len(y_test)%0A%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20f%22**Daily%20electricity%20demand**%3A%20%7Blen(elec_daily)%7D%20days%5Cn%5Cn%22%0A%20%20%20%20%20%20%20%20f%22**Train**%3A%20%7Blen(y_train)%7D%20days%2C%20**Test**%3A%20%7Blen(y_test)%7D%20days%5Cn%5Cn%22%0A%20%20%20%20%20%20%20%20f%22**Weekly%20seasonality**%20%3D%207%20days%22%0A%20%20%20%20)%0A%20%20%20%20return%20horizon%2C%20y_test%2C%20y_train%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.%20Harmonics%20Comparison%0A%0A%20%20%20%20Each%20harmonic%20captures%20a%20different%20frequency%20of%20the%20seasonal%0A%20%20%20%20cycle.%20%60harmonics%3D%5B1%5D%60%20fits%20only%20the%20fundamental%20period%2C%0A%20%20%20%20producing%20a%20single%20smooth%20sine%20wave.%20Adding%20higher%20harmonics%0A%20%20%20%20(e.g.%20%60%5B1%2C%202%5D%60%20or%20%60%5B1%2C%202%2C%2052%5D%60)%20lets%20the%20model%20represent%0A%20%20%20%20sharper%2C%20more%20complex%20patterns%20but%20risks%20overfitting%20if%20the%0A%20%20%20%20data%20doesn't%20support%20that%20complexity.%0A%0A%20%20%20%20The%20maximum%20useful%20harmonic%20is%20%60floor(seasonality%20%2F%202)%60.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(%0A%20%20%20%20FourierSeasonalityForecaster%2C%0A%20%20%20%20MeanAbsoluteError%2C%0A%20%20%20%20Ridge%2C%0A%20%20%20%20horizon%2C%0A%20%20%20%20mo%2C%0A%20%20%20%20pl%2C%0A%20%20%20%20plot_forecast%2C%0A%20%20%20%20y_test%2C%0A%20%20%20%20y_train%2C%0A)%3A%0A%20%20%20%20_scorer%20%3D%20MeanAbsoluteError()%0A%20%20%20%20_scorer.fit(y_train)%0A%20%20%20%20_rows%20%3D%20%5B%5D%0A%20%20%20%20_preds%20%3D%20%7B%7D%0A%0A%20%20%20%20for%20_nh%20in%20%5B%5B1%5D%2C%20%5B1%2C%202%5D%2C%20%5B1%2C%202%2C%2052%5D%5D%3A%0A%20%20%20%20%20%20%20%20_fc%20%3D%20FourierSeasonalityForecaster(estimator%3DRidge(alpha%3D1e-5)%2C%20seasonality%3D365%2C%20harmonics%3D_nh)%0A%20%20%20%20%20%20%20%20_fc.fit(y_train%2C%20forecasting_horizon%3Dhorizon)%0A%20%20%20%20%20%20%20%20_y_pred%20%3D%20_fc.predict(forecasting_horizon%3Dhorizon)%0A%20%20%20%20%20%20%20%20_mae%20%3D%20float(_scorer.score(y_test%2C%20_y_pred))%0A%20%20%20%20%20%20%20%20_rows.append(%7B%22Harmonics%22%3A%20str(_nh)%2C%20%22MAE%22%3A%20round(_mae%2C%202)%7D)%0A%20%20%20%20%20%20%20%20_preds%5Bf%22harmonics%3D%7B_nh%7D%22%5D%20%3D%20_y_pred%0A%0A%20%20%20%20mo.vstack(%5B%0A%20%20%20%20%20%20%20%20mo.ui.table(pl.DataFrame(_rows))%2C%0A%20%20%20%20%20%20%20%20plot_forecast(%0A%20%20%20%20%20%20%20%20%20%20%20%20y_test%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20_preds%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20y_train%3Dy_train%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20n_history%3D1000%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20title%3D%22Harmonics%20Comparison%22%2C%0A%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20%5D)%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.%20Fourier%20vs%20Pattern%20Seasonality%0A%0A%20%20%20%20%5B%60FourierSeasonalityForecaster%60%5D(%2Fpages%2Fapi%2Fgenerated%2Fyohou.stationarity.seasonality.FourierSeasonalityForecaster%2F)%20represents%20seasonality%20as%20a%0A%20%20%20%20weighted%20sum%20of%20sine%2Fcosine%20terms%20creating%20a%20smooth%2C%20parametric%20model.%0A%20%20%20%20%5B%60PatternSeasonalityForecaster%60%5D(%2Fpages%2Fapi%2Fgenerated%2Fyohou.stationarity.seasonality.PatternSeasonalityForecaster%2F)%20directly%20averages%20(or%20mediates)%0A%20%20%20%20historical%20values%20at%20each%20seasonal%20position%20as%20a%20non-parametric%0A%20%20%20%20approach%20that%20can%20capture%20irregular%20shapes.%0A%0A%20%20%20%20When%20the%20true%20seasonal%20shape%20is%20close%20to%20sinusoidal%2C%20Fourier%0A%20%20%20%20generalises%20better.%20When%20it%20has%20sharp%20spikes%20or%20asymmetric%0A%20%20%20%20patterns%2C%20Pattern%20may%20win.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(%0A%20%20%20%20FourierSeasonalityForecaster%2C%0A%20%20%20%20MeanAbsoluteError%2C%0A%20%20%20%20PatternSeasonalityForecaster%2C%0A%20%20%20%20Ridge%2C%0A%20%20%20%20horizon%2C%0A%20%20%20%20mo%2C%0A%20%20%20%20plot_forecast%2C%0A%20%20%20%20y_test%2C%0A%20%20%20%20y_train%2C%0A)%3A%0A%20%20%20%20_fc_fourier%20%3D%20FourierSeasonalityForecaster(estimator%3DRidge(alpha%3D1e-5)%2C%20seasonality%3D365%2C%20harmonics%3D%5B1%2C%202%2C%2052%5D)%0A%20%20%20%20_fc_fourier.fit(y_train%2C%20forecasting_horizon%3Dhorizon)%0A%20%20%20%20_y_pred_fourier%20%3D%20_fc_fourier.predict(forecasting_horizon%3Dhorizon)%0A%0A%20%20%20%20_fc_pattern%20%3D%20PatternSeasonalityForecaster(seasonality%3D365%2C%20method%3D%22average%22)%0A%20%20%20%20_fc_pattern.fit(y_train%2C%20forecasting_horizon%3Dhorizon)%0A%20%20%20%20_y_pred_pattern%20%3D%20_fc_pattern.predict(forecasting_horizon%3Dhorizon)%0A%0A%20%20%20%20_scorer%20%3D%20MeanAbsoluteError()%0A%20%20%20%20_scorer.fit(y_train)%0A%20%20%20%20_mae_f%20%3D%20float(_scorer.score(y_test%2C%20_y_pred_fourier))%0A%20%20%20%20_mae_p%20%3D%20float(_scorer.score(y_test%2C%20_y_pred_pattern))%0A%0A%20%20%20%20mo.vstack(%5B%0A%20%20%20%20%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20%20%20%20%20f%22**Fourier%20(3%20harmonics)%20MAE**%3A%20%7B_mae_f%3A.2f%7D%5Cn%5Cn%22%0A%20%20%20%20%20%20%20%20%20%20%20%20f%22**Pattern%20(average)%20MAE**%3A%20%7B_mae_p%3A.2f%7D%5Cn%5Cn%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%22Fourier%20produces%20smoother%20seasonal%20patterns%3B%20Pattern%20is%20more%20flexible.%22%0A%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20%20%20%20%20plot_forecast(%0A%20%20%20%20%20%20%20%20%20%20%20%20y_test%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%7B%22Fourier%20(3%20harmonics)%22%3A%20_y_pred_fourier%2C%20%22Pattern%20(average)%22%3A%20_y_pred_pattern%7D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20y_train%3Dy_train%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20n_history%3D1000%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20title%3D%22Fourier%20vs%20Pattern%20Seasonality%22%2C%0A%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20%5D)%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.%20Different%20Estimators%0A%0A%20%20%20%20Internally%2C%20%5B%60FourierSeasonalityForecaster%60%5D(%2Fpages%2Fapi%2Fgenerated%2Fyohou.stationarity.seasonality.FourierSeasonalityForecaster%2F)%20builds%20a%20design%20matrix%0A%20%20%20%20of%20sine%2Fcosine%20columns%20and%20fits%20a%20linear%20regression.%20The%20choice%0A%20%20%20%20of%20estimator%20controls%20how%20the%20Fourier%20coefficients%20are%0A%20%20%20%20regularised%3A%20ElasticNet%20applies%20both%20L1%20and%20L2%20penalties%0A%20%20%20%20(potentially%20zeroing%20out%20harmonics)%2C%20while%20Ridge%20applies%20only%20L2%0A%20%20%20%20(shrinking%20coefficients%20towards%20zero%20without%20eliminating%20them).%0A%0A%20%20%20%20Higher%20%60alpha%60%20values%20increase%20shrinkage%20sharply.%20Try%20different%0A%20%20%20%20orders%20of%20magnitude%20to%20see%20the%20effect%20on%20forecast%20accuracy.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(%0A%20%20%20%20ElasticNet%2C%0A%20%20%20%20FourierSeasonalityForecaster%2C%0A%20%20%20%20MeanAbsoluteError%2C%0A%20%20%20%20Ridge%2C%0A%20%20%20%20horizon%2C%0A%20%20%20%20mo%2C%0A%20%20%20%20pl%2C%0A%20%20%20%20plot_forecast%2C%0A%20%20%20%20y_test%2C%0A%20%20%20%20y_train%2C%0A)%3A%0A%20%20%20%20_scorer%20%3D%20MeanAbsoluteError()%0A%20%20%20%20_scorer.fit(y_train)%0A%20%20%20%20_rows%20%3D%20%5B%5D%0A%20%20%20%20_preds%20%3D%20%7B%7D%0A%20%20%20%20for%20_name%2C%20_est%20in%20%5B%0A%20%20%20%20%20%20%20%20(%22ElasticNet%20(default)%22%2C%20ElasticNet())%2C%0A%20%20%20%20%20%20%20%20(%22Ridge(alpha%3D0.1)%22%2C%20Ridge(alpha%3D0.1))%2C%0A%20%20%20%20%20%20%20%20(%22Ridge(alpha%3D10)%22%2C%20Ridge(alpha%3D10.0))%2C%0A%20%20%20%20%5D%3A%0A%20%20%20%20%20%20%20%20_fc%20%3D%20FourierSeasonalityForecaster(%0A%20%20%20%20%20%20%20%20%20%20%20%20seasonality%3D365%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20harmonics%3D%5B1%2C%202%2C%2052%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20estimator%3D_est%2C%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20_fc.fit(y_train%2C%20forecasting_horizon%3Dhorizon)%0A%20%20%20%20%20%20%20%20_y_pred%20%3D%20_fc.predict(forecasting_horizon%3Dhorizon)%0A%20%20%20%20%20%20%20%20_mae%20%3D%20float(_scorer.score(y_test%2C%20_y_pred))%0A%20%20%20%20%20%20%20%20_rows.append(%7B%22Estimator%22%3A%20_name%2C%20%22MAE%22%3A%20round(_mae%2C%202)%7D)%0A%20%20%20%20%20%20%20%20_preds%5B_name%5D%20%3D%20_y_pred%0A%0A%20%20%20%20mo.vstack(%5B%0A%20%20%20%20%20%20%20%20mo.ui.table(pl.DataFrame(_rows))%2C%0A%20%20%20%20%20%20%20%20plot_forecast(%0A%20%20%20%20%20%20%20%20%20%20%20%20y_test%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20_preds%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20y_train%3Dy_train%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20n_history%3D60%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20title%3D%22Estimator%20Comparison%22%2C%0A%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20%5D)%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.%20Fourier%20in%20a%20DecompositionPipeline%0A%0A%20%20%20%20A%20%5B%60DecompositionPipeline%60%5D(%2Fpages%2Fapi%2Fgenerated%2Fyohou.compose.decomposition_pipeline.DecompositionPipeline%2F)%20lets%20you%20stack%20forecasters%0A%20%20%20%20sequentially%3A%20each%20one%20fits%20and%20removes%20its%20component%2C%20passing%0A%20%20%20%20the%20residual%20to%20the%20next.%20Here%20we%20use%20Fourier%20seasonality%20to%0A%20%20%20%20capture%20the%20yearly%20pattern%2C%20then%20a%20%5B%60PointReductionForecaster%60%5D(%2Fpages%2Fapi%2Fgenerated%2Fyohou.point.reduction.PointReductionForecaster%2F)%0A%20%20%20%20with%20lag%20features%20to%20model%20whatever%20structure%20remains%20in%20the%0A%20%20%20%20residual.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(%0A%20%20%20%20DecompositionPipeline%2C%0A%20%20%20%20FourierSeasonalityForecaster%2C%0A%20%20%20%20LagTransformer%2C%0A%20%20%20%20PointReductionForecaster%2C%0A%20%20%20%20Ridge%2C%0A%20%20%20%20horizon%2C%0A%20%20%20%20plot_forecast%2C%0A%20%20%20%20y_test%2C%0A%20%20%20%20y_train%2C%0A)%3A%0A%20%20%20%20fc_decomp_fourier%20%3D%20DecompositionPipeline(%0A%20%20%20%20%20%20%20%20forecasters%3D%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20(%22season%22%2C%20FourierSeasonalityForecaster(estimator%3DRidge(1e-5)%2C%20seasonality%3D365%2C%20harmonics%3D%5B1%2C%202%2C%2052%5D))%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22residual%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20PointReductionForecaster(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20estimator%3DRidge(alpha%3D1.0)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20feature_transformer%3DLagTransformer(lag%3D%5B1%2C%207%2C%20365%5D)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20%20%20%20%20%5D%2C%0A%20%20%20%20)%0A%20%20%20%20fc_decomp_fourier.fit(y_train%2C%20forecasting_horizon%3Dhorizon)%0A%20%20%20%20_y_pred_decomp%20%3D%20fc_decomp_fourier.predict(forecasting_horizon%3Dhorizon)%0A%0A%20%20%20%20plot_forecast(%0A%20%20%20%20%20%20%20%20y_test%2C%0A%20%20%20%20%20%20%20%20_y_pred_decomp%2C%0A%20%20%20%20%20%20%20%20y_train%3Dy_train%2C%0A%20%20%20%20%20%20%20%20n_history%3D1000%2C%0A%20%20%20%20%20%20%20%20title%3D%22Trend%20%2B%20Fourier%20Season%20%2B%20Residual%22%2C%0A%20%20%20%20)%0A%20%20%20%20return%20(fc_decomp_fourier%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.%20Grid%20Search%3A%20Tuning%20Harmonics%20%26%20Ridge%20Alphas%0A%0A%20%20%20%20Rather%20than%20picking%20harmonics%20and%20Ridge%20alphas%20by%20hand%2C%20we%20can%0A%20%20%20%20search%20over%20them%20jointly%20with%20%5B%60GridSearchCV%60%5D(%2Fpages%2Fapi%2Fgenerated%2Fyohou.model_selection.search.GridSearchCV%2F).%20The%20search%0A%20%20%20%20evaluates%20every%20combination%20using%20time-series%20cross-validation%0A%20%20%20%20(%5B%60ExpandingWindowSplitter%60%5D(%2Fpages%2Fapi%2Fgenerated%2Fyohou.model_selection.split.ExpandingWindowSplitter%2F))%20and%20selects%20the%20configuration%20with%0A%20%20%20%20the%20lowest%20MAE.%0A%0A%20%20%20%20The%20grid%20covers%20the%20Fourier%20harmonic%20sets%2C%20the%20Fourier%0A%20%20%20%20estimator's%20regularisation%2C%20and%20the%20residual%20estimator's%0A%20%20%20%20regularisation%20as%20those%20three%20axes%20interact%20with%20each%20other.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(%0A%20%20%20%20ExpandingWindowSplitter%2C%0A%20%20%20%20GridSearchCV%2C%0A%20%20%20%20MeanAbsoluteError%2C%0A%20%20%20%20clone%2C%0A%20%20%20%20fc_decomp_fourier%2C%0A%20%20%20%20horizon%2C%0A%20%20%20%20mo%2C%0A%20%20%20%20plot_forecast%2C%0A%20%20%20%20y_test%2C%0A%20%20%20%20y_train%2C%0A)%3A%0A%20%20%20%20_param_grid%20%3D%20%7B%0A%20%20%20%20%20%20%20%20%22season__harmonics%22%3A%20%5B%5B1%5D%2C%20%5B1%2C%202%5D%2C%20%5B1%2C%202%2C%2052%5D%2C%20%5B1%2C%202%2C%2052%2C%20104%5D%5D%2C%0A%20%20%20%20%20%20%20%20%22season__estimator__alpha%22%3A%20%5B1e-5%2C%201e-3%2C%201.0%5D%2C%0A%20%20%20%20%20%20%20%20%22residual__estimator__alpha%22%3A%20%5B0.1%2C%201.0%2C%2010.0%5D%2C%0A%20%20%20%20%7D%0A%0A%20%20%20%20_search%20%3D%20GridSearchCV(%0A%20%20%20%20%20%20%20%20forecaster%3Dclone(fc_decomp_fourier)%2C%0A%20%20%20%20%20%20%20%20param_grid%3D_param_grid%2C%0A%20%20%20%20%20%20%20%20scoring%3DMeanAbsoluteError()%2C%0A%20%20%20%20%20%20%20%20cv%3DExpandingWindowSplitter(n_splits%3D2%2C%20test_size%3Dhorizon)%2C%0A%20%20%20%20)%0A%20%20%20%20_search.fit(y_train%2C%20forecasting_horizon%3Dhorizon)%0A%0A%20%20%20%20_y_pred_best%20%3D%20_search.predict(forecasting_horizon%3Dhorizon)%0A%0A%20%20%20%20mo.vstack(%5B%0A%20%20%20%20%20%20%20%20mo.md(f%22**Best%20params**%3A%20%60%7B_search.best_params_%7D%60%5Cn%5Cn**Best%20CV%20score**%3A%20%7B_search.best_score_%3A.2f%7D%22)%2C%0A%20%20%20%20%20%20%20%20plot_forecast(%0A%20%20%20%20%20%20%20%20%20%20%20%20y_test%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20_y_pred_best%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20y_train%3Dy_train%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20n_history%3D60%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20title%3D%22Grid%20Search%20Best%20Pipeline%22%2C%0A%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20%5D)%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%20Multi-vintage%20Scoring%0A%0A%20%20%20%20The%20%60observe_predict%60%20method%20with%20%60stride%3D1%60%20produces%20one%20forecast%20per%0A%20%20%20%20observation%20point%2C%20creating%20multiple%20*vintages*.%20Each%20vintage%20represents%0A%20%20%20%20a%20different%20forecast%20origin%2C%20so%20you%20can%20analyse%20how%20accuracy%20evolves%20as%0A%20%20%20%20the%20model%20absorbs%20more%20data.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(FourierSeasonalityForecaster%2C%20Ridge%2C%20deepcopy%2C%20horizon%2C%20y_test%2C%20y_train)%3A%0A%20%20%20%20_fc_v%20%3D%20FourierSeasonalityForecaster(estimator%3DRidge(alpha%3D1e-5)%2C%20seasonality%3D365%2C%20harmonics%3D%5B1%2C%202%2C%2052%5D)%0A%20%20%20%20_fc_v.fit(y_train%2C%20forecasting_horizon%3Dhorizon)%0A%20%20%20%20_vintage_model%20%3D%20deepcopy(_fc_v)%0A%20%20%20%20y_pred_vintages%20%3D%20_vintage_model.observe_predict(%0A%20%20%20%20%20%20%20%20y%3Dy_test%2C%0A%20%20%20%20%20%20%20%20stride%3D1%2C%0A%20%20%20%20%20%20%20%20forecasting_horizon%3Dhorizon%2C%0A%20%20%20%20)%0A%20%20%20%20print(f%22Vintages%3A%20%7By_pred_vintages%5B'vintage_time'%5D.n_unique()%7D%22)%0A%20%20%20%20y_pred_vintages.head(10)%0A%20%20%20%20return%20(y_pred_vintages%2C)%0A%0A%0A%40app.cell%0Adef%20_(MeanAbsoluteError%2C%20y_train)%3A%0A%20%20%20%20vintage_scorer%20%3D%20MeanAbsoluteError()%0A%20%20%20%20vintage_scorer.fit(y_train)%0A%20%20%20%20return%20(vintage_scorer%2C)%0A%0A%0A%40app.cell%0Adef%20_(plot_score_per_vintage%2C%20vintage_scorer%2C%20y_pred_vintages%2C%20y_test)%3A%0A%20%20%20%20plot_score_per_vintage(%0A%20%20%20%20%20%20%20%20vintage_scorer%2C%0A%20%20%20%20%20%20%20%20y_test%2C%0A%20%20%20%20%20%20%20%20y_pred_vintages%2C%0A%20%20%20%20%20%20%20%20title%3D%22MAE%20per%20Forecast%20Vintage%22%2C%0A%20%20%20%20%20%20%20%20y_label%3D%22MAE%22%2C%0A%20%20%20%20%20%20%20%20height%3D380%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**Decomposition%20pipelines**%3A%20See%20%5B%60examples%2Fcompose%2Fdecomposition_variations.py%60%5D(%2Fexamples%2Fforecasting-models%2Fdecomposition_variations%2F)%0A%20%20%20%20-%20**Stationarity%20transforms**%3A%20See%20%5B%60examples%2Fstationarity%2Fstationarity_transforms.py%60%5D(%2Fexamples%2Fdata-features%2Fstationarity_transforms%2F)%0A%20%20%20%20-%20**Decomposition**%3A%20See%20%5B%60examples%2Fstationarity%2Fdecomposition.py%60%5D(%2Fexamples%2Fgetting-started%2Fdecomposition%2F)%0A%20%20%20%20-%20**Hyperparameter%20search**%3A%20See%20%5B%60examples%2Fmodel_selection%2Fhyperparameter_search.py%60%5D(%2Fexamples%2Fevaluation-search%2Fhyperparameter_search%2F)%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
433ebf2ec0c5688072187bc3bac259c6