%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%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%20Create%20a%20Custom%20Scorer%0A%0A%20%20%20%20This%20notebook%20shows%20how%20to%20implement%20a%20custom%20point%20scorer%20by%20extending%0A%20%20%20%20%5B%60BasePointScorer%60%5D(%2Fpages%2Fapi%2Fgenerated%2Fyohou.metrics.base.BasePointScorer%2F)%2C%0A%20%20%20%20integrating%20with%20Yohou's%20aggregation%2C%20panel%20dispatch%2C%20and%0A%20%20%20%20cross-validation%20machinery.%0A%0A%20%20%20%20**Prerequisites%3A**%20Familiarity%20with%20the%20built-in%20scorers%0A%20%20%20%20(%5BPoint%20Metrics%5D(%2Fexamples%2Fevaluation-search%2Fpoint_metrics%2F)%20%C2%B7%20%5BEvaluate%20Forecast%20Accuracy%5D(%2Fpages%2Fhow-to%2Fevaluate-forecast-accuracy%2F)).%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_tourism_monthly%0A%20%20%20%20from%20yohou.metrics%20import%20MeanAbsoluteError%0A%20%20%20%20from%20yohou.metrics.base%20import%20BasePointScorer%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%20BasePointScorer%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%20SeasonalNaive%2C%0A%20%20%20%20%20%20%20%20fetch_tourism_monthly%2C%0A%20%20%20%20%20%20%20%20pl%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.%20Define%20the%20Scorer%0A%0A%20%20%20%20Subclass%20%5B%60BasePointScorer%60%5D(%2Fpages%2Fapi%2Fgenerated%2Fyohou.metrics.base.BasePointScorer%2F)%20and%20implement%20%60score%60.%20The%20method%20validates%0A%20%20%20%20inputs%2C%20computes%20per-timestep%20raw%20scores%2C%20and%20delegates%20aggregation%0A%20%20%20%20to%20the%20base%20class.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(BasePointScorer%2C%20pl)%3A%0A%20%20%20%20class%20MaxAbsoluteError(BasePointScorer)%3A%0A%20%20%20%20%20%20%20%20%22%22%22Maximum%20absolute%20error%20across%20the%20forecast%20horizon.%22%22%22%0A%0A%20%20%20%20%20%20%20%20_parameter_constraints%3A%20dict%20%3D%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20**BasePointScorer._parameter_constraints%2C%0A%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20_metric_name%20%3D%20%22max_ae%22%0A%0A%20%20%20%20%20%20%20%20def%20__init__(%0A%20%20%20%20%20%20%20%20%20%20%20%20self%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20aggregation_method%3D%22all%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20groups%3DNone%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20components%3DNone%2C%0A%20%20%20%20%20%20%20%20)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20super().__init__(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20aggregation_method%3Daggregation_method%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20groups%3Dgroups%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20components%3Dcomponents%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20)%0A%0A%20%20%20%20%20%20%20%20def%20_compute_raw_errors(self%2C%20y_truth%2C%20y_pred)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20(y_truth%20-%20y_pred).select(pl.all().abs())%0A%0A%20%20%20%20return%20(MaxAbsoluteError%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%202.%20Prepare%20Test%20Data%0A%0A%20%20%20%20Fit%20two%20forecasters%20on%20the%20Tourism%20Monthly%20dataset%20to%20produce%0A%20%20%20%20predictions%20for%20scoring.%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%20SeasonalNaive%2C%0A%20%20%20%20fetch_tourism_monthly%2C%0A)%3A%0A%20%20%20%20y%20%3D%20(%0A%20%20%20%20%20%20%20%20fetch_tourism_monthly()%0A%20%20%20%20%20%20%20%20.frame.select(%22time%22%2C%20%22T1__tourists%22)%0A%20%20%20%20%20%20%20%20.drop_nulls()%0A%20%20%20%20%20%20%20%20.rename(%7B%22T1__tourists%22%3A%20%22tourists%22%7D)%0A%20%20%20%20)%0A%0A%20%20%20%20from%20yohou.model_selection%20import%20train_test_split%0A%0A%20%20%20%20y_train%2C%20y_test%20%3D%20train_test_split(y%2C%20test_size%3D0.2)%0A%20%20%20%20fh%20%3D%20len(y_test)%0A%0A%20%20%20%20naive%20%3D%20SeasonalNaive(seasonality%3D12)%0A%20%20%20%20naive.fit(y_train%2C%20forecasting_horizon%3Dfh)%0A%20%20%20%20y_pred_naive%20%3D%20naive.predict(forecasting_horizon%3Dfh)%0A%0A%20%20%20%20ridge_fc%20%3D%20PointReductionForecaster(%0A%20%20%20%20%20%20%20%20estimator%3DRidge()%2C%0A%20%20%20%20%20%20%20%20feature_transformer%3DLagTransformer(lag%3Dlist(range(1%2C%2013)))%2C%0A%20%20%20%20)%0A%20%20%20%20ridge_fc.fit(y_train%2C%20forecasting_horizon%3Dfh)%0A%20%20%20%20y_pred_ridge%20%3D%20ridge_fc.predict(forecasting_horizon%3Dfh)%0A%0A%20%20%20%20print(f%22Train%3A%20%7Blen(y_train)%7D%2C%20Test%3A%20%7Blen(y_test)%7D%22)%0A%20%20%20%20return%20y_pred_naive%2C%20y_pred_ridge%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%203.%20Score%20with%20the%20Custom%20Metric%0A%0A%20%20%20%20Use%20the%20same%20%60fit%60%20%2F%20%60score%60%20pattern%20as%20built-in%20scorers.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(MaxAbsoluteError%2C%20y_pred_naive%2C%20y_pred_ridge%2C%20y_test%2C%20y_train)%3A%0A%20%20%20%20max_ae%20%3D%20MaxAbsoluteError()%0A%20%20%20%20max_ae.fit(y_train)%0A%0A%20%20%20%20print(f%22MaxAE%20%20Naive%3A%20%7Bmax_ae.score(y_test%2C%20y_pred_naive)%3A.2f%7D%22)%0A%20%20%20%20print(f%22MaxAE%20%20Ridge%3A%20%7Bmax_ae.score(y_test%2C%20y_pred_ridge)%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%204.%20Use%20Aggregation%20Modes%0A%0A%20%20%20%20All%20standard%20aggregation%20modes%20work%20automatically%3A%20%60%22stepwise%22%60%2C%0A%20%20%20%20%60%22vintagewise%22%60%2C%20%60%22componentwise%22%60%2C%20and%20%60%22all%22%60.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(MaxAbsoluteError%2C%20y_pred_naive%2C%20y_test%2C%20y_train)%3A%0A%20%20%20%20stepwise%20%3D%20MaxAbsoluteError(aggregation_method%3D%22stepwise%22)%0A%20%20%20%20stepwise.fit(y_train)%0A%20%20%20%20stepwise.score(y_test%2C%20y_pred_naive)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(MaxAbsoluteError%2C%20y_pred_naive%2C%20y_test%2C%20y_train)%3A%0A%20%20%20%20componentwise%20%3D%20MaxAbsoluteError(aggregation_method%3D%22componentwise%22)%0A%20%20%20%20componentwise.fit(y_train)%0A%20%20%20%20componentwise.score(y_test%2C%20y_pred_naive)%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.%20Compare%20Against%20Built-in%20Metrics%0A%0A%20%20%20%20The%20custom%20scorer%20plugs%20into%20the%20same%20comparison%20workflows%20as%0A%20%20%20%20built-in%20metrics.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(%0A%20%20%20%20MaxAbsoluteError%2C%0A%20%20%20%20MeanAbsoluteError%2C%0A%20%20%20%20y_pred_naive%2C%0A%20%20%20%20y_pred_ridge%2C%0A%20%20%20%20y_test%2C%0A%20%20%20%20y_train%2C%0A)%3A%0A%20%20%20%20scorers%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%22MaxAE%22%3A%20MaxAbsoluteError()%2C%0A%20%20%20%20%7D%0A%0A%20%20%20%20for%20name%2C%20scorer%20in%20scorers.items()%3A%0A%20%20%20%20%20%20%20%20scorer.fit(y_train)%0A%20%20%20%20%20%20%20%20naive_score%20%3D%20scorer.score(y_test%2C%20y_pred_naive)%0A%20%20%20%20%20%20%20%20ridge_score%20%3D%20scorer.score(y_test%2C%20y_pred_ridge)%0A%20%20%20%20%20%20%20%20print(f%22%7Bname%3A%3C6%7D%20%20Naive%3A%20%7Bnaive_score%3A8.2f%7D%20%20Ridge%3A%20%7Bridge_score%3A8.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%206.%20Use%20in%20Cross-Validation%0A%0A%20%20%20%20Custom%20scorers%20work%20directly%20with%20%5B%60GridSearchCV%60%5D(%2Fpages%2Fapi%2Fgenerated%2Fyohou.model_selection.search.GridSearchCV%2F)%20and%0A%20%20%20%20%5B%60RandomizedSearchCV%60%5D(%2Fpages%2Fapi%2Fgenerated%2Fyohou.model_selection.search.RandomizedSearchCV%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%20LagTransformer%2C%0A%20%20%20%20MaxAbsoluteError%2C%0A%20%20%20%20PointReductionForecaster%2C%0A%20%20%20%20Ridge%2C%0A%20%20%20%20y_train%2C%0A)%3A%0A%20%20%20%20from%20yohou.model_selection%20import%20ExpandingWindowSplitter%2C%20GridSearchCV%0A%0A%20%20%20%20search%20%3D%20GridSearchCV(%0A%20%20%20%20%20%20%20%20forecaster%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%3Dlist(range(1%2C%2013)))%2C%0A%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20%20%20%20%20param_grid%3D%7B%22estimator__alpha%22%3A%20%5B0.1%2C%201.0%2C%2010.0%5D%7D%2C%0A%20%20%20%20%20%20%20%20scoring%3DMaxAbsoluteError()%2C%0A%20%20%20%20%20%20%20%20cv%3DExpandingWindowSplitter(n_splits%3D3%2C%20test_size%3Dlen(y_train)%20%2F%2F%205)%2C%0A%20%20%20%20)%0A%20%20%20%20search.fit(y_train%2C%20forecasting_horizon%3Dlen(y_train)%20%2F%2F%205)%0A%0A%20%20%20%20print(f%22Best%20alpha%3A%20%7Bsearch.best_params_%5B'estimator__alpha'%5D%7D%22)%0A%20%20%20%20print(f%22Best%20MaxAE%3A%20%7Bsearch.best_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%20Next%20Steps%0A%0A%20%20%20%20-%20%5BCreate%20a%20Custom%20Scorer%5D(%2Fpages%2Fhow-to%2Fcreate-a-scorer%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
73c2cf7c72e52141cf4d4ae00ec203e8