%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%20Yohou%20Quickstart%3A%20Time%20Series%20Forecasting%20with%20a%20Scikit-Learn%20API%0A%0A%20%20%20%20Welcome%20to%20**Yohou**%2C%20a%20scikit-learn-compatible%20time%20series%20forecasting%20framework%20built%20on%20**Polars**.%0A%0A%20%20%20%20%23%23%20Prerequisites%0A%0A%20%20%20%20Basic%20Python%20and%20familiarity%20with%20sklearn's%20%60fit%60%20%2F%20%60predict%60%20API.%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%20copy%0A%0A%20%20%20%20import%20plotly.graph_objects%20as%20go%0A%20%20%20%20import%20polars%20as%20pl%0A%20%20%20%20from%20scipy.stats%20import%20randint%2C%20uniform%0A%20%20%20%20from%20sklearn.base%20import%20clone%0A%20%20%20%20from%20sklearn.linear_model%20import%20Ridge%0A%0A%20%20%20%20from%20yohou.compose%20import%20DecompositionPipeline%2C%20FeaturePipeline%2C%20LocalPanelForecaster%0A%20%20%20%20from%20yohou.datasets%20import%20fetch_dominick%2C%20fetch_tourism_monthly%0A%20%20%20%20from%20yohou.interval%20import%20SplitConformalForecaster%0A%20%20%20%20from%20yohou.metrics%20import%20MeanAbsoluteError%2C%20MeanSquaredError%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%20RandomizedSearchCV%2C%0A%20%20%20%20%20%20%20%20SlidingWindowSplitter%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%20(%0A%20%20%20%20%20%20%20%20plot_calibration%2C%0A%20%20%20%20%20%20%20%20plot_cv_results_scatter%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%20plot_score_time_series%2C%0A%20%20%20%20%20%20%20%20plot_splits%2C%0A%20%20%20%20%20%20%20%20plot_time_series%2C%0A%20%20%20%20%20%20%20%20plot_time_weight%2C%0A%20%20%20%20)%0A%20%20%20%20from%20yohou.point%20import%20PointReductionForecaster%2C%20SeasonalNaive%0A%20%20%20%20from%20yohou.preprocessing%20import%20LagTransformer%0A%20%20%20%20from%20yohou.stationarity%20import%20(%0A%20%20%20%20%20%20%20%20FourierSeasonalityForecaster%2C%0A%20%20%20%20%20%20%20%20LogTransformer%2C%0A%20%20%20%20%20%20%20%20PolynomialTrendForecaster%2C%0A%20%20%20%20%20%20%20%20SeasonalDifferencing%2C%0A%20%20%20%20)%0A%20%20%20%20from%20yohou.utils.panel%20import%20inspect_panel%0A%20%20%20%20from%20yohou.utils.weighting%20import%20(%0A%20%20%20%20%20%20%20%20compose_weights%2C%0A%20%20%20%20%20%20%20%20exponential_decay_weight%2C%0A%20%20%20%20%20%20%20%20linear_decay_weight%2C%0A%20%20%20%20%20%20%20%20seasonal_emphasis_weight%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%20ExpandingWindowSplitter%2C%0A%20%20%20%20%20%20%20%20FeaturePipeline%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%20LocalPanelForecaster%2C%0A%20%20%20%20%20%20%20%20LogTransformer%2C%0A%20%20%20%20%20%20%20%20MeanAbsoluteError%2C%0A%20%20%20%20%20%20%20%20MeanSquaredError%2C%0A%20%20%20%20%20%20%20%20PointReductionForecaster%2C%0A%20%20%20%20%20%20%20%20PolynomialTrendForecaster%2C%0A%20%20%20%20%20%20%20%20RandomizedSearchCV%2C%0A%20%20%20%20%20%20%20%20Ridge%2C%0A%20%20%20%20%20%20%20%20SeasonalDifferencing%2C%0A%20%20%20%20%20%20%20%20SeasonalNaive%2C%0A%20%20%20%20%20%20%20%20SlidingWindowSplitter%2C%0A%20%20%20%20%20%20%20%20SplitConformalForecaster%2C%0A%20%20%20%20%20%20%20%20clone%2C%0A%20%20%20%20%20%20%20%20compose_weights%2C%0A%20%20%20%20%20%20%20%20copy%2C%0A%20%20%20%20%20%20%20%20exponential_decay_weight%2C%0A%20%20%20%20%20%20%20%20fetch_dominick%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%20inspect_panel%2C%0A%20%20%20%20%20%20%20%20linear_decay_weight%2C%0A%20%20%20%20%20%20%20%20pl%2C%0A%20%20%20%20%20%20%20%20plot_calibration%2C%0A%20%20%20%20%20%20%20%20plot_cv_results_scatter%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%20plot_score_time_series%2C%0A%20%20%20%20%20%20%20%20plot_splits%2C%0A%20%20%20%20%20%20%20%20plot_time_series%2C%0A%20%20%20%20%20%20%20%20plot_time_weight%2C%0A%20%20%20%20%20%20%20%20randint%2C%0A%20%20%20%20%20%20%20%20seasonal_emphasis_weight%2C%0A%20%20%20%20%20%20%20%20train_test_split%2C%0A%20%20%20%20%20%20%20%20uniform%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.%20Data%20%26%20Visualisation%0A%0A%20%20%20%20We%20use%20the%20**Tourism%20Monthly**%20dataset%20from%20the%20Monash%20forecasting%20archive%2C%0A%20%20%20%20a%20panel%20of%20366%20monthly%20tourism%20series.%20For%20this%20quickstart%20we%20pick%20a%20single%0A%20%20%20%20series%20(%60T1__tourists%60)%20and%20rename%20it%20to%20%60%22tourists%22%60%20so%20the%20downstream%0A%20%20%20%20code%20stays%20readable.%0A%0A%20%20%20%20Yohou%20requires%20a%20polars%20DataFrame%20with%20a%20**%60%22time%22%60%20column**%20(datetime%20type).%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(fetch_tourism_monthly)%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%20print(f%22Shape%3A%20%7By.shape%7D%20%20%7C%20%20Range%3A%20%7By%5B'time'%5D.min()%7D%20%E2%86%92%20%7By%5B'time'%5D.max()%7D%22)%0A%20%20%20%20y.head()%0A%20%20%20%20return%20(y%2C)%0A%0A%0A%40app.cell%0Adef%20_(plot_time_series%2C%20y)%3A%0A%20%20%20%20plot_time_series(%0A%20%20%20%20%20%20%20%20y%2C%0A%20%20%20%20%20%20%20%20columns%3D%22tourists%22%2C%0A%20%20%20%20%20%20%20%20title%3D%22Monthly%20Tourism%20(1979%20-%202007)%22%2C%0A%20%20%20%20%20%20%20%20y_label%3D%22Monthly%20tourists%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**Observations**%3A%20clear%20yearly%20seasonality%2C%20a%20long-term%20upward%20trend%2C%0A%20%20%20%20and%20variance%20that%20grows%20with%20level%2C%20which%20is%20typical%20of%20many%20tourism%20time%20series.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(train_test_split%2C%20y)%3A%0A%20%20%20%20y_train%2C%20y_test%20%3D%20train_test_split(y%2C%20test_size%3D24)%0A%20%20%20%20forecasting_horizon%20%3D%20len(y_test)%0A%0A%20%20%20%20print(f%22Train%3A%20%7Blen(y_train)%7D%20rows%20%20%7C%20%20Test%3A%20%7Blen(y_test)%7D%20rows%20%20%7C%20%20Horizon%3A%20%7Bforecasting_horizon%7D%22)%0A%20%20%20%20return%20forecasting_horizon%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.%20Baseline%3A%20Seasonal%20Naive%0A%0A%20%20%20%20The%20simplest%20seasonal%20model%3A%20repeat%20the%20values%20from%20one%20year%20ago%0A%20%20%20%20(%60seasonality%3D12%60%20for%20monthly%20data).%0A%0A%20%20%20%20Every%20more%20complex%20model%20should%20**beat%20this%20baseline**.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(MeanAbsoluteError%2C%20SeasonalNaive%2C%20forecasting_horizon%2C%20y_test%2C%20y_train)%3A%0A%20%20%20%20baseline%20%3D%20SeasonalNaive(seasonality%3D12)%0A%20%20%20%20baseline.fit(y_train%2C%20forecasting_horizon%3Dforecasting_horizon)%0A%20%20%20%20y_pred_baseline%20%3D%20baseline.predict(forecasting_horizon%3Dforecasting_horizon)%0A%0A%20%20%20%20scorer%20%3D%20MeanAbsoluteError()%0A%20%20%20%20scorer.fit(y_train)%0A%20%20%20%20mae_baseline%20%3D%20scorer.score(y_test%2C%20y_pred_baseline)%0A%20%20%20%20print(f%22Baseline%20MAE%3A%20%7Bmae_baseline%3A.2f%7D%22)%0A%20%20%20%20return%20mae_baseline%2C%20scorer%2C%20y_pred_baseline%0A%0A%0A%40app.cell%0Adef%20_(plot_forecast%2C%20y_pred_baseline%2C%20y_test%2C%20y_train)%3A%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%20y_pred_baseline%2C%0A%20%20%20%20%20%20%20%20y_train%3Dy_train%2C%0A%20%20%20%20%20%20%20%20title%3D%22Baseline%3A%20Seasonal%20Naive%22%2C%0A%20%20%20%20%20%20%20%20y_label%3D%22Monthly%20tourists%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%203.%20Preprocessing%20Pipelines%0A%0A%20%20%20%20Reduction%20forecasters%20convert%20time-series%20forecasting%20to%20supervised%20learning.%0A%20%20%20%20A%20**FeaturePipeline**%20chains%20transforms%3A%0A%0A%20%20%20%201.%20%5B%60LogTransformer%60%5D(%2Fpages%2Fapi%2Fgenerated%2Fyohou.stationarity.transformers.LogTransformer%2F)%3A%20stabilises%20multiplicative%20variance%0A%20%20%20%202.%20%60SeasonalDifferencing(12)%60%3A%20removes%20yearly%20seasonality%20and%20trend%0A%20%20%20%203.%20%5B%60LagTransformer%60%5D(%2Fpages%2Fapi%2Fgenerated%2Fyohou.preprocessing.window.LagTransformer%2F)%3A%20creates%20autoregressive%20features%20from%20past%20values%0A%0A%20%20%20%20When%20used%20as%20a%20%60target_transformer%60%2C%20all%20transformations%20are%20automatically%20inverted%20at%20prediction%20time.%20In%20the%20case%20of%20a%20%5B%60FeaturePipeline%60%5D(%2Fpages%2Fapi%2Fgenerated%2Fyohou.compose.feature_pipeline.FeaturePipeline%2F)%2C%20this%20means%20all%20transforms%20it%20includes%20have%20to%20be%20invertible.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20max_lag_slider%20%3D%20mo.ui.slider(start%3D1%2C%20stop%3D12%2C%20value%3D3%2C%20label%3D%22Max%20lag%22%2C%20show_value%3DTrue)%0A%20%20%20%20max_lag_slider%0A%20%20%20%20return%20(max_lag_slider%2C)%0A%0A%0A%40app.cell%0Adef%20_(%0A%20%20%20%20FeaturePipeline%2C%0A%20%20%20%20LagTransformer%2C%0A%20%20%20%20LogTransformer%2C%0A%20%20%20%20PointReductionForecaster%2C%0A%20%20%20%20Ridge%2C%0A%20%20%20%20SeasonalDifferencing%2C%0A%20%20%20%20forecasting_horizon%2C%0A%20%20%20%20mae_baseline%2C%0A%20%20%20%20max_lag_slider%2C%0A%20%20%20%20scorer%2C%0A%20%20%20%20y_test%2C%0A%20%20%20%20y_train%2C%0A)%3A%0A%20%20%20%20pipeline_target%20%3D%20FeaturePipeline(%5B%0A%20%20%20%20%20%20%20%20(%22log%22%2C%20LogTransformer(offset%3D1.0))%2C%0A%20%20%20%20%20%20%20%20(%22diff%22%2C%20SeasonalDifferencing(seasonality%3D12))%2C%0A%20%20%20%20%5D)%0A%20%20%20%20pipeline_feature%20%3D%20FeaturePipeline(%5B%0A%20%20%20%20%20%20%20%20(%22lag%22%2C%20LagTransformer(lag%3Dlist(range(1%2C%20max_lag_slider.value%20%2B%201))))%2C%0A%20%20%20%20%5D)%0A%0A%20%20%20%20reduction%20%3D%20PointReductionForecaster(%0A%20%20%20%20%20%20%20%20estimator%3DRidge(alpha%3D10)%2C%0A%20%20%20%20%20%20%20%20target_transformer%3Dpipeline_target%2C%0A%20%20%20%20%20%20%20%20feature_transformer%3Dpipeline_feature%2C%0A%20%20%20%20)%0A%20%20%20%20reduction.fit(y_train%2C%20forecasting_horizon%3Dforecasting_horizon)%0A%20%20%20%20y_pred_reduction%20%3D%20reduction.predict(forecasting_horizon%3Dforecasting_horizon)%0A%0A%20%20%20%20mae_reduction%20%3D%20scorer.score(y_test%2C%20y_pred_reduction)%0A%20%20%20%20improvement%20%3D%20(mae_baseline%20-%20mae_reduction)%20%2F%20mae_baseline%20*%20100%0A%20%20%20%20print(f%22Reduction%20MAE%3A%20%7Bmae_reduction%3A.2f%7D%20%20(%7Bimprovement%3A%2B.1f%7D%25%20vs%20baseline)%22)%0A%20%20%20%20return%20reduction%2C%20y_pred_reduction%0A%0A%0A%40app.cell%0Adef%20_(plot_forecast%2C%20y_pred_baseline%2C%20y_pred_reduction%2C%20y_test%2C%20y_train)%3A%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%7B%22Baseline%22%3A%20y_pred_baseline%2C%20%22Reduction%22%3A%20y_pred_reduction%7D%2C%0A%20%20%20%20%20%20%20%20y_train%3Dy_train%2C%0A%20%20%20%20%20%20%20%20title%3D%22Reduction%20Forecaster%20vs%20Baseline%22%2C%0A%20%20%20%20%20%20%20%20y_label%3D%22Monthly%20tourists%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%204.%20Decomposition%20Pipeline%0A%0A%20%20%20%20A%20**DecompositionPipeline**%20is%20a%20meta-forecaster%20made%20of%20several%20forecasters%20that%20sequentially%20fit%20on%20the%20residual%20of%20the%20previous%20forecaster.%20I%20can%20be%20used%20to%20explicitly%20models%20structural%20components%3A%0A%0A%20%20%20%20-%20**Trend**%20via%20%5B%60PolynomialTrendForecaster%60%5D(%2Fpages%2Fapi%2Fgenerated%2Fyohou.stationarity.trend.PolynomialTrendForecaster%2F)%0A%20%20%20%20-%20**Seasonality**%20via%20%5B%60FourierSeasonalityForecaster%60%5D(%2Fpages%2Fapi%2Fgenerated%2Fyohou.stationarity.seasonality.FourierSeasonalityForecaster%2F)%0A%20%20%20%20-%20**Residual**%20via%20any%20forecaster%20(here%20%5B%60PointReductionForecaster%60%5D(%2Fpages%2Fapi%2Fgenerated%2Fyohou.point.reduction.PointReductionForecaster%2F))%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%20LogTransformer%2C%0A%20%20%20%20PointReductionForecaster%2C%0A%20%20%20%20PolynomialTrendForecaster%2C%0A%20%20%20%20Ridge%2C%0A%20%20%20%20forecasting_horizon%2C%0A%20%20%20%20scorer%2C%0A%20%20%20%20y_test%2C%0A%20%20%20%20y_train%2C%0A)%3A%0A%20%20%20%20decomp%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(%22trend%22%2C%20PolynomialTrendForecaster(degree%3D1))%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20(%22season%22%2C%20FourierSeasonalityForecaster(seasonality%3D12%2C%20harmonics%3D%5B1%2C%202%2C%203%2C%204%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%202%2C%203%2C%2012%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%20%20%20%20target_transformer%3DLogTransformer(offset%3D1.0)%2C%0A%20%20%20%20)%0A%20%20%20%20decomp.fit(y_train%2C%20forecasting_horizon%3Dforecasting_horizon)%0A%20%20%20%20y_pred_decomp%20%3D%20decomp.predict(forecasting_horizon%3Dforecasting_horizon)%0A%20%20%20%20mae_decomp%20%3D%20scorer.score(y_test%2C%20y_pred_decomp)%0A%20%20%20%20print(f%22Decomposition%20MAE%3A%20%7Bmae_decomp%3A.2f%7D%22)%0A%20%20%20%20return%20(y_pred_decomp%2C)%0A%0A%0A%40app.cell%0Adef%20_(%0A%20%20%20%20plot_forecast%2C%0A%20%20%20%20y_pred_baseline%2C%0A%20%20%20%20y_pred_decomp%2C%0A%20%20%20%20y_pred_reduction%2C%0A%20%20%20%20y_test%2C%0A%20%20%20%20y_train%2C%0A)%3A%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%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%22Baseline%22%3A%20y_pred_baseline%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22Reduction%22%3A%20y_pred_reduction%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22Decomposition%22%3A%20y_pred_decomp%2C%0A%20%20%20%20%20%20%20%20%7D%2C%0A%20%20%20%20%20%20%20%20y_train%3Dy_train%2C%0A%20%20%20%20%20%20%20%20title%3D%22Model%20Comparison%20(so%20far)%22%2C%0A%20%20%20%20%20%20%20%20y_label%3D%22Monthly%20tourists%22%2C%0A%20%20%20%20%20%20%20%20height%3D400%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%205.%20Incremental%20Learning%3A%20%60observe%60%20%2F%20%60predict%60%0A%0A%20%20%20%20In%20production%20you%20rarely%20retrain%20from%20scratch.%20Yohou's%20**streaming%20workflow**%3A%0A%0A%20%20%20%201.%20%60fit(y_train)%60%3A%20initial%20training%0A%20%20%20%202.%20%60observe(y_new)%60%3A%20feed%20new%20observations%20(updates%20memory%2C%20NOT%20model%20weights)%0A%20%20%20%203.%20%60predict(horizon)%60%3A%20forecast%20from%20latest%20observation%0A%20%20%20%204.%20%60observe_predict(y_new)%60%3A%20atomic%20shortcut%20for%20observe%20%2B%20predict%0A%0A%20%20%20%20Here%20we%20compare%20a%20**static**%20prediction%20(no%20new%20observations)%20against%20one%20that%0A%20%20%20%20first%20calls%20%60observe_predict%60%20to%20absorb%20half%20the%20test%20period%20before%20predicting%0A%20%20%20%20the%20remaining%20horizon%3A%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(copy%2C%20forecasting_horizon%2C%20plot_forecast%2C%20reduction%2C%20y_test%2C%20y_train)%3A%0A%20%20%20%20_pivot%20%3D%20forecasting_horizon%20%2F%2F%202%0A%0A%20%20%20%20%23%20Static%3A%20predict%20straight%20from%20the%20training%20end%2C%20no%20new%20data%0A%20%20%20%20_static%20%3D%20copy.deepcopy(reduction)%0A%20%20%20%20_y_pred_static%20%3D%20_static.predict(forecasting_horizon%3Dforecasting_horizon)%0A%0A%20%20%20%20%23%20Incremental%3A%20observe%20the%20first%20half%20of%20the%20test%20window%2C%20then%20predict%20the%20rest%0A%20%20%20%20_incr%20%3D%20copy.deepcopy(reduction)%0A%20%20%20%20_y_pred_incr%20%3D%20_incr.observe_predict(%0A%20%20%20%20%20%20%20%20y%3Dy_test%5B%3A_pivot%5D%2C%0A%20%20%20%20%20%20%20%20forecasting_horizon%3D_pivot%2C%0A%20%20%20%20)%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%7B%22Static%20(predict%20only)%22%3A%20_y_pred_static%2C%20%22Incremental%20(observe_predict)%22%3A%20_y_pred_incr%7D%2C%0A%20%20%20%20%20%20%20%20y_train%3Dy_train%2C%0A%20%20%20%20%20%20%20%20title%3D%22Static%20vs%20Incremental%3A%20single%20observe_predict%20call%22%2C%0A%20%20%20%20%20%20%20%20y_label%3D%22Monthly%20tourists%22%2C%0A%20%20%20%20%20%20%20%20height%3D400%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%206.%20Cross-Validation%20%26%20Hyperparameter%20Search%0A%0A%20%20%20%20Standard%20k-fold%20CV%20destroys%20temporal%20order.%20Yohou%20provides%20**temporal%20splitters**%3A%0A%0A%20%20%20%20-%20%5B%60ExpandingWindowSplitter%60%5D(%2Fpages%2Fapi%2Fgenerated%2Fyohou.model_selection.split.ExpandingWindowSplitter%2F)%3A%20training%20window%20grows%20each%20fold%0A%20%20%20%20-%20%5B%60SlidingWindowSplitter%60%5D(%2Fpages%2Fapi%2Fgenerated%2Fyohou.model_selection.split.SlidingWindowSplitter%2F)%3A%20training%20window%20slides%20at%20fixed%20size%0A%0A%20%20%20%20Pair%20with%20%5B%60GridSearchCV%60%5D(%2Fpages%2Fapi%2Fgenerated%2Fyohou.model_selection.search.GridSearchCV%2F)%20or%20%5B%60RandomizedSearchCV%60%5D(%2Fpages%2Fapi%2Fgenerated%2Fyohou.model_selection.search.RandomizedSearchCV%2F)%20to%20tune%20hyperparameters.%0A%20%20%20%20%22%22%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%5B%60plot_splits%60%5D(%2Fpages%2Fapi%2Fgenerated%2Fyohou.plotting.model_selection.plot_splits%2F)%20visualizes%20the%20train%2Ftest%20structure%20across%20folds.%0A%20%20%20%20An%20**expanding%20window**%20grows%20the%20training%20set%20with%20each%20fold%3A%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(ExpandingWindowSplitter%2C%20plot_splits%2C%20y_train)%3A%0A%20%20%20%20plot_splits(%0A%20%20%20%20%20%20%20%20y_train%2C%0A%20%20%20%20%20%20%20%20ExpandingWindowSplitter(n_splits%3D5%2C%20test_size%3D12)%2C%0A%20%20%20%20%20%20%20%20title%3D%22Expanding%20Window%20(5%20folds)%22%2C%0A%20%20%20%20%20%20%20%20height%3D310%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%20A%20**sliding%20window**%20keeps%20training%20size%20fixed.%20The%20oldest%20data%20is%20dropped%20each%20fold%3A%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(SlidingWindowSplitter%2C%20plot_splits%2C%20y_train)%3A%0A%20%20%20%20plot_splits(%0A%20%20%20%20%20%20%20%20y_train%2C%0A%20%20%20%20%20%20%20%20SlidingWindowSplitter(n_splits%3D5%2C%20test_size%3D12%2C%20stride%3D12)%2C%0A%20%20%20%20%20%20%20%20title%3D%22Sliding%20Window%20(5%20folds)%22%2C%0A%20%20%20%20%20%20%20%20height%3D310%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%23%20RandomizedSearchCV%0A%0A%20%20%20%20Randomly%20sample%20from%20continuous%20%2F%20discrete%20parameter%20distributions.%0A%20%20%20%20Efficient%20for%20larger%20search%20spaces.%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%20MeanAbsoluteError%2C%0A%20%20%20%20RandomizedSearchCV%2C%0A%20%20%20%20clone%2C%0A%20%20%20%20forecasting_horizon%2C%0A%20%20%20%20randint%2C%0A%20%20%20%20reduction%2C%0A%20%20%20%20uniform%2C%0A%20%20%20%20y_train%2C%0A)%3A%0A%20%20%20%20cv%20%3D%20ExpandingWindowSplitter(n_splits%3D2%2C%20test_size%3D12)%0A%0A%20%20%20%20random_search%20%3D%20RandomizedSearchCV(%0A%20%20%20%20%20%20%20%20forecaster%3Dclone(reduction)%2C%0A%20%20%20%20%20%20%20%20param_distributions%3D%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%22estimator__alpha%22%3A%20uniform(0.01%2C%2010.0)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22feature_transformer__lag__lag%22%3A%20randint(1%2C%207)%2C%0A%20%20%20%20%20%20%20%20%7D%2C%0A%20%20%20%20%20%20%20%20scoring%3DMeanAbsoluteError()%2C%0A%20%20%20%20%20%20%20%20cv%3Dcv%2C%0A%20%20%20%20%20%20%20%20n_iter%3D9%2C%0A%20%20%20%20%20%20%20%20refit%3DTrue%2C%0A%20%20%20%20%20%20%20%20return_train_score%3DFalse%2C%0A%20%20%20%20%20%20%20%20n_jobs%3D1%2C%0A%20%20%20%20)%0A%20%20%20%20random_search.fit(y_train%2C%20forecasting_horizon%3Dforecasting_horizon)%0A%0A%20%20%20%20print(f%22Best%20params%3A%20%7Brandom_search.best_params_%7D%22)%0A%20%20%20%20print(f%22Best%20CV%20MAE%3A%20%20%7Brandom_search.best_score_%3A.2f%7D%22)%0A%20%20%20%20return%20(random_search%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%20GridSearchCV%0A%0A%20%20%20%20Exhaustively%20test%20every%20combination%2C%20best%20for%20small%20discrete%20grids.%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%20forecasting_horizon%2C%0A%20%20%20%20reduction%2C%0A%20%20%20%20y_train%2C%0A)%3A%0A%20%20%20%20grid_search%20%3D%20GridSearchCV(%0A%20%20%20%20%20%20%20%20forecaster%3Dclone(reduction)%2C%0A%20%20%20%20%20%20%20%20param_grid%3D%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%22estimator__alpha%22%3A%20%5B0.001%2C%200.01%2C%200.1%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22feature_transformer__lag__lag%22%3A%20%5B1%2C%202%2C%203%5D%2C%0A%20%20%20%20%20%20%20%20%7D%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%3D12)%2C%0A%20%20%20%20%20%20%20%20refit%3DTrue%2C%0A%20%20%20%20%20%20%20%20return_train_score%3DFalse%2C%0A%20%20%20%20%20%20%20%20n_jobs%3D1%2C%0A%20%20%20%20)%0A%20%20%20%20grid_search.fit(y_train%2C%20forecasting_horizon%3Dforecasting_horizon)%0A%0A%20%20%20%20print(f%22Best%20params%3A%20%7Bgrid_search.best_params_%7D%22)%0A%20%20%20%20print(f%22Best%20CV%20MAE%3A%20%20%7Bgrid_search.best_score_%3A.2f%7D%22)%0A%20%20%20%20return%20(grid_search%2C)%0A%0A%0A%40app.cell%0Adef%20_(forecasting_horizon%2C%20grid_search%2C%20random_search%2C%20scorer%2C%20y_test)%3A%0A%20%20%20%20y_pred_random%20%3D%20random_search.predict(forecasting_horizon%3Dforecasting_horizon)%0A%20%20%20%20y_pred_grid%20%3D%20grid_search.predict(forecasting_horizon%3Dforecasting_horizon)%0A%0A%20%20%20%20mae_random%20%3D%20scorer.score(y_test%2C%20y_pred_random)%0A%20%20%20%20mae_grid%20%3D%20scorer.score(y_test%2C%20y_pred_grid)%0A%20%20%20%20print(f%22RandomizedSearchCV%20test%20MAE%3A%20%7Bmae_random%3A.2f%7D%22)%0A%20%20%20%20print(f%22GridSearchCV%20%20%20%20%20%20%20test%20MAE%3A%20%7Bmae_grid%3A.2f%7D%22)%0A%20%20%20%20return%20y_pred_grid%2C%20y_pred_random%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%20Compare%20the%20tuned%20models%20against%20the%20baseline%20on%20the%20test%20set%3A%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(%0A%20%20%20%20plot_forecast%2C%0A%20%20%20%20y_pred_baseline%2C%0A%20%20%20%20y_pred_grid%2C%0A%20%20%20%20y_pred_random%2C%0A%20%20%20%20y_test%2C%0A%20%20%20%20y_train%2C%0A)%3A%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%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%22Baseline%22%3A%20y_pred_baseline%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22RandomizedSearchCV%22%3A%20y_pred_random%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22GridSearchCV%22%3A%20y_pred_grid%2C%0A%20%20%20%20%20%20%20%20%7D%2C%0A%20%20%20%20%20%20%20%20y_train%3Dy_train%2C%0A%20%20%20%20%20%20%20%20title%3D%22Model%20Comparison%20(tuning)%22%2C%0A%20%20%20%20%20%20%20%20y_label%3D%22Monthly%20tourists%22%2C%0A%20%20%20%20%20%20%20%20height%3D400%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%5B%60plot_cv_results_scatter%60%5D(%2Fpages%2Fapi%2Fgenerated%2Fyohou.plotting.model_selection.plot_cv_results_scatter%2F)%20shows%20how%20each%20hyperparameter%20value%20affects%20the%0A%20%20%20%20cross-validated%20score%2C%20making%20it%20easy%20to%20identify%20the%20optimal%20range%3A%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(grid_search%2C%20plot_cv_results_scatter)%3A%0A%20%20%20%20plot_cv_results_scatter(%0A%20%20%20%20%20%20%20%20grid_search.cv_results_%2C%0A%20%20%20%20%20%20%20%20param_name%3D%22estimator__alpha%22%2C%0A%20%20%20%20%20%20%20%20higher_is_better%3DFalse%2C%0A%20%20%20%20%20%20%20%20title%3D%22GridSearchCV%3A%20alpha%20vs%20MAE%22%2C%0A%20%20%20%20%20%20%20%20y_label%3D%22Mean%20MAE%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%0Adef%20_(plot_cv_results_scatter%2C%20random_search)%3A%0A%20%20%20%20plot_cv_results_scatter(%0A%20%20%20%20%20%20%20%20random_search.cv_results_%2C%0A%20%20%20%20%20%20%20%20param_name%3D%22estimator__alpha%22%2C%0A%20%20%20%20%20%20%20%20higher_is_better%3DFalse%2C%0A%20%20%20%20%20%20%20%20title%3D%22RandomizedSearchCV%3A%20alpha%20vs%20MAE%22%2C%0A%20%20%20%20%20%20%20%20y_label%3D%22Mean%20MAE%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%207.%20Scoring%20%26%20Aggregation%0A%0A%20%20%20%20Yohou%20scorers%20extend%20sklearn%20with%20time-series-specific%20features%3A%0A%0A%20%20%20%20-%20**%60score(y_truth%2C%20y_pred)%60**%20returns%20a%20float%20(default%20%60aggregation_method%3D%22all%22%60)%0A%20%20%20%20-%20Change%20%60aggregation_method%60%20for%20richer%20output%3A%0A%20%20%20%20%20%20-%20%60%5B%22stepwise%22%2C%20%22vintagewise%22%5D%60%20-%3E%20per-component%20scores%20(DataFrame)%0A%20%20%20%20%20%20-%20%60%22componentwise%22%60%20%E2%86%92%20per-timestep%20scores%20(DataFrame)%0A%20%20%20%20%20%20-%20%60%22all%22%60%20%E2%86%92%20single%20scalar%20across%20all%20columns%20and%20timesteps%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(MeanAbsoluteError%2C%20MeanSquaredError%2C%20y_pred_reduction%2C%20y_test%2C%20y_train)%3A%0A%20%20%20%20_scorers%20%3D%20%7B%0A%20%20%20%20%20%20%20%20%22MAE%22%3A%20MeanAbsoluteError()%2C%0A%20%20%20%20%20%20%20%20%22MSE%22%3A%20MeanSquaredError()%2C%0A%20%20%20%20%20%20%20%20%22MAE-componentwise%22%3A%20MeanAbsoluteError(aggregation_method%3D%22componentwise%22)%2C%0A%20%20%20%20%7D%0A%20%20%20%20for%20_name%2C%20_s%20in%20_scorers.items()%3A%0A%20%20%20%20%20%20%20%20_s.fit(y_train)%0A%20%20%20%20%20%20%20%20_result%20%3D%20_s.score(y_test%2C%20y_pred_reduction)%0A%20%20%20%20%20%20%20%20print(f%22%7B_name%7D%3A%20%7B_result%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%20With%20%60%22componentwise%22%60%20you%20get%20a%20DataFrame%20with%20one%20error%20per%20timestep%2C%0A%20%20%20%20useful%20for%20diagnosing%20where%20in%20the%20forecast%20horizon%20accuracy%20degrades.%0A%0A%20%20%20%20See%20%60examples%2Fmetrics%2F%60%20for%20an%20exhaustive%20scorer%20survey.%0A%20%20%20%20%22%22%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%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_(copy%2C%20forecasting_horizon%2C%20reduction%2C%20y_test)%3A%0A%20%20%20%20_model%20%3D%20copy.deepcopy(reduction)%0A%20%20%20%20y_pred_vintages%20%3D%20_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%3Dforecasting_horizon%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_(plot_score_per_vintage%2C%20scorer%2C%20y_pred_vintages%2C%20y_test)%3A%0A%20%20%20%20plot_score_per_vintage(%0A%20%20%20%20%20%20%20%20scorer%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%0Adef%20_(plot_score_time_series%2C%20scorer%2C%20y_pred_vintages%2C%20y_test)%3A%0A%20%20%20%20plot_score_time_series(%0A%20%20%20%20%20%20%20%20scorer%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%20facet_by%3D%22vintage%22%2C%0A%20%20%20%20%20%20%20%20title%3D%22Per-timestep%20MAE%20across%20Vintages%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%3D500%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%208.%20Interval%20Forecasting%3A%20Prediction%20Intervals%0A%0A%20%20%20%20%5B%60SplitConformalForecaster%60%5D(%2Fpages%2Fapi%2Fgenerated%2Fyohou.interval.split_conformal.SplitConformalForecaster%2F)%20wraps%20any%20point%20forecaster%20and%20produces%0A%20%20%20%20**calibrated%20prediction%20intervals**%20using%20split%20conformal%20prediction.%0A%0A%20%20%20%20Pass%20%60coverage_rates%60%20to%20%60predict_interval%60%20to%20get%20intervals%20at%20the%0A%20%20%20%20desired%20confidence%20levels.%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%20LagTransformer%2C%0A%20%20%20%20LogTransformer%2C%0A%20%20%20%20PointReductionForecaster%2C%0A%20%20%20%20Ridge%2C%0A%20%20%20%20SeasonalDifferencing%2C%0A%20%20%20%20SplitConformalForecaster%2C%0A%20%20%20%20forecasting_horizon%2C%0A%20%20%20%20y_train%2C%0A)%3A%0A%20%20%20%20conformal%20%3D%20SplitConformalForecaster(%0A%20%20%20%20%20%20%20%20point_forecaster%3DPointReductionForecaster(%0A%20%20%20%20%20%20%20%20%20%20%20%20estimator%3DRidge(alpha%3D10)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20target_transformer%3DFeaturePipeline(%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20(%22log%22%2C%20LogTransformer(offset%3D1.0))%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20(%22diff%22%2C%20SeasonalDifferencing(seasonality%3D12))%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%5D)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20feature_transformer%3DFeaturePipeline(%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20(%22lag%22%2C%20LagTransformer(lag%3D%5B1%2C%202%2C%203%5D))%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%5D)%2C%0A%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20%20%20%20%20calibration_size%3D30%2C%0A%20%20%20%20)%0A%20%20%20%20conformal.fit(y_train%2C%20forecasting_horizon%3Dforecasting_horizon)%0A%0A%20%20%20%20coverage_rates%20%3D%20%5B0.1%2C%200.2%2C%200.3%2C%200.4%2C%200.5%2C%200.6%2C%200.7%2C%200.8%2C%200.9%5D%0A%20%20%20%20_y_pred_point%20%3D%20conformal.predict(forecasting_horizon%3Dforecasting_horizon).drop(%22vintage_time%22)%0A%20%20%20%20_y_pred_pi%20%3D%20conformal.predict_interval(%0A%20%20%20%20%20%20%20%20forecasting_horizon%3Dforecasting_horizon%2C%0A%20%20%20%20%20%20%20%20coverage_rates%3Dcoverage_rates%2C%0A%20%20%20%20)%0A%20%20%20%20y_pred_interval%20%3D%20_y_pred_point.join(_y_pred_pi%2C%20on%3D%22time%22)%0A%20%20%20%20print(f%22Interval%20columns%3A%20%7By_pred_interval.columns%7D%22)%0A%20%20%20%20y_pred_interval.head()%0A%20%20%20%20return%20conformal%2C%20coverage_rates%2C%20y_pred_interval%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%20Conformality%20score%20distribution%0A%0A%20%20%20%20Under%20the%20hood%20%5B%60SplitConformalForecaster%60%5D(%2Fpages%2Fapi%2Fgenerated%2Fyohou.interval.split_conformal.SplitConformalForecaster%2F)%20computes%20a%20**conformality%20score**%0A%20%20%20%20for%20every%20point%20in%20the%20held-out%20calibration%20set%3A%0A%0A%20%20%20%20%24%24s_i%20%3D%20y_i%20-%20%5Chat%7By%7D_i%20%5Cquad%20%5Ctext%7B(signed%20residual%2C%20default%20scorer)%7D%24%24%0A%0A%20%20%20%20These%20scores%20are%20stored%20in%20%60conformal.conformity_scores_%60%20(one%20row%20per%0A%20%20%20%20calibration%20point%2C%20one%20score%20per%20forecast%20step).%20%20Their%20empirical%0A%20%20%20%20distribution%20governs%20two%20things%3A%0A%0A%20%20%20%20*%20**Interval%20centres**%3A%20a%20zero-centred%20distribution%20means%20the%20point%0A%20%20%20%20%20%20forecaster%20is%20unbiased%20on%20the%20calibration%20window.%0A%20%20%20%20*%20**Interval%20widths**%3A%20the%20spread%20sets%20the%20floor%20on%20achievable%20interval%0A%20%20%20%20%20%20width.%20%20The%20dashed%20lines%20mark%20the%20lower%2Fupper%20quantiles%20used%20for%20each%0A%20%20%20%20%20%20coverage%20level.%20The%20further%20apart%20a%20pair%2C%20the%20wider%20that%20band.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(conformal%2C%20go)%3A%0A%20%20%20%20_scores%20%3D%20conformal.conformity_scores_%0A%20%20%20%20_col%20%3D%20next(c%20for%20c%20in%20_scores.columns%20if%20c%20not%20in%20(%22time%22%2C%20%22step%22))%0A%20%20%20%20_vals%20%3D%20_scores%5B_col%5D.drop_nulls()%0A%0A%20%20%20%20_fig_scores%20%3D%20go.Figure()%0A%20%20%20%20_fig_scores.add_trace(%0A%20%20%20%20%20%20%20%20go.Histogram(%0A%20%20%20%20%20%20%20%20%20%20%20%20x%3D_vals.to_list()%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20nbinsx%3D20%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20name%3D%22Calibration%20scores%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20marker_color%3D%22%234c78a8%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20opacity%3D0.72%2C%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20)%0A%20%20%20%20_fig_scores.add_vline(%0A%20%20%20%20%20%20%20%20x%3D0%2C%0A%20%20%20%20%20%20%20%20line_dash%3D%22dot%22%2C%0A%20%20%20%20%20%20%20%20line_color%3D%22%23888%22%2C%0A%20%20%20%20%20%20%20%20line_width%3D1.5%2C%0A%20%20%20%20%20%20%20%20annotation_text%3D%220%22%2C%0A%20%20%20%20%20%20%20%20annotation_position%3D%22top%22%2C%0A%20%20%20%20)%0A%20%20%20%20for%20_rate%2C%20_color%20in%20zip(%5B0.5%2C%200.7%2C%200.9%5D%2C%20%5B%22%23f28e2b%22%2C%20%22%2359a14f%22%2C%20%22%23e15759%22%5D)%3A%0A%20%20%20%20%20%20%20%20_alpha%20%3D%20(1%20-%20_rate)%20%2F%202%0A%20%20%20%20%20%20%20%20_q_lo%20%3D%20float(_vals.quantile(_alpha))%0A%20%20%20%20%20%20%20%20_q_hi%20%3D%20float(_vals.quantile(1%20-%20_alpha))%0A%20%20%20%20%20%20%20%20_fig_scores.add_vline(%0A%20%20%20%20%20%20%20%20%20%20%20%20x%3D_q_lo%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20line_dash%3D%22dash%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20line_color%3D_color%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20line_width%3D1.5%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20annotation_text%3Df%22%7B_rate%3A.0%25%7D%20lo%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20annotation_position%3D%22bottom%20right%22%2C%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20_fig_scores.add_vline(%0A%20%20%20%20%20%20%20%20%20%20%20%20x%3D_q_hi%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20line_dash%3D%22dash%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20line_color%3D_color%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20line_width%3D1.5%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20annotation_text%3Df%22%7B_rate%3A.0%25%7D%20hi%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20annotation_position%3D%22top%20right%22%2C%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20_fig_scores.update_layout(%0A%20%20%20%20%20%20%20%20title%3Df%22Conformality%20Score%20Distribution%2C%20calibration%20set%20(%7B_col%7D)%22%2C%0A%20%20%20%20%20%20%20%20xaxis_title%3D%22Score%20%20(y%20%E2%88%92%20%C5%B7)%22%2C%0A%20%20%20%20%20%20%20%20yaxis_title%3D%22Count%22%2C%0A%20%20%20%20%20%20%20%20bargap%3D0.05%2C%0A%20%20%20%20%20%20%20%20height%3D380%2C%0A%20%20%20%20%20%20%20%20template%3D%22simple_white%22%2C%0A%20%20%20%20%20%20%20%20showlegend%3DFalse%2C%0A%20%20%20%20)%0A%20%20%20%20_fig_scores%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(plot_forecast%2C%20y_pred_interval%2C%20y_test%2C%20y_train)%3A%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%20y_pred_interval%2C%0A%20%20%20%20%20%20%20%20y_train%3Dy_train%2C%0A%20%20%20%20%20%20%20%20coverage_rates%3D%5B0.5%2C%200.7%2C%200.9%5D%2C%0A%20%20%20%20%20%20%20%20title%3D%2250%20%2F%2070%20%2F%2090%25%20Prediction%20Intervals%20(Split%20Conformal)%22%2C%0A%20%20%20%20%20%20%20%20y_label%3D%22Monthly%20tourists%22%2C%0A%20%20%20%20%20%20%20%20height%3D400%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%23%20Calibration%0A%0A%20%20%20%20A%20well-calibrated%20forecaster%20should%20hit%20its%20nominal%20coverage%3A%20a%2090%25%20interval%0A%20%20%20%20should%20contain%20the%20actual%20value%20~90%25%20of%20the%20time.%20The%20calibration%20plot%20compares%0A%20%20%20%20**empirical%20coverage**%20(observed%20fraction%20of%20actuals%20inside%20the%20interval)%20against%0A%20%20%20%20**nominal%20coverage**%20(the%20requested%20rate)%20for%20every%20level.%20Points%20on%20the%20diagonal%0A%20%20%20%20indicate%20perfect%20calibration%3B%20points%20below%20mean%20the%20intervals%20are%20too%20narrow%0A%20%20%20%20(under-coverage)%2C%20points%20above%20mean%20they%20are%20too%20wide%20(over-coverage).%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(coverage_rates%2C%20plot_calibration%2C%20y_pred_interval%2C%20y_test)%3A%0A%20%20%20%20plot_calibration(%0A%20%20%20%20%20%20%20%20y_pred_interval%2C%0A%20%20%20%20%20%20%20%20y_test%2C%0A%20%20%20%20%20%20%20%20coverage_rates%3Dcoverage_rates%2C%0A%20%20%20%20%20%20%20%20columns%3D%22tourists%22%2C%0A%20%20%20%20%20%20%20%20title%3D%22Prediction%20Interval%20Calibration%22%2C%0A%20%20%20%20%20%20%20%20height%3D400%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%209.%20Time-Weighted%20Training%0A%0A%20%20%20%20Give%20more%20importance%20to%20**recent**%20observations%20during%20model%20fitting%20via%0A%20%20%20%20%60time_weight%60%2C%20a%20callable%20%60(pl.Series)%20%E2%86%92%20pl.Series%60.%0A%0A%20%20%20%20Built-in%20weight%20functions%3A%0A%0A%20%20%20%20%7C%20Function%20%7C%20Effect%20%7C%0A%20%20%20%20%7C----------%7C--------%7C%0A%20%20%20%20%7C%20%60exponential_decay_weight(half_life%3D%E2%80%A6)%60%20%7C%20Recent%20data%20gets%20exponentially%20more%20weight%20%7C%0A%20%20%20%20%7C%20%60linear_decay_weight(max_steps%3D%E2%80%A6)%60%20%7C%20Linear%20ramp%20from%200%20%E2%86%92%201%20%7C%0A%20%20%20%20%7C%20%60seasonal_emphasis_weight(seasonality%3D%E2%80%A6%2C%20emphasis%3D%E2%80%A6)%60%20%7C%20Boost%20specific%20seasonal%20positions%20%7C%0A%20%20%20%20%7C%20%60compose_weights(fn1%2C%20fn2%2C%20%E2%80%A6)%60%20%7C%20Multiply%20multiple%20weight%20functions%20element-wise%20%7C%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(%0A%20%20%20%20exponential_decay_weight%2C%0A%20%20%20%20linear_decay_weight%2C%0A%20%20%20%20pl%2C%0A%20%20%20%20plot_time_weight%2C%0A%20%20%20%20seasonal_emphasis_weight%2C%0A%20%20%20%20y_train%2C%0A)%3A%0A%20%20%20%20_fns%20%3D%20%7B%0A%20%20%20%20%20%20%20%20%22exponential%22%3A%20exponential_decay_weight(half_life%3D15)%2C%0A%20%20%20%20%20%20%20%20%22linear%22%3A%20linear_decay_weight(max_steps%3DNone)%2C%0A%20%20%20%20%20%20%20%20%22seasonal%22%3A%20seasonal_emphasis_weight(seasonality%3D12%2C%20emphasis%3D3.0)%2C%0A%20%20%20%20%7D%0A%20%20%20%20_weights_df%20%3D%20y_train.select(%22time%22).with_columns(%5B%0A%20%20%20%20%20%20%20%20pl.Series(f%22time_weight__%7Bname%7D%22%2C%20fn(y_train%5B%22time%22%5D).to_list())%20for%20name%2C%20fn%20in%20_fns.items()%0A%20%20%20%20%5D)%0A%20%20%20%20plot_time_weight(%0A%20%20%20%20%20%20%20%20_weights_df%2C%0A%20%20%20%20%20%20%20%20weight_column%3D%22time_weight%22%2C%0A%20%20%20%20%20%20%20%20facet_n_cols%3D1%2C%0A%20%20%20%20%20%20%20%20title%3D%22Time%20Weight%20Functions%22%2C%0A%20%20%20%20%20%20%20%20height%3D500%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%23%20Composing%20weights%20%26%20metadata%20routing%0A%0A%20%20%20%20%60compose_weights(fn1%2C%20fn2%2C%20%E2%80%A6)%60%20multiplies%20weight%20vectors%20element-wise%2C%20so%0A%20%20%20%20recent%20*and*%20seasonally%20prominent%20timesteps%20are%20emphasised%20simultaneously%3A%0A%0A%20%20%20%20%60%60%60python%0A%20%20%20%20_tw%20%3D%20compose_weights(%0A%20%20%20%20%20%20%20%20exponential_decay_weight(half_life%3D20)%2C%20%20%20%23%20recency%0A%20%20%20%20%20%20%20%20seasonal_emphasis_weight(seasonality%3D12%2C%20emphasis%3D3.0)%2C%20%20%23%20season%0A%20%20%20%20)%0A%20%20%20%20%60%60%60%0A%0A%20%20%20%20Yohou%20uses%20**sklearn%20metadata%20routing**%20to%20thread%20%60time_weight%60%20through%0A%20%20%20%20pipelines%20without%20touching%20every%20intermediate%20step.%0A%20%20%20%20Opt%20the%20forecaster%20in%20with%20%60.set_fit_request(time_weight%3DTrue)%60%2C%20then%0A%20%20%20%20pass%20the%20weight%20as%20a%20keyword%20argument%20to%20%60.fit()%60%3A%0A%0A%20%20%20%20%60%60%60python%0A%20%20%20%20forecaster.set_fit_request(time_weight%3DTrue)%0A%20%20%20%20forecaster.fit(y_train%2C%20forecasting_horizon%3Dh%2C%20time_weight%3D_tw)%0A%20%20%20%20%60%60%60%0A%0A%20%20%20%20The%20framework%20converts%20%60time_weight%60%20to%20sklearn's%20%60sample_weight%60%20internally%0A%20%20%20%20before%20passing%20it%20to%20the%20underlying%20estimator.%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%20LagTransformer%2C%0A%20%20%20%20LogTransformer%2C%0A%20%20%20%20PointReductionForecaster%2C%0A%20%20%20%20Ridge%2C%0A%20%20%20%20SeasonalDifferencing%2C%0A%20%20%20%20compose_weights%2C%0A%20%20%20%20exponential_decay_weight%2C%0A%20%20%20%20forecasting_horizon%2C%0A%20%20%20%20scorer%2C%0A%20%20%20%20seasonal_emphasis_weight%2C%0A%20%20%20%20y_test%2C%0A%20%20%20%20y_train%2C%0A)%3A%0A%20%20%20%20_tw%20%3D%20compose_weights(%0A%20%20%20%20%20%20%20%20exponential_decay_weight(half_life%3D20)%2C%0A%20%20%20%20%20%20%20%20seasonal_emphasis_weight(seasonality%3D12%2C%20emphasis%3D3.0)%2C%0A%20%20%20%20)%0A%0A%20%20%20%20weighted_forecaster%20%3D%20PointReductionForecaster(%0A%20%20%20%20%20%20%20%20estimator%3DRidge(alpha%3D10)%2C%0A%20%20%20%20%20%20%20%20target_transformer%3DFeaturePipeline(%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20(%22log%22%2C%20LogTransformer(offset%3D1.0))%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20(%22diff%22%2C%20SeasonalDifferencing(seasonality%3D12))%2C%0A%20%20%20%20%20%20%20%20%5D)%2C%0A%20%20%20%20%20%20%20%20feature_transformer%3DFeaturePipeline(%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20(%22lag%22%2C%20LagTransformer(lag%3D%5B1%2C%202%2C%203%5D))%2C%0A%20%20%20%20%20%20%20%20%5D)%2C%0A%20%20%20%20).set_fit_request(time_weight%3DTrue)%0A%0A%20%20%20%20weighted_forecaster.fit(y_train%2C%20forecasting_horizon%3Dforecasting_horizon%2C%20time_weight%3D_tw)%0A%20%20%20%20y_pred_weighted%20%3D%20weighted_forecaster.predict(forecasting_horizon%3Dforecasting_horizon)%0A%20%20%20%20mae_weighted%20%3D%20scorer.score(y_test%2C%20y_pred_weighted)%0A%20%20%20%20print(f%22Time-weighted%20MAE%3A%20%7Bmae_weighted%3A.2f%7D%22)%0A%20%20%20%20return%20(y_pred_weighted%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%20Weighted%20vs.%20unweighted%20forecast%0A%0A%20%20%20%20Overlaying%20both%20forecasts%20shows%20the%20practical%20effect%20of%20the%20time%20weight%3A%0A%20%20%20%20the%20weighted%20model%20tilts%20its%20extrapolation%20towards%20the%20most%20recent%20trend%0A%20%20%20%20and%20the%20seasonal%20peak%20positions%20it%20was%20told%20to%20emphasise.%0A%20%20%20%20A%20lower%20MAE%20alone%20does%20not%20tell%20the%20whole%20story.%20The%20chart%20reveals%20*where*%0A%20%20%20%20the%20two%20models%20diverge%20and%20whether%20the%20weighted%20version%20tracks%20the%0A%20%20%20%20actual%20trajectory%20more%20closely%20in%20the%20critical%20final%20months.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(plot_forecast%2C%20y_pred_reduction%2C%20y_pred_weighted%2C%20y_test%2C%20y_train)%3A%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%7B%22Unweighted%22%3A%20y_pred_reduction%2C%20%22Time-weighted%22%3A%20y_pred_weighted%7D%2C%0A%20%20%20%20%20%20%20%20y_train%3Dy_train%2C%0A%20%20%20%20%20%20%20%20title%3D%22Time-Weighted%20vs.%20Unweighted%20Forecast%22%2C%0A%20%20%20%20%20%20%20%20y_label%3D%22Monthly%20tourists%22%2C%0A%20%20%20%20%20%20%20%20height%3D400%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%2010.%20Panel%20Data%0A%0A%20%20%20%20Yohou%20uses%20**column%20prefixes%20with%20%60__%60**%20to%20represent%20panel%20groups%3A%0A%0A%20%20%20%20%60%60%60%0A%20%20%20%20T1__profit%20%20%20T2__profit%20%20%20T3__profit%0A%20%20%20%20%60%60%60%0A%0A%20%20%20%20Any%20forecaster%20automatically%20handles%20all%20groups%20when%20it%20sees%20this%20pattern.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(fetch_dominick%2C%20inspect_panel)%3A%0A%20%20%20%20_bunch%20%3D%20fetch_dominick()%0A%20%20%20%20%23%20Select%203%20series%20that%20have%20complete%20data%20(no%20nulls)%0A%20%20%20%20_cols%20%3D%20%5B%22T7__profit%22%2C%20%22T11__profit%22%2C%20%22T12__profit%22%5D%0A%20%20%20%20y_panel%20%3D%20_bunch.frame.select(%22time%22%2C%20*_cols)%0A%0A%20%20%20%20_global%2C%20_groups%20%3D%20inspect_panel(y_panel)%0A%20%20%20%20print(f%22Panel%20groups%3A%20%7Blist(_groups.keys())%7D%22)%0A%20%20%20%20y_panel.head()%0A%20%20%20%20return%20(y_panel%2C)%0A%0A%0A%40app.cell%0Adef%20_(MeanAbsoluteError%2C%20SeasonalNaive%2C%20train_test_split%2C%20y_panel)%3A%0A%20%20%20%20y_panel_train%2C%20y_panel_test%20%3D%20train_test_split(%0A%20%20%20%20%20%20%20%20y_panel%2C%0A%20%20%20%20%20%20%20%20test_size%3D13%2C%0A%20%20%20%20)%0A%0A%20%20%20%20panel_baseline%20%3D%20SeasonalNaive(seasonality%3D52)%0A%20%20%20%20panel_baseline.fit(y_panel_train%2C%20forecasting_horizon%3D13)%0A%20%20%20%20y_pred_panel%20%3D%20panel_baseline.predict(forecasting_horizon%3D13)%0A%0A%20%20%20%20_scorer%20%3D%20MeanAbsoluteError()%0A%20%20%20%20_scorer.fit(y_panel_test)%0A%20%20%20%20print(f%22Panel%20baseline%20MAE%3A%20%7B_scorer.score(y_panel_test%2C%20y_pred_panel)%3A.2f%7D%22)%0A%20%20%20%20return%20y_panel_test%2C%20y_panel_train%2C%20y_pred_panel%0A%0A%0A%40app.cell%0Adef%20_(plot_forecast%2C%20y_panel_test%2C%20y_panel_train%2C%20y_pred_panel)%3A%0A%20%20%20%20plot_forecast(%0A%20%20%20%20%20%20%20%20y_panel_test%2C%0A%20%20%20%20%20%20%20%20y_pred_panel%2C%0A%20%20%20%20%20%20%20%20y_train%3Dy_panel_train%2C%0A%20%20%20%20%20%20%20%20facet_n_cols%3D1%2C%0A%20%20%20%20%20%20%20%20title%3D%22Panel%20Forecast%3A%20SeasonalNaive%22%2C%0A%20%20%20%20%20%20%20%20y_label%3D%22Profit%22%2C%0A%20%20%20%20%20%20%20%20height%3D700%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%23%20Panel%20Strategies%0A%0A%20%20%20%20When%20a%20reduction%20forecaster%20sees%20panel%20data%2C%20the%20%60panel_strategy%60%20parameter%0A%20%20%20%20controls%20how%20groups%20are%20handled%3A%0A%0A%20%20%20%20%7C%20Strategy%20%7C%20Behaviour%20%7C%0A%20%20%20%20%7C----------%7C-----------%7C%0A%20%20%20%20%7C%20%60%22global%22%60%20(default)%20%7C%20Per-group%20transformers%2C%20but%20**one%20pooled%20model**%20trained%20on%20all%20groups%20%7C%0A%20%20%20%20%7C%20%60%22multivariate%22%60%20%7C%20Treat%20prefixed%20columns%20as%20ordinary%20multivariate%20columns%20(no%20panel%20logic)%20%7C%0A%0A%20%20%20%20For%20**independent%20per-group%20models**%2C%20wrap%20any%20forecaster%20with%0A%20%20%20%20%5B%60LocalPanelForecaster%60%5D(%2Fpages%2Fapi%2Fgenerated%2Fyohou.compose.local_panel_forecaster.LocalPanelForecaster%2F)%2C%20which%20clones%20a%20separate%20instance%20for%20each%20group.%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%20LagTransformer%2C%0A%20%20%20%20MeanAbsoluteError%2C%0A%20%20%20%20PointReductionForecaster%2C%0A%20%20%20%20Ridge%2C%0A%20%20%20%20y_panel_test%2C%0A%20%20%20%20y_panel_train%2C%0A)%3A%0A%20%20%20%20%23%20Global%20panel%20strategy%20(default)%0A%20%20%20%20%23%20One%20Ridge%20model%20is%20trained%20on%20the%20pooled%20tabularised%20data%20from%20all%20groups.%0A%20%20%20%20global_forecaster%20%3D%20PointReductionForecaster(%0A%20%20%20%20%20%20%20%20estimator%3DRidge(alpha%3D1.0)%2C%0A%20%20%20%20%20%20%20%20feature_transformer%3DFeaturePipeline(%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20(%22lag%22%2C%20LagTransformer(lag%3D%5B1%2C%202%2C%203%2C%204%5D))%2C%0A%20%20%20%20%20%20%20%20%5D)%2C%0A%20%20%20%20%20%20%20%20panel_strategy%3D%22global%22%2C%20%20%23%20default%3A%20pool%20groups%20into%20one%20model%0A%20%20%20%20)%0A%20%20%20%20global_forecaster.fit(y_panel_train%2C%20forecasting_horizon%3D13)%0A%20%20%20%20y_pred_global%20%3D%20global_forecaster.predict(forecasting_horizon%3D13)%0A%0A%20%20%20%20_scorer%20%3D%20MeanAbsoluteError()%0A%20%20%20%20_scorer.fit(y_panel_test)%0A%20%20%20%20print(f%22Global%20panel%20MAE%3A%20%7B_scorer.score(y_panel_test%2C%20y_pred_global)%3A.2f%7D%22)%0A%20%20%20%20return%20(y_pred_global%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%60LocalPanelForecaster%60%5D(%2Fpages%2Fapi%2Fgenerated%2Fyohou.compose.local_panel_forecaster.LocalPanelForecaster%2F)%20takes%20the%20opposite%20approach%3A%20it%20**clones**%20the%0A%20%20%20%20wrapped%20forecaster%20once%20per%20panel%20group%20and%20fits%20each%20clone%20independently%0A%20%20%20%20on%20that%20group's%20data%20alone.%20%20This%20is%20useful%20when%20groups%20are%20heterogeneous%0A%20%20%20%20and%20a%20single%20pooled%20model%20cannot%20capture%20group-specific%20dynamics.%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%20LagTransformer%2C%0A%20%20%20%20LocalPanelForecaster%2C%0A%20%20%20%20MeanAbsoluteError%2C%0A%20%20%20%20PointReductionForecaster%2C%0A%20%20%20%20Ridge%2C%0A%20%20%20%20y_panel_test%2C%0A%20%20%20%20y_panel_train%2C%0A)%3A%0A%20%20%20%20local_forecaster%20%3D%20LocalPanelForecaster(%0A%20%20%20%20%20%20%20%20forecaster%3DPointReductionForecaster(%0A%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%20feature_transformer%3DFeaturePipeline(%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20(%22lag%22%2C%20LagTransformer(lag%3D%5B1%2C%202%2C%203%2C%204%5D))%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%5D)%2C%0A%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20)%0A%20%20%20%20local_forecaster.fit(y_panel_train%2C%20forecasting_horizon%3D13)%0A%20%20%20%20y_pred_local%20%3D%20local_forecaster.predict(forecasting_horizon%3D13)%0A%0A%20%20%20%20_scorer%20%3D%20MeanAbsoluteError()%0A%20%20%20%20_scorer.fit(y_panel_test)%0A%20%20%20%20print(f%22Local%20panel%20MAE%3A%20%20%7B_scorer.score(y_panel_test%2C%20y_pred_local)%3A.2f%7D%22)%0A%20%20%20%20return%20(y_pred_local%2C)%0A%0A%0A%40app.cell%0Adef%20_(%0A%20%20%20%20plot_forecast%2C%0A%20%20%20%20y_panel_test%2C%0A%20%20%20%20y_panel_train%2C%0A%20%20%20%20y_pred_global%2C%0A%20%20%20%20y_pred_local%2C%0A%20%20%20%20y_pred_panel%2C%0A)%3A%0A%20%20%20%20plot_forecast(%0A%20%20%20%20%20%20%20%20y_panel_test%2C%0A%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%22SeasonalNaive%22%3A%20y_pred_panel%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22Global%20(pooled)%22%3A%20y_pred_global%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22Local%20(per-group)%22%3A%20y_pred_local%2C%0A%20%20%20%20%20%20%20%20%7D%2C%0A%20%20%20%20%20%20%20%20y_train%3Dy_panel_train%2C%0A%20%20%20%20%20%20%20%20facet_n_cols%3D1%2C%0A%20%20%20%20%20%20%20%20title%3D%22Panel%20Strategies%20Comparison%22%2C%0A%20%20%20%20%20%20%20%20y_label%3D%22Profit%22%2C%0A%20%20%20%20%20%20%20%20height%3D700%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%7C%20Topic%20%7C%20Notebook%20%7C%0A%20%20%20%20%7C-------%7C----------%7C%0A%20%20%20%20%7C%20Feature%20engineering%20%7C%20%5B%60point%2Ffeature_forecasting.py%60%5D(%2Fexamples%2Fforecasting-models%2Ffeature_forecasting%2F)%2C%20%5B%60preprocessing%2Fwindow_transformers.py%60%5D(%2Fexamples%2Fdata-features%2Fwindow_transformers%2F)%20%7C%0A%20%20%20%20%7C%20Point%20forecasters%20%7C%20%5B%60point%2Fnaive_forecasters.py%60%5D(%2Fexamples%2Fgetting-started%2Fnaive_forecasters%2F)%2C%20%5B%60point%2Freduction_forecaster.py%60%5D(%2Fexamples%2Fgetting-started%2Freduction_forecaster%2F)%20%7C%0A%20%20%20%20%7C%20Class%20probability%20%7C%20%5B%60point%2Fclass_proba_forecaster.py%60%5D(%2Fexamples%2Fpoint%2Fclass_proba_forecaster%2F)%2C%20%5B%60metrics%2Fclass_proba_metrics.py%60%5D(%2Fexamples%2Fevaluation-search%2Fclass_proba_metrics%2F)%20%7C%0A%20%20%20%20%7C%20Interval%20forecasting%20%7C%20%5B%60interval%2Finterval_reduction.py%60%5D(%2Fexamples%2Fforecasting-models%2Finterval_reduction%2F)%2C%20%5B%60metrics%2Fconformity_scorers.py%60%5D(%2Fexamples%2Fevaluation-search%2Fconformity_scorers%2F)%20%7C%0A%20%20%20%20%7C%20Decomposition%20deep%20dive%20%7C%20%5B%60stationarity%2Fdecomposition.py%60%5D(%2Fexamples%2Fgetting-started%2Fdecomposition%2F)%20%7C%0A%20%20%20%20%7C%20Metrics%20guide%20%7C%20%5B%60metrics%2Fpoint_metrics.py%60%5D(%2Fexamples%2Fevaluation-search%2Fpoint_metrics%2F)%2C%20%5B%60metrics%2Finterval_metrics.py%60%5D(%2Fexamples%2Fevaluation-search%2Finterval_metrics%2F)%20%7C%0A%20%20%20%20%7C%20Splitters%20%26%20search%20%7C%20%5B%60model_selection%2Fcv_splitters.py%60%5D(%2Fexamples%2Fgetting-started%2Fcv_splitters%2F)%2C%20%5B%60model_selection%2Fhyperparameter_search.py%60%5D(%2Fexamples%2Fevaluation-search%2Fhyperparameter_search%2F)%20%7C%0A%20%20%20%20%7C%20Dataset%20explorers%20%7C%20%5B%60datasets%2Ftourism_monthly.py%60%5D(%2Fexamples%2Fdatasets%2Ftourism_monthly%2F)%2C%20%5B%60datasets%2Fstore_sales.py%60%5D(%2Fexamples%2Fdatasets%2Fstore_sales%2F)%2C%20%E2%80%A6%20%7C%0A%20%20%20%20%7C%20Plotting%20gallery%20%7C%20%5B%60plotting%2Fexploration.py%60%5D(%2Fexamples%2Fvisualization%2Fexploration%2F)%2C%20%5B%60plotting%2Fforecasting_visualization.py%60%5D(%2Fexamples%2Fvisualization%2Fforecasting_visualization%2F)%2C%20%E2%80%A6%20%7C%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
014c28cba937f2106e1ffb0c00088a78