%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%20Scorer%20Aggregation%20Modes%0A%0A%20%20%20%20Yohou%20scorers%20support%20multiple%20**aggregation%20strategies**%20that%20control%20how%0A%20%20%20%20per-timestep%2C%20per-component%20errors%20are%20reduced%20into%20summary%20scores.%0A%20%20%20%20These%20strategies%20apply%20to%20any%20scorer%20that%20extends%0A%20%20%20%20%5B%60BasePointScorer%60%5D(%2Fpages%2Fapi%2Fgenerated%2Fyohou.metrics.base.BasePointScorer%2F)%20or%0A%20%20%20%20%5B%60BaseIntervalScorer%60%5D(%2Fpages%2Fapi%2Fgenerated%2Fyohou.metrics.base.BaseIntervalScorer%2F).%0A%0A%20%20%20%20%23%23%201.%20Prepare%20Data%20and%20Predictions%0A%0A%20%20%20%20We%20load%20the%20**KDD%20Cup%202018**%20air%20quality%20dataset%2C%20a%20multivariate%20panel%0A%20%20%20%20with%203%20Beijing%20stations%2C%20each%20monitoring%206%20pollutants%20(PM2.5%2C%20PM10%2C%20NO2%2C%0A%20%20%20%20CO%2C%20O3%2C%20SO2).%20This%20structure%20with%20**multiple%20members%20per%20group**%20is%20key%0A%20%20%20%20to%20showing%20the%20difference%20between%20componentwise%20and%20groupwise%20aggregation.%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%20from%20sklearn.linear_model%20import%20Ridge%0A%0A%20%20%20%20from%20yohou.datasets%20import%20fetch_kdd_cup%0A%20%20%20%20from%20yohou.interval%20import%20SplitConformalForecaster%0A%20%20%20%20from%20yohou.metrics%20import%20EmpiricalCoverage%2C%20MeanAbsoluteError%0A%20%20%20%20from%20yohou.model_selection%20import%20train_test_split%0A%20%20%20%20from%20yohou.plotting%20import%20plot_score_heatmap%2C%20plot_time_series%0A%20%20%20%20from%20yohou.point%20import%20PointReductionForecaster%0A%20%20%20%20from%20yohou.preprocessing%20import%20LagTransformer%0A%20%20%20%20from%20yohou.utils.panel%20import%20inspect_panel%0A%0A%20%20%20%20return%20(%0A%20%20%20%20%20%20%20%20EmpiricalCoverage%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%20PointReductionForecaster%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%20deepcopy%2C%0A%20%20%20%20%20%20%20%20fetch_kdd_cup%2C%0A%20%20%20%20%20%20%20%20inspect_panel%2C%0A%20%20%20%20%20%20%20%20plot_score_heatmap%2C%0A%20%20%20%20%20%20%20%20plot_time_series%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_(%0A%20%20%20%20LagTransformer%2C%0A%20%20%20%20PointReductionForecaster%2C%0A%20%20%20%20Ridge%2C%0A%20%20%20%20fetch_kdd_cup%2C%0A%20%20%20%20inspect_panel%2C%0A%20%20%20%20mo%2C%0A%20%20%20%20plot_time_series%2C%0A%20%20%20%20train_test_split%2C%0A)%3A%0A%20%20%20%20_bunch%20%3D%20fetch_kdd_cup(n_groups%3D3)%0A%20%20%20%20_y%20%3D%20_bunch.frame.drop_nulls().tail(250)%0A%0A%20%20%20%20fh%20%3D%2024%0A%20%20%20%20y_train%2C%20y_test%20%3D%20train_test_split(_y%2C%20test_size%3Dfh)%0A%0A%20%20%20%20_%2C%20groups%20%3D%20inspect_panel(_y)%0A%0A%20%20%20%20_fc%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%3DLagTransformer(lag%3D%5B1%2C%2024%5D)%2C%0A%20%20%20%20)%0A%20%20%20%20_fc.fit(y_train%2C%20forecasting_horizon%3Dfh)%0A%20%20%20%20y_pred%20%3D%20_fc.predict(forecasting_horizon%3Dfh)%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**Panel%20structure**%3A%20%7Blen(groups)%7D%20groups%20%C3%97%20%22%0A%20%20%20%20%20%20%20%20%20%20%20%20f%22%7Blen(next(iter(groups.values())))%7D%20members%20%3D%20%22%0A%20%20%20%20%20%20%20%20%20%20%20%20f%22%7Bsum(len(v)%20for%20v%20in%20groups.values())%7D%20columns%5Cn%5Cn%22%0A%20%20%20%20%20%20%20%20%20%20%20%20f%22**Groups**%3A%20%22%0A%20%20%20%20%20%20%20%20%20%20%20%20f%22%7B%20%7Bk%3A%20%5Bc.split('__')%5B1%5D%20for%20c%20in%20v%5D%20for%20k%2C%20v%20in%20groups.items()%7D%20%7D%5Cn%5Cn%22%0A%20%20%20%20%20%20%20%20%20%20%20%20f%22**Train**%3A%20%7Blen(y_train)%7D%20hours%20%C2%B7%20**Test**%3A%20%7Bfh%7D%20hours%22%0A%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20%20%20%20%20plot_time_series(_y%2C%20title%3D%22KDD%20Cup%202018%20-%20Three%20Beijing%20Stations%22)%2C%0A%20%20%20%20%5D)%0A%20%20%20%20return%20fh%2C%20groups%2C%20y_pred%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.%20%60%22all%22%60%3A%20Single%20Scalar%0A%0A%20%20%20%20The%20default%20mode.%20Every%20dimension%20(time%2C%20columns%2C%20groups)%20is%20aggregated%0A%20%20%20%20into%20a%20single%20number.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(MeanAbsoluteError%2C%20mo%2C%20y_pred%2C%20y_test%2C%20y_train)%3A%0A%20%20%20%20_scorer%20%3D%20MeanAbsoluteError(aggregation_method%3D%22all%22)%0A%20%20%20%20_scorer.fit(y_train)%0A%20%20%20%20_score%20%3D%20_scorer.score(y_test%2C%20y_pred)%0A%20%20%20%20mo.md(f%22**MAE%20(%60'all'%60)**%3A%20%7Bfloat(_score)%3A.2f%7D%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%203.%20%60%5B%22stepwise%22%2C%20%22vintagewise%22%5D%60%3A%20Per-Column%20Average%0A%0A%20%20%20%20Aggregate%20over%20time%20%E2%86%92%20one%20score%20per%20column.%20Shows%20which%20individual%0A%20%20%20%20series%20are%20hardest%20to%20forecast.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(MeanAbsoluteError%2C%20mo%2C%20y_pred%2C%20y_test%2C%20y_train)%3A%0A%20%20%20%20_scorer_tw%20%3D%20MeanAbsoluteError(aggregation_method%3D%5B%22stepwise%22%2C%20%22vintagewise%22%5D)%0A%20%20%20%20_scorer_tw.fit(y_train)%0A%20%20%20%20_score_tw%20%3D%20_scorer_tw.score(y_test%2C%20y_pred)%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**Shape**%3A%20%7B_score_tw.shape%7D%20i.e.%20one%20row%2C%20one%20column%20per%20series%5Cn%5Cn%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%22Each%20value%20is%20the%20mean%20absolute%20error%20for%20that%20column%20across%20all%20%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%22test%20timesteps.%22%0A%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20%20%20%20%20_score_tw%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.%20%60%22componentwise%22%60%3A%20Per-Group%20Over%20Time%0A%0A%20%20%20%20Aggregate%20**members%20within%20each%20panel%20group**%20at%20each%20timestep.%20Since%0A%20%20%20%20each%20region%20has%20two%20series%20(%60demand%60%20and%20%60temperature%60)%2C%20their%20errors%20are%0A%20%20%20%20averaged%20into%20a%20single%20per-group%20score.%0A%0A%20%20%20%20This%20is%20where%20having%20**multivariate%20groups**%20matters%3A%20the%20six%20pollutant%0A%20%20%20%20errors%20within%20each%20station%20are%20collapsed%20into%20one%20%60%7Bstation%7D__score%60%0A%20%20%20%20column.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(MeanAbsoluteError%2C%20mo%2C%20y_pred%2C%20y_test%2C%20y_train)%3A%0A%20%20%20%20_scorer_cw%20%3D%20MeanAbsoluteError(aggregation_method%3D%22componentwise%22)%0A%20%20%20%20_scorer_cw.fit(y_train)%0A%20%20%20%20_score_cw%20%3D%20_scorer_cw.score(y_test%2C%20y_pred)%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**Shape**%3A%20%7B_score_cw.shape%7D%20i.e.%20%22%0A%20%20%20%20%20%20%20%20%20%20%20%20f%22one%20row%20per%20timestep%2C%20one%20column%20per%20**group**%5Cn%5Cn%22%0A%20%20%20%20%20%20%20%20%20%20%20%20f%22**Columns**%3A%20%7B_score_cw.columns%7D%5Cn%5Cn%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%22Note%20the%20%60__score%60%20suffix%3A%20pollutant%20errors%20within%20each%20%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%22station%20were%20averaged.%22%0A%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20%20%20%20%20_score_cw.head(5)%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.%20%60%22groupwise%22%60%3A%20Per-Component%20Over%20Time%0A%0A%20%20%20%20Aggregates%20**across%20panel%20groups**%20for%20each%20component%20at%20each%20timestep.%0A%20%20%20%20Since%20each%20station%20monitors%206%20pollutants%2C%20the%203%20station-specific%20errors%0A%20%20%20%20for%20each%20pollutant%20are%20averaged%20into%20a%20single%20%60%7Bpollutant%7D%60%20column.%0A%0A%20%20%20%20Compare%20with%20componentwise%20(section%204)%3A%20componentwise%20aggregates%0A%20%20%20%20*within*%20groups%20(pollutants%20%E2%86%92%20stations)%2C%20groupwise%20aggregates%20*across*%0A%20%20%20%20groups%20(stations%20%E2%86%92%20pollutants).%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(MeanAbsoluteError%2C%20mo%2C%20y_pred%2C%20y_test%2C%20y_train)%3A%0A%20%20%20%20_scorer_gw%20%3D%20MeanAbsoluteError(aggregation_method%3D%22groupwise%22)%0A%20%20%20%20_scorer_gw.fit(y_train)%0A%20%20%20%20_score_gw%20%3D%20_scorer_gw.score(y_test%2C%20y_pred)%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**Shape**%3A%20%7B_score_gw.shape%7D%20i.e.%20%22%0A%20%20%20%20%20%20%20%20%20%20%20%20f%22one%20row%20per%20timestep%2C%20one%20column%20per%20**component**%5Cn%5Cn%22%0A%20%20%20%20%20%20%20%20%20%20%20%20f%22**Columns**%3A%20%7B_score_gw.columns%7D%5Cn%5Cn%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%22Compare%20with%20componentwise%20above%3A%20componentwise%20collapses%20%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%22pollutants%20within%20each%20station%20(18%20%E2%86%92%203%20groups)%2C%20groupwise%20%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%22collapses%20stations%20for%20each%20pollutant%20(18%20%E2%86%92%206%20components).%22%0A%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20%20%20%20%20_score_gw.head(5)%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%206.%20Combining%20Modes%0A%0A%20%20%20%20Pass%20a%20list%20to%20aggregate%20over%20multiple%20dimensions%20at%20once.%0A%20%20%20%20%60%5B%22stepwise%22%2C%20%22vintagewise%22%2C%20%22componentwise%22%5D%60%20removes%20both%20time%20and%20member%0A%20%20%20%20dimensions%2C%20producing%20a%20single%20scalar%20which%20is%20equivalent%20to%20a%20flat%20mean%0A%20%20%20%20over%20all%20errors.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(MeanAbsoluteError%2C%20mo%2C%20y_pred%2C%20y_test%2C%20y_train)%3A%0A%20%20%20%20_scorer_tc%20%3D%20MeanAbsoluteError(aggregation_method%3D%5B%22stepwise%22%2C%20%22vintagewise%22%2C%20%22componentwise%22%2C%20%22groupwise%22%5D)%0A%20%20%20%20_scorer_tc.fit(y_train)%0A%20%20%20%20_score_tc%20%3D%20_scorer_tc.score(y_test%2C%20y_pred)%0A%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20f%22**%60%5B'stepwise'%2C%20'vintagewise'%2C%20'componentwise'%2C%20'groupwise'%5D%60**%20%E2%86%92%20scalar%3A%20%22%0A%20%20%20%20%20%20%20%20f%22%7Bfloat(_score_tc)%3A.2f%7D%5Cn%5Cn%22%0A%20%20%20%20%20%20%20%20%22Both%20time%20and%20member%20dimensions%20are%20aggregated%2C%20collapsing%20%22%0A%20%20%20%20%20%20%20%20%22everything%20into%20a%20single%20number.%22%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%20Group%20Aggregation%20via%20%60groups%60%0A%0A%20%20%20%20Pass%20%60groups%60%20as%20a%20dictionary%20of%20%60%7Bname%3A%20weight%7D%60%20to%20emphasise%20certain%0A%20%20%20%20stations%20during%20%60%22all%22%60%20aggregation.%20This%20is%20useful%20when%20some%20monitoring%0A%20%20%20%20stations%20are%20more%20critical%20than%20others.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(MeanAbsoluteError%2C%20groups%2C%20mo%2C%20y_pred%2C%20y_test%2C%20y_train)%3A%0A%20%20%20%20_group_names%20%3D%20sorted(groups.keys())%0A%20%20%20%20_weights%20%3D%20%7B_group_names%5B0%5D%3A%205.0%7D%0A%20%20%20%20_scorer_w%20%3D%20MeanAbsoluteError(aggregation_method%3D%22all%22%2C%20groups%3D_weights)%0A%20%20%20%20_scorer_u%20%3D%20MeanAbsoluteError(aggregation_method%3D%22all%22)%0A%0A%20%20%20%20_scorer_w.fit(y_train)%0A%20%20%20%20_scorer_u.fit(y_train)%0A%0A%20%20%20%20_s_w%20%3D%20_scorer_w.score(y_test%2C%20y_pred)%0A%20%20%20%20_s_u%20%3D%20_scorer_u.score(y_test%2C%20y_pred)%0A%0A%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20f%22**Weights**%3A%20%60%7B_weights%7D%60%20(others%20default%20to%201.0)%5Cn%5Cn%22%0A%20%20%20%20%20%20%20%20f%22**Unweighted%20MAE**%3A%20%7Bfloat(_s_u)%3A.2f%7D%5Cn%5Cn%22%0A%20%20%20%20%20%20%20%20f%22**Weighted%20MAE**%3A%20%7Bfloat(_s_w)%3A.2f%7D%5Cn%5Cn%22%0A%20%20%20%20%20%20%20%20%22The%20score%20shifts%20toward%20the%20heavily-weighted%20group.%22%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.%20%60%22coveragewise%22%60%3A%20Interval%20Scorer%20Aggregation%0A%0A%20%20%20%20Interval%20scorers%20score%20each%20**coverage%20rate**%20separately.%20This%20creates%20an%0A%20%20%20%20extra%20dimension%20in%20the%20output.%0A%0A%20%20%20%20-%20**Without**%20%60%22coveragewise%22%60%3A%20scores%20are%20returned%20per%20rate%20(as%20a%20DataFrame%0A%20%20%20%20%20%20with%20a%20%60coverage_rate%60%20column)%0A%20%20%20%20-%20**With**%20%60%22coveragewise%22%60%3A%20coverage%20rates%20are%20averaged%2C%20collapsing%20that%0A%20%20%20%20%20%20dimension%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%20LagTransformer%2C%0A%20%20%20%20PointReductionForecaster%2C%0A%20%20%20%20Ridge%2C%0A%20%20%20%20SplitConformalForecaster%2C%0A%20%20%20%20fh%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%20_coverage_rates%20%3D%20%5B0.8%2C%200.9%2C%200.95%5D%0A%0A%20%20%20%20_fc_int%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%3D1.0)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20feature_transformer%3DLagTransformer(lag%3D%5B1%2C%207%5D)%2C%0A%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20%20%20%20%20calibration_size%3Dfh%20%2B%2024%2C%0A%20%20%20%20)%0A%20%20%20%20_fc_int.fit(y_train%2C%20forecasting_horizon%3Dfh%2C%20coverage_rates%3D_coverage_rates)%0A%20%20%20%20_y_pred_int%20%3D%20_fc_int.predict_interval(forecasting_horizon%3Dfh%2C%20coverage_rates%3D_coverage_rates)%0A%0A%20%20%20%20_cov_per_rate%20%3D%20EmpiricalCoverage(aggregation_method%3D%5B%22stepwise%22%2C%20%22vintagewise%22%2C%20%22componentwise%22%5D)%0A%20%20%20%20_cov_all%20%3D%20EmpiricalCoverage(aggregation_method%3D%22all%22)%0A%0A%20%20%20%20_cov_per_rate.fit(y_train)%0A%20%20%20%20_cov_all.fit(y_train)%0A%0A%20%20%20%20_s_per_rate%20%3D%20_cov_per_rate.score(y_test%2C%20_y_pred_int)%0A%20%20%20%20_s_all%20%3D%20_cov_all.score(y_test%2C%20_y_pred_int)%0A%0A%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20f%22**Coverage%20rates**%3A%20%7B_coverage_rates%7D%5Cn%5Cn%22%0A%20%20%20%20%20%20%20%20%22---%5Cn%5Cn%22%0A%20%20%20%20%20%20%20%20%22**Without%20%60'coveragewise'%60**%20%22%0A%20%20%20%20%20%20%20%20f%22(%60%5B'stepwise'%2C%20'vintagewise'%2C%20'componentwise'%5D%60)%20%E2%86%92%20per-rate%20DataFrame%3A%5Cn%5Cn%22%0A%20%20%20%20%20%20%20%20%2B%20%22%5Cn%5Cn%22.join(%0A%20%20%20%20%20%20%20%20%20%20%20%20f%22-%20rate%20%7Brow%5B'coverage_rate'%5D%7D%3A%20%7Brow%5B_s_per_rate.columns%5B1%5D%5D%3A.3f%7D%22%0A%20%20%20%20%20%20%20%20%20%20%20%20for%20row%20in%20_s_per_rate.iter_rows(named%3DTrue)%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20%2B%20%22%5Cn%5Cn---%5Cn%5Cn%22%0A%20%20%20%20%20%20%20%20f%22**With%20%60'coveragewise'%60**%20(%60'all'%60)%20%E2%86%92%20scalar%3A%20%22%0A%20%20%20%20%20%20%20%20f%22%7Bfloat(_s_all)%3A.3f%7D%5Cn%5Cn%22%0A%20%20%20%20%20%20%20%20%22The%20per-rate%20DataFrame%20shows%20that%20wider%20intervals%20(0.95)%20achieve%20higher%20%22%0A%20%20%20%20%20%20%20%20%22coverage%20than%20narrow%20ones%20(0.8)%2C%20as%20expected.%20Adding%20%60'coveragewise'%60%20%22%0A%20%20%20%20%20%20%20%20%22averages%20across%20rates%20into%20a%20single%20number.%22%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%20Multi-vintage%20Scoring%0A%0A%20%20%20%20Using%20%60observe_predict%60%20with%20%60stride%3D1%60%2C%20we%20create%20multiple%20vintages%20and%0A%20%20%20%20visualise%20the%20score%20matrix%20as%20a%20heatmap.%20Each%20cell%20shows%20the%20error%20for%20a%0A%20%20%20%20specific%20(step%2C%20vintage)%20combination.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(%0A%20%20%20%20LagTransformer%2C%0A%20%20%20%20PointReductionForecaster%2C%0A%20%20%20%20Ridge%2C%0A%20%20%20%20deepcopy%2C%0A%20%20%20%20fh%2C%0A%20%20%20%20y_test%2C%0A%20%20%20%20y_train%2C%0A)%3A%0A%20%20%20%20_fc_v%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%3DLagTransformer(lag%3D%5B1%2C%202%2C%203%5D)%2C%0A%20%20%20%20)%0A%20%20%20%20_fc_v.fit(y_train%2C%20forecasting_horizon%3Dfh)%0A%20%20%20%20_fc_v2%20%3D%20deepcopy(_fc_v)%0A%20%20%20%20y_pred_vintages%20%3D%20_fc_v2.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%3Dfh%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%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_heatmap%2C%20vintage_scorer%2C%20y_pred_vintages%2C%20y_test)%3A%0A%20%20%20%20plot_score_heatmap(%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%22Score%20Heatmap%20(Step%20x%20Vintage)%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%5BEvaluate%20Forecast%20Accuracy%5D(%2Fpages%2Fhow-to%2Fevaluate-forecast-accuracy%2F)%20for%20the%20full%20guide%0A%20%20%20%20-%20%5BCreate%20a%20Custom%20Scorer%5D(%2Fpages%2Fhow-to%2Fcreate-a-scorer%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
5b45438e4f7cbb98ab239e4775312abc