%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%20How%20to%20Combine%20Interval%20Forecasters%20with%20VotingIntervalForecaster%0A%0A%20%20%20%20This%20notebook%20shows%20how%20to%20combine%20multiple%20interval%20forecasters%20into%0A%20%20%20%20a%20single%20ensemble%20using%20%5B%60VotingIntervalForecaster%60%5D(%2Fpages%2Fapi%2Fgenerated%2Fyohou.ensemble.VotingIntervalForecaster%2F).%0A%20%20%20%20Aggregating%20intervals%20from%20diverse%20models%20can%20produce%20more%20robust%0A%20%20%20%20coverage%20than%20any%20single%20model.%0A%0A%20%20%20%20**Prerequisites%3A**%20Familiarity%20with%20interval%20forecasting.%20See%20the%20%5Bconformal%20forecasting%20example%5D(%2Fexamples%2Fgetting-started%2Fconformal_forecasting%2F)%20for%20an%20introduction.%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.ensemble%20import%20GradientBoostingRegressor%0A%20%20%20%20from%20sklearn.linear_model%20import%20Ridge%0A%0A%20%20%20%20from%20yohou.datasets%20import%20fetch_sunspot%0A%20%20%20%20from%20yohou.ensemble%20import%20VotingIntervalForecaster%0A%20%20%20%20from%20yohou.interval%20import%20SplitConformalForecaster%0A%20%20%20%20from%20yohou.model_selection%20import%20train_test_split%0A%20%20%20%20from%20yohou.plotting%20import%20plot_forecast%0A%20%20%20%20from%20yohou.point%20import%20PointReductionForecaster%2C%20SeasonalNaive%0A%20%20%20%20from%20yohou.preprocessing%20import%20LagTransformer%0A%0A%20%20%20%20return%20(%0A%20%20%20%20%20%20%20%20GradientBoostingRegressor%2C%0A%20%20%20%20%20%20%20%20LagTransformer%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%20SeasonalNaive%2C%0A%20%20%20%20%20%20%20%20SplitConformalForecaster%2C%0A%20%20%20%20%20%20%20%20VotingIntervalForecaster%2C%0A%20%20%20%20%20%20%20%20deepcopy%2C%0A%20%20%20%20%20%20%20%20fetch_sunspot%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%20train_test_split%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.%20Load%20and%20Prepare%20Data%0A%0A%20%20%20%20Monthly%20sunspot%20numbers%20with%20a%20strong%20~11-year%20cycle.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(fetch_sunspot%2C%20pl%2C%20train_test_split)%3A%0A%20%20%20%20y%20%3D%20fetch_sunspot().frame.group_by_dynamic(%22time%22%2C%20every%3D%221mo%22).agg(pl.col(%22sunspot_number%22).mean())%0A%20%20%20%20forecasting_horizon%20%3D%2024%0A%20%20%20%20y_train%2C%20y_test%20%3D%20train_test_split(y%2C%20test_size%3D5%20*%20forecasting_horizon)%0A%20%20%20%20coverage%20%3D%20%5B0.50%2C%200.80%2C%200.90%5D%0A%0A%20%20%20%20print(f%22Training%3A%20%7Blen(y_train)%7D%20obs%2C%20Test%3A%20%7Blen(y_test)%7D%20obs%22)%0A%20%20%20%20return%20coverage%2C%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.%20Define%20Base%20Interval%20Forecasters%0A%0A%20%20%20%20Wrap%20different%20point%20forecasters%20in%20%5B%60SplitConformalForecaster%60%5D(%2Fpages%2Fapi%2Fgenerated%2Fyohou.interval.split_conformal.SplitConformalForecaster%2F)%20to%0A%20%20%20%20produce%20prediction%20intervals.%20Diversity%20in%20the%20underlying%20point%0A%20%20%20%20model%20leads%20to%20better%20ensemble%20coverage.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(%0A%20%20%20%20GradientBoostingRegressor%2C%0A%20%20%20%20LagTransformer%2C%0A%20%20%20%20PointReductionForecaster%2C%0A%20%20%20%20Ridge%2C%0A%20%20%20%20SeasonalNaive%2C%0A%20%20%20%20SplitConformalForecaster%2C%0A)%3A%0A%20%20%20%20base_forecasters%20%3D%20%5B%0A%20%20%20%20%20%20%20%20(%0A%20%20%20%20%20%20%20%20%20%20%20%20%22conf_naive%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20SplitConformalForecaster(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20point_forecaster%3DSeasonalNaive(seasonality%3D12)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20calibration_size%3D120%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20%20%20%20%20(%0A%20%20%20%20%20%20%20%20%20%20%20%20%22conf_ridge%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20SplitConformalForecaster(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20point_forecaster%3DPointReductionForecaster(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20estimator%3DRidge()%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%206%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%20%20%20%20calibration_size%3D120%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20%20%20%20%20(%0A%20%20%20%20%20%20%20%20%20%20%20%20%22conf_gbr%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20SplitConformalForecaster(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20point_forecaster%3DPointReductionForecaster(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20estimator%3DGradientBoostingRegressor(n_estimators%3D50%2C%20random_state%3D42)%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%206%2C%2012%5D)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20reduction_strategy%3D%22direct%22%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%20%20%20%20calibration_size%3D120%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20%5D%0A%20%20%20%20return%20(base_forecasters%2C)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%203.%20Envelope%20Strategy%20(Default)%0A%0A%20%20%20%20%60method%3D%22envelope%22%60%20takes%20the%20minimum%20of%20lower%20bounds%20and%20the%20maximum%0A%20%20%20%20of%20upper%20bounds%20across%20all%20forecasters%2C%20producing%20the%20widest%0A%20%20%20%20(most%20conservative)%20interval.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(%0A%20%20%20%20VotingIntervalForecaster%2C%0A%20%20%20%20base_forecasters%2C%0A%20%20%20%20coverage%2C%0A%20%20%20%20forecasting_horizon%2C%0A%20%20%20%20y_train%2C%0A)%3A%0A%20%20%20%20ensemble_envelope%20%3D%20VotingIntervalForecaster(%0A%20%20%20%20%20%20%20%20forecasters%3Dbase_forecasters%2C%0A%20%20%20%20%20%20%20%20method%3D%22envelope%22%2C%0A%20%20%20%20)%0A%20%20%20%20ensemble_envelope.fit(%0A%20%20%20%20%20%20%20%20y_train%2C%0A%20%20%20%20%20%20%20%20forecasting_horizon%3Dforecasting_horizon%2C%0A%20%20%20%20%20%20%20%20coverage_rates%3Dcoverage%2C%0A%20%20%20%20)%0A%20%20%20%20return%20(ensemble_envelope%2C)%0A%0A%0A%40app.cell%0Adef%20_(%0A%20%20%20%20coverage%2C%0A%20%20%20%20deepcopy%2C%0A%20%20%20%20ensemble_envelope%2C%0A%20%20%20%20forecasting_horizon%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%20y_pred_envelope%20%3D%20deepcopy(ensemble_envelope).observe_predict_interval(%0A%20%20%20%20%20%20%20%20y_test%2C%0A%20%20%20%20%20%20%20%20forecasting_horizon%3Dforecasting_horizon%2C%0A%20%20%20%20%20%20%20%20coverage_rates%3Dcoverage%2C%0A%20%20%20%20)%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_envelope%2C%0A%20%20%20%20%20%20%20%20y_train%3Dy_train%2C%0A%20%20%20%20%20%20%20%20n_history%3D400%2C%0A%20%20%20%20%20%20%20%20coverage_rates%3Dcoverage%2C%0A%20%20%20%20%20%20%20%20title%3D%22Envelope%20Ensemble%20(Widest%20Coverage)%22%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.%20Mean%20Strategy%0A%0A%20%20%20%20%60method%3D%22mean%22%60%20averages%20the%20lower%20and%20upper%20bounds%20separately.%0A%20%20%20%20This%20produces%20a%20moderate%20interval%20width.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(%0A%20%20%20%20VotingIntervalForecaster%2C%0A%20%20%20%20base_forecasters%2C%0A%20%20%20%20coverage%2C%0A%20%20%20%20forecasting_horizon%2C%0A%20%20%20%20y_train%2C%0A)%3A%0A%20%20%20%20ensemble_mean%20%3D%20VotingIntervalForecaster(%0A%20%20%20%20%20%20%20%20forecasters%3Dbase_forecasters%2C%0A%20%20%20%20%20%20%20%20method%3D%22mean%22%2C%0A%20%20%20%20)%0A%20%20%20%20ensemble_mean.fit(%0A%20%20%20%20%20%20%20%20y_train%2C%0A%20%20%20%20%20%20%20%20forecasting_horizon%3Dforecasting_horizon%2C%0A%20%20%20%20%20%20%20%20coverage_rates%3Dcoverage%2C%0A%20%20%20%20)%0A%20%20%20%20return%20(ensemble_mean%2C)%0A%0A%0A%40app.cell%0Adef%20_(%0A%20%20%20%20coverage%2C%0A%20%20%20%20deepcopy%2C%0A%20%20%20%20ensemble_mean%2C%0A%20%20%20%20forecasting_horizon%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%20y_pred_mean%20%3D%20deepcopy(ensemble_mean).observe_predict_interval(%0A%20%20%20%20%20%20%20%20y_test%2C%0A%20%20%20%20%20%20%20%20forecasting_horizon%3Dforecasting_horizon%2C%0A%20%20%20%20%20%20%20%20coverage_rates%3Dcoverage%2C%0A%20%20%20%20)%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_mean%2C%0A%20%20%20%20%20%20%20%20y_train%3Dy_train%2C%0A%20%20%20%20%20%20%20%20n_history%3D400%2C%0A%20%20%20%20%20%20%20%20coverage_rates%3Dcoverage%2C%0A%20%20%20%20%20%20%20%20title%3D%22Mean%20Ensemble%22%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.%20Median%20Strategy%0A%0A%20%20%20%20%60method%3D%22median%22%60%20takes%20the%20median%20of%20lower%20and%20upper%20bounds.%0A%20%20%20%20More%20robust%20to%20outlier%20predictions%20than%20mean%20averaging.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(%0A%20%20%20%20VotingIntervalForecaster%2C%0A%20%20%20%20base_forecasters%2C%0A%20%20%20%20coverage%2C%0A%20%20%20%20forecasting_horizon%2C%0A%20%20%20%20y_train%2C%0A)%3A%0A%20%20%20%20ensemble_median%20%3D%20VotingIntervalForecaster(%0A%20%20%20%20%20%20%20%20forecasters%3Dbase_forecasters%2C%0A%20%20%20%20%20%20%20%20method%3D%22median%22%2C%0A%20%20%20%20)%0A%20%20%20%20ensemble_median.fit(%0A%20%20%20%20%20%20%20%20y_train%2C%0A%20%20%20%20%20%20%20%20forecasting_horizon%3Dforecasting_horizon%2C%0A%20%20%20%20%20%20%20%20coverage_rates%3Dcoverage%2C%0A%20%20%20%20)%0A%20%20%20%20return%20(ensemble_median%2C)%0A%0A%0A%40app.cell%0Adef%20_(%0A%20%20%20%20coverage%2C%0A%20%20%20%20deepcopy%2C%0A%20%20%20%20ensemble_median%2C%0A%20%20%20%20forecasting_horizon%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%20y_pred_median%20%3D%20deepcopy(ensemble_median).observe_predict_interval(%0A%20%20%20%20%20%20%20%20y_test%2C%0A%20%20%20%20%20%20%20%20forecasting_horizon%3Dforecasting_horizon%2C%0A%20%20%20%20%20%20%20%20coverage_rates%3Dcoverage%2C%0A%20%20%20%20)%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_median%2C%0A%20%20%20%20%20%20%20%20y_train%3Dy_train%2C%0A%20%20%20%20%20%20%20%20n_history%3D400%2C%0A%20%20%20%20%20%20%20%20coverage_rates%3Dcoverage%2C%0A%20%20%20%20%20%20%20%20title%3D%22Median%20Ensemble%22%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.%20Point%20Predictions%20from%20Interval%20Ensembles%0A%0A%20%20%20%20When%20all%20base%20forecasters%20also%20support%20%60predict()%60%2C%0A%20%20%20%20%5B%60VotingIntervalForecaster%60%5D(%2Fpages%2Fapi%2Fgenerated%2Fyohou.ensemble.voting_interval.VotingIntervalForecaster%2F)%20exposes%20a%20%60predict()%60%20method.%0A%20%20%20%20Control%20aggregation%20with%20the%20%60point_method%60%20parameter.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(%0A%20%20%20%20deepcopy%2C%0A%20%20%20%20ensemble_envelope%2C%0A%20%20%20%20forecasting_horizon%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%20y_pred_point%20%3D%20deepcopy(ensemble_envelope).observe_predict(y_test%2C%20forecasting_horizon%3Dforecasting_horizon)%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_point%2C%0A%20%20%20%20%20%20%20%20y_train%3Dy_train%2C%0A%20%20%20%20%20%20%20%20n_history%3D400%2C%0A%20%20%20%20%20%20%20%20title%3D%22Point%20Predictions%20from%20Interval%20Ensemble%22%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.%20Weighted%20Mean%20Ensemble%0A%0A%20%20%20%20Pass%20%60weights%60%20to%20give%20higher%20influence%20to%20certain%20forecasters.%0A%20%20%20%20Weights%20apply%20to%20both%20%60method%3D%22mean%22%60%20and%20%60point_method%3D%22mean%22%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%20VotingIntervalForecaster%2C%0A%20%20%20%20base_forecasters%2C%0A%20%20%20%20coverage%2C%0A%20%20%20%20forecasting_horizon%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%20ensemble_weighted%20%3D%20VotingIntervalForecaster(%0A%20%20%20%20%20%20%20%20forecasters%3Dbase_forecasters%2C%0A%20%20%20%20%20%20%20%20method%3D%22mean%22%2C%0A%20%20%20%20%20%20%20%20weights%3D%5B0.5%2C%201.0%2C%202.0%5D%2C%0A%20%20%20%20)%0A%20%20%20%20ensemble_weighted.fit(%0A%20%20%20%20%20%20%20%20y_train%2C%0A%20%20%20%20%20%20%20%20forecasting_horizon%3Dforecasting_horizon%2C%0A%20%20%20%20%20%20%20%20coverage_rates%3Dcoverage%2C%0A%20%20%20%20)%0A%20%20%20%20y_pred_weighted%20%3D%20ensemble_weighted.observe_predict_interval(%0A%20%20%20%20%20%20%20%20y_test%2C%0A%20%20%20%20%20%20%20%20forecasting_horizon%3Dforecasting_horizon%2C%0A%20%20%20%20%20%20%20%20coverage_rates%3Dcoverage%2C%0A%20%20%20%20)%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_weighted%2C%0A%20%20%20%20%20%20%20%20y_train%3Dy_train%2C%0A%20%20%20%20%20%20%20%20n_history%3D400%2C%0A%20%20%20%20%20%20%20%20coverage_rates%3Dcoverage%2C%0A%20%20%20%20%20%20%20%20title%3D%22Weighted%20Mean%20Ensemble%22%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%5BEnsemble%20Forecasting%5D(%2Fpages%2Fhow-to%2Fensemble-forecasting%2F)%20for%20the%20full%20guide%0A%20%20%20%20-%20%5BInterval%20Forecasting%20Tutorial%5D(%2Fpages%2Ftutorials%2Finterval-forecasting%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
0b7842dccd47d36fd4844e0ff24854b4