%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%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%20Create%20a%20Custom%20Estimator%0A%0A%20%20%20%20This%20notebook%20implements%20a%20%60LastValueForecaster%60%20from%20scratch%2C%0A%20%20%20%20validates%20it%20using%20the%20built-in%20check%20generator%2C%20and%20compares%0A%20%20%20%20its%20performance%20against%20%5B%60SeasonalNaive%60%5D(%2Fpages%2Fapi%2Fgenerated%2Fyohou.point.naive.SeasonalNaive%2F).%0A%0A%20%20%20%20**Prerequisites%3A**%20Familiarity%20with%20the%20fit%2Fpredict%20API%0A%20%20%20%20(%5BGetting%20Started%5D(%2Fpages%2Ftutorials%2Fgetting-started%2F)).%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%201.%20Implement%20the%20Forecaster%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_()%3A%0A%20%20%20%20import%20polars%20as%20pl%0A%20%20%20%20import%20polars.selectors%20as%20cs%0A%0A%20%20%20%20from%20yohou.point.base%20import%20BasePointForecaster%0A%0A%20%20%20%20class%20LastValueForecaster(BasePointForecaster)%3A%0A%20%20%20%20%20%20%20%20%22%22%22Repeats%20the%20last%20observed%20value%20for%20every%20forecast%20step.%22%22%22%0A%0A%20%20%20%20%20%20%20%20_tags%20%3D%20%7B%22ignores_exogenous%22%3A%20True%2C%20%22stateful%22%3A%20True%7D%0A%0A%20%20%20%20%20%20%20%20%40property%0A%20%20%20%20%20%20%20%20def%20_observation_horizon(self)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20return%201%0A%0A%20%20%20%20%20%20%20%20def%20_predict_one(self%2C%20groups%2C%20**params)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20last_value%20%3D%20self._y_observed.select(~cs.by_name(%22time%22)).row(-1)%5B0%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20col_name%20%3D%20next(iter(self.local_y_schema_))%0A%20%20%20%20%20%20%20%20%20%20%20%20y_pred%20%3D%20pl.DataFrame(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%7Bcol_name%3A%20%5Blast_value%5D%20*%20self.fit_forecasting_horizon_%7D%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%20return%20self._add_time_columns(y_pred)%0A%0A%20%20%20%20return%20LastValueForecaster%2C%20pl%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.%20Fit%20and%20Predict%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(LastValueForecaster%2C%20pl)%3A%0A%20%20%20%20from%20yohou.datasets%20import%20fetch_sunspot%0A%20%20%20%20from%20yohou.model_selection%20import%20train_test_split%0A%0A%20%20%20%20bunch%20%3D%20fetch_sunspot()%0A%20%20%20%20y%20%3D%20bunch.frame.group_by_dynamic(%22time%22%2C%20every%3D%221mo%22).agg(%0A%20%20%20%20%20%20%20%20pl.col(%22sunspot_number%22).mean()%0A%20%20%20%20)%0A%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%3Dforecasting_horizon)%0A%0A%20%20%20%20forecaster%20%3D%20LastValueForecaster()%0A%20%20%20%20forecaster.fit(y_train%2C%20forecasting_horizon%3Dforecasting_horizon)%0A%20%20%20%20y_pred%20%3D%20forecaster.predict(forecasting_horizon%3Dforecasting_horizon)%0A%20%20%20%20y_pred.head()%0A%20%20%20%20return%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%203.%20Score%20Against%20a%20Baseline%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(y_pred%2C%20y_test%2C%20y_train)%3A%0A%20%20%20%20from%20yohou.metrics%20import%20MeanAbsoluteError%0A%20%20%20%20from%20yohou.point%20import%20SeasonalNaive%0A%0A%20%20%20%20scorer%20%3D%20MeanAbsoluteError()%0A%20%20%20%20scorer.fit(y_train)%0A%0A%20%20%20%20baseline%20%3D%20SeasonalNaive(seasonality%3D12)%0A%20%20%20%20baseline.fit(y_train%2C%20forecasting_horizon%3Dlen(y_test))%0A%20%20%20%20y_pred_baseline%20%3D%20baseline.predict(forecasting_horizon%3Dlen(y_test))%0A%0A%20%20%20%20score_custom%20%3D%20scorer.score(y_test%2C%20y_pred)%0A%20%20%20%20score_baseline%20%3D%20scorer.score(y_test%2C%20y_pred_baseline)%0A%20%20%20%20print(f%22LastValue%20MAE%3A%20%7Bscore_custom%3A.2f%7D%22)%0A%20%20%20%20print(f%22SeasonalNaive%20MAE%3A%20%7Bscore_baseline%3A.2f%7D%22)%0A%20%20%20%20return%20(y_pred_baseline%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%204.%20Plot%20the%20Comparison%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(y_pred%2C%20y_pred_baseline%2C%20y_test%2C%20y_train)%3A%0A%20%20%20%20from%20yohou.plotting%20import%20plot_forecast%0A%0A%20%20%20%20fig%20%3D%20plot_forecast(%0A%20%20%20%20%20%20%20%20y_train%3Dy_train%2C%0A%20%20%20%20%20%20%20%20y_test%3Dy_test%2C%0A%20%20%20%20%20%20%20%20y_pred%3D%7B%22LastValue%22%3A%20y_pred%2C%20%22SeasonalNaive%22%3A%20y_pred_baseline%7D%2C%0A%20%20%20%20)%0A%20%20%20%20fig%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%20Point%20Forecaster%5D(%2Fpages%2Fhow-to%2Fcreate-a-point-forecaster%2F)%20for%20the%20full%20guide%0A%20%20%20%20-%20%5BBuild%20Reduction%20Forecasters%5D(%2Fpages%2Fhow-to%2Fbuild-reduction-forecasters%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
9762e4f1c405c038b1960c2d0cfa3141