%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%20Hyperparameter%20Search%20for%20Interval%20Forecasters%0A%0A%20%20%20%20Tune%20interval%20forecaster%20parameters%20using%20interval%20metrics%0A%20%20%20%20**directly**%20in%20%5B%60GridSearchCV%60%5D(%2Fpages%2Fapi%2Fgenerated%2Fyohou.model_selection.search.GridSearchCV%2F).%0A%0A%20%20%20%20This%20notebook%20shows%20how%20to%20tune%20interval%20forecaster%20parameters%20directly%20with%20interval%20metrics%20in%20GridSearchCV%2C%20including%20mixed%20point%2Binterval%20multimetric%20search.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_()%3A%0A%20%20%20%20import%20polars%20as%20pl%0A%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.interval%20import%20SplitConformalForecaster%0A%20%20%20%20from%20yohou.metrics%20import%20(%0A%20%20%20%20%20%20%20%20EmpiricalCoverage%2C%0A%20%20%20%20%20%20%20%20IntervalScore%2C%0A%20%20%20%20%20%20%20%20MeanAbsoluteError%2C%0A%20%20%20%20%20%20%20%20MeanIntervalWidth%2C%0A%20%20%20%20)%0A%20%20%20%20from%20yohou.metrics.conformity%20import%20AbsoluteResidual%2C%20Residual%0A%20%20%20%20from%20yohou.model_selection%20import%20GridSearchCV%0A%20%20%20%20from%20yohou.model_selection.split%20import%20ExpandingWindowSplitter%0A%20%20%20%20from%20yohou.plotting%20import%20plot_forecast%2C%20plot_time_series%0A%20%20%20%20from%20yohou.point%20import%20PointReductionForecaster%0A%20%20%20%20from%20yohou.preprocessing%20import%20LagTransformer%0A%0A%20%20%20%20return%20(%0A%20%20%20%20%20%20%20%20AbsoluteResidual%2C%0A%20%20%20%20%20%20%20%20EmpiricalCoverage%2C%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%20IntervalScore%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%20MeanIntervalWidth%2C%0A%20%20%20%20%20%20%20%20PointReductionForecaster%2C%0A%20%20%20%20%20%20%20%20Residual%2C%0A%20%20%20%20%20%20%20%20Ridge%2C%0A%20%20%20%20%20%20%20%20SplitConformalForecaster%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%20plot_time_series%2C%0A%20%20%20%20)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%201.%20Prepare%20Data%0A%0A%20%20%20%20We%20load%20the%20Sunspots%20dataset%2C%20aggregate%20to%20monthly%2C%20and%20split%0A%20%20%20%20into%20training%20and%20test%20sets.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(fetch_sunspot%2C%20mo%2C%20pl)%3A%0A%20%20%20%20from%20yohou.model_selection%20import%20train_test_split%0A%0A%20%20%20%20_raw%20%3D%20fetch_sunspot().frame%0A%20%20%20%20ss%20%3D%20_raw.group_by_dynamic(%22time%22%2C%20every%3D%221mo%22).agg(pl.col(%22sunspot_number%22).mean())%0A%20%20%20%20n_test%20%3D%2024%0A%20%20%20%20y_train%2C%20y_test%20%3D%20train_test_split(ss%2C%20test_size%3Dn_test)%0A%20%20%20%20horizon%20%3D%20n_test%0A%0A%20%20%20%20mo.md(f%22**Sunspots**%3A%20%7Blen(ss)%7D%20rows%20%7C%20**Train**%3A%20%7Blen(y_train)%7D%20%7C%20**Test**%3A%20%7Blen(y_test)%7D%22)%0A%20%20%20%20return%20horizon%2C%20ss%2C%20y_test%2C%20y_train%0A%0A%0A%40app.cell%0Adef%20_(plot_time_series%2C%20ss)%3A%0A%20%20%20%20plot_time_series(ss%2C%20title%3D%22Monthly%20Sunspot%20Numbers%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%202.%20Search%20with%20Interval%20Metrics%0A%0A%20%20%20%20Pass%20an%20interval%20scorer%20like%20%5B%60IntervalScore%60%5D(%2Fpages%2Fapi%2Fgenerated%2Fyohou.metrics.interval.IntervalScore%2F)%0A%20%20%20%20to%20%5B%60GridSearchCV%60%5D(%2Fpages%2Fapi%2Fgenerated%2Fyohou.model_selection.search.GridSearchCV%2F).%20The%20search%20automatically%3A%0A%0A%20%20%20%201.%20Collects%20%60coverage_rates%60%20from%20all%20interval%20scorers%0A%20%20%20%202.%20Passes%20them%20to%20%60forecaster.fit()%60%20and%20%60observe_predict_interval()%60%0A%20%20%20%203.%20Scores%20each%20fold%20with%20interval%20predictions%0A%0A%20%20%20%20No%20manual%20%60predict_interval%60%20call%20is%20needed%20during%20the%20search.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(%0A%20%20%20%20AbsoluteResidual%2C%0A%20%20%20%20ExpandingWindowSplitter%2C%0A%20%20%20%20GridSearchCV%2C%0A%20%20%20%20IntervalScore%2C%0A%20%20%20%20LagTransformer%2C%0A%20%20%20%20PointReductionForecaster%2C%0A%20%20%20%20Residual%2C%0A%20%20%20%20Ridge%2C%0A%20%20%20%20SplitConformalForecaster%2C%0A%20%20%20%20horizon%2C%0A%20%20%20%20y_train%2C%0A)%3A%0A%20%20%20%20_base%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()%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20feature_transformer%3DLagTransformer(lag%3D%5B1%2C%2012%5D)%2C%0A%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20%20%20%20%20calibration_size%3D50%2C%0A%20%20%20%20%20%20%20%20conformity_scorer%3DResidual()%2C%0A%20%20%20%20)%0A%20%20%20%20gs_interval%20%3D%20GridSearchCV(%0A%20%20%20%20%20%20%20%20forecaster%3D_base%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%22calibration_size%22%3A%20%5B30%2C%2050%2C%2080%2C%20120%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22conformity_scorer%22%3A%20%5BResidual()%2C%20AbsoluteResidual()%5D%2C%0A%20%20%20%20%20%20%20%20%7D%2C%0A%20%20%20%20%20%20%20%20scoring%3DIntervalScore(coverage_rates%3D%5B0.9%5D)%2C%0A%20%20%20%20%20%20%20%20refit%3DTrue%2C%0A%20%20%20%20%20%20%20%20cv%3DExpandingWindowSplitter(n_splits%3D3)%2C%0A%20%20%20%20)%0A%20%20%20%20gs_interval.fit(y_train%2C%20forecasting_horizon%3Dhorizon)%0A%20%20%20%20return%20(gs_interval%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.%20Results%0A%0A%20%20%20%20The%20search%20used%20%5B%60IntervalScore%60%5D(%2Fpages%2Fapi%2Fgenerated%2Fyohou.metrics.interval.IntervalScore%2F)%20to%20rank%20parameters.%20Lower%20(more%20negative)%0A%20%20%20%20interval%20score%20means%20tighter%20intervals%20with%20good%20coverage.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(gs_interval%2C%20mo%2C%20pl)%3A%0A%20%20%20%20_raw%20%3D%20gs_interval.cv_results_%0A%20%20%20%20_safe%20%3D%20%7B%0A%20%20%20%20%20%20%20%20k%3A%20%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20str(v)%20if%20hasattr(v%2C%20%22__class__%22)%20and%20not%20isinstance(v%2C%20(int%2C%20float%2C%20str%2C%20bool%2C%20type(None)))%20else%20v%0A%20%20%20%20%20%20%20%20%20%20%20%20for%20v%20in%20vals%0A%20%20%20%20%20%20%20%20%5D%0A%20%20%20%20%20%20%20%20if%20isinstance(vals%2C%20list)%0A%20%20%20%20%20%20%20%20else%20vals%0A%20%20%20%20%20%20%20%20for%20k%2C%20vals%20in%20_raw.items()%0A%20%20%20%20%7D%0A%20%20%20%20_results%20%3D%20pl.DataFrame(_safe)%0A%20%20%20%20_cols%20%3D%20%5Bc%20for%20c%20in%20_results.columns%20if%20%22param_%22%20in%20c%20or%20%22mean_test%22%20in%20c%20or%20%22rank_test%22%20in%20c%5D%0A%20%20%20%20mo.ui.table(_results.select(_cols))%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(gs_interval%2C%20mo)%3A%0A%20%20%20%20mo.md(f%22%22%22%0A%20%20%20%20**Best%20params**%3A%20%7Bgs_interval.best_params_%7D%0A%0A%20%20%20%20**Best%20IntervalScore%20(negated)**%3A%20%7Bgs_interval.best_score_%3A.4f%7D%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%204.%20Predict%20and%20Visualize%0A%0A%20%20%20%20The%20best%20forecaster%20was%20refitted%20on%20all%20training%20data%20with%20the%0A%20%20%20%20%60coverage_rates%60%20collected%20from%20the%20scorer.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(gs_interval%2C%20horizon%2C%20plot_forecast%2C%20y_test%2C%20y_train)%3A%0A%20%20%20%20y_pred_interval%20%3D%20gs_interval.predict_interval(%0A%20%20%20%20%20%20%20%20forecasting_horizon%3Dhorizon%2C%0A%20%20%20%20%20%20%20%20coverage_rates%3D%5B0.9%5D%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_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.9%5D%2C%0A%20%20%20%20%20%20%20%20n_history%3D50%2C%0A%20%20%20%20%20%20%20%20title%3D%22Best%20Interval%20Forecaster%20(Interval%20Score%20Search)%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.%20Mixed%20Multimetric%3A%20Point%20%2B%20Interval%0A%0A%20%20%20%20You%20can%20combine%20point%20and%20interval%20scorers%20in%20a%20single%20search.%0A%20%20%20%20%5B%60GridSearchCV%60%5D(%2Fpages%2Fapi%2Fgenerated%2Fyohou.model_selection.search.GridSearchCV%2F)%20handles%20this%20transparently%3A%0A%0A%20%20%20%20-%20**Interval%20scorers**%20receive%20interval%20predictions%20(%60_lower_%60%2F%60_upper_%60%20columns)%0A%20%20%20%20-%20**Point%20scorers**%20receive%20midpoint%20estimates%20derived%20from%20the%20tightest%20interval%0A%0A%20%20%20%20Use%20%60refit%60%20to%20select%20which%20metric%20determines%20the%20best%20model.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(%0A%20%20%20%20AbsoluteResidual%2C%0A%20%20%20%20EmpiricalCoverage%2C%0A%20%20%20%20ExpandingWindowSplitter%2C%0A%20%20%20%20GridSearchCV%2C%0A%20%20%20%20IntervalScore%2C%0A%20%20%20%20LagTransformer%2C%0A%20%20%20%20MeanAbsoluteError%2C%0A%20%20%20%20PointReductionForecaster%2C%0A%20%20%20%20Residual%2C%0A%20%20%20%20Ridge%2C%0A%20%20%20%20SplitConformalForecaster%2C%0A%20%20%20%20horizon%2C%0A%20%20%20%20y_train%2C%0A)%3A%0A%20%20%20%20_base_mixed%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()%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20feature_transformer%3DLagTransformer(lag%3D%5B1%2C%2012%5D)%2C%0A%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20%20%20%20%20calibration_size%3D50%2C%0A%20%20%20%20%20%20%20%20conformity_scorer%3DResidual()%2C%0A%20%20%20%20)%0A%20%20%20%20gs_mixed%20%3D%20GridSearchCV(%0A%20%20%20%20%20%20%20%20forecaster%3D_base_mixed%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%22calibration_size%22%3A%20%5B30%2C%2050%2C%2080%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22conformity_scorer%22%3A%20%5BResidual()%2C%20AbsoluteResidual()%5D%2C%0A%20%20%20%20%20%20%20%20%7D%2C%0A%20%20%20%20%20%20%20%20scoring%3D%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%22interval_score%22%3A%20IntervalScore(coverage_rates%3D%5B0.9%5D)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22coverage%22%3A%20EmpiricalCoverage(coverage_rates%3D%5B0.9%5D)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22mae%22%3A%20MeanAbsoluteError()%2C%0A%20%20%20%20%20%20%20%20%7D%2C%0A%20%20%20%20%20%20%20%20refit%3D%22interval_score%22%2C%0A%20%20%20%20%20%20%20%20cv%3DExpandingWindowSplitter(n_splits%3D3)%2C%0A%20%20%20%20)%0A%20%20%20%20gs_mixed.fit(y_train%2C%20forecasting_horizon%3Dhorizon)%0A%20%20%20%20return%20(gs_mixed%2C)%0A%0A%0A%40app.cell%0Adef%20_(gs_mixed%2C%20mo%2C%20pl)%3A%0A%20%20%20%20_raw_m%20%3D%20gs_mixed.cv_results_%0A%20%20%20%20_safe_m%20%3D%20%7B%0A%20%20%20%20%20%20%20%20k%3A%20%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20str(v)%20if%20hasattr(v%2C%20%22__class__%22)%20and%20not%20isinstance(v%2C%20(int%2C%20float%2C%20str%2C%20bool%2C%20type(None)))%20else%20v%0A%20%20%20%20%20%20%20%20%20%20%20%20for%20v%20in%20vals%0A%20%20%20%20%20%20%20%20%5D%0A%20%20%20%20%20%20%20%20if%20isinstance(vals%2C%20list)%0A%20%20%20%20%20%20%20%20else%20vals%0A%20%20%20%20%20%20%20%20for%20k%2C%20vals%20in%20_raw_m.items()%0A%20%20%20%20%7D%0A%20%20%20%20_results_m%20%3D%20pl.DataFrame(_safe_m)%0A%20%20%20%20_cols_m%20%3D%20%5Bc%20for%20c%20in%20_results_m.columns%20if%20%22param_%22%20in%20c%20or%20%22mean_test%22%20in%20c%20or%20%22rank_test%22%20in%20c%5D%0A%20%20%20%20mo.ui.table(_results_m.select(_cols_m))%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(gs_mixed%2C%20mo)%3A%0A%20%20%20%20mo.md(f%22%22%22%0A%20%20%20%20**Best%20params%20(by%20interval%20score)**%3A%20%7Bgs_mixed.best_params_%7D%0A%0A%20%20%20%20**Best%20IntervalScore**%3A%20%7Bgs_mixed.best_score_%3A.4f%7D%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%206.%20Evaluate%20Interval%20Width%0A%0A%20%20%20%20After%20selecting%20the%20best%20model%2C%20evaluate%20coverage%20and%20width%20on%20the%0A%20%20%20%20held-out%20test%20set.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(%0A%20%20%20%20EmpiricalCoverage%2C%0A%20%20%20%20MeanIntervalWidth%2C%0A%20%20%20%20gs_mixed%2C%0A%20%20%20%20horizon%2C%0A%20%20%20%20mo%2C%0A%20%20%20%20y_test%2C%0A%20%20%20%20y_train%2C%0A)%3A%0A%20%20%20%20y_pred_mixed%20%3D%20gs_mixed.predict_interval(%0A%20%20%20%20%20%20%20%20forecasting_horizon%3Dhorizon%2C%0A%20%20%20%20%20%20%20%20coverage_rates%3D%5B0.9%5D%2C%0A%20%20%20%20)%0A%20%20%20%20_cov%20%3D%20EmpiricalCoverage(coverage_rates%3D%5B0.9%5D)%0A%20%20%20%20_width%20%3D%20MeanIntervalWidth(coverage_rates%3D%5B0.9%5D)%0A%20%20%20%20_cov.fit(y_train)%0A%20%20%20%20_width.fit(y_train)%0A%0A%20%20%20%20mo.md(f%22%22%22%0A%20%20%20%20-%20**Empirical%20coverage%20(90%25)**%3A%20%7Bfloat(_cov.score(y_test%2C%20y_pred_mixed))%3A.3f%7D%0A%20%20%20%20-%20**Mean%20interval%20width**%3A%20%7Bfloat(_width.score(y_test%2C%20y_pred_mixed))%3A.1f%7D%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%20Next%20Steps%0A%0A%20%20%20%20-%20%5BInterval%20Forecasting%5D(%2Fpages%2Fhow-to%2Finterval-forecasting%2F)%20for%20the%20full%20guide%0A%20%20%20%20-%20%5BEvaluate%20Forecast%20Accuracy%5D(%2Fpages%2Fhow-to%2Fevaluate-forecast-accuracy%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
e6d02daf740733cb3068ed49572f2924