%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%20Pipeline%20Composition%0A%0A%20%20%20%20Yohou's%20composition%20classes%20can%20be%20nested%20arbitrarily%3A%0A%20%20%20%20%5B%60FeaturePipeline%60%5D(%2Fpages%2Fapi%2Fgenerated%2Fyohou.compose.feature_pipeline.FeaturePipeline%2F)%20steps%20execute%20sequentially%2C%20%5B%60FeatureUnion%60%5D(%2Fpages%2Fapi%2Fgenerated%2Fyohou.compose.feature_union.FeatureUnion%2F)%0A%20%20%20%20branches%20execute%20in%20parallel%2C%20and%20%5B%60DecompositionPipeline%60%5D(%2Fpages%2Fapi%2Fgenerated%2Fyohou.compose.decomposition_pipeline.DecompositionPipeline%2F)%20cascades%0A%20%20%20%20residuals.%0A%0A%20%20%20%20This%20notebook%20shows%20how%20to%20nest%20FeaturePipeline%2C%20FeatureUnion%2C%20and%20DecompositionPipeline%20for%20multi-level%20feature%20engineering%20with%20trend-season-residual%20decomposition.%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.compose%20import%20DecompositionPipeline%2C%20FeaturePipeline%2C%20FeatureUnion%0A%20%20%20%20from%20yohou.datasets%20import%20fetch_electricity_demand%0A%20%20%20%20from%20yohou.metrics%20import%20MeanAbsoluteError%0A%20%20%20%20from%20yohou.model_selection%20import%20train_test_split%0A%20%20%20%20from%20yohou.plotting%20import%20plot_forecast%2C%20plot_time_series%0A%20%20%20%20from%20yohou.point%20import%20PointReductionForecaster%2C%20SeasonalNaive%0A%20%20%20%20from%20yohou.preprocessing%20import%20(%0A%20%20%20%20%20%20%20%20LagTransformer%2C%0A%20%20%20%20%20%20%20%20RollingStatisticsTransformer%2C%0A%20%20%20%20%20%20%20%20StandardScaler%2C%0A%20%20%20%20)%0A%20%20%20%20from%20yohou.stationarity%20import%20PolynomialTrendForecaster%0A%0A%20%20%20%20return%20(%0A%20%20%20%20%20%20%20%20DecompositionPipeline%2C%0A%20%20%20%20%20%20%20%20FeaturePipeline%2C%0A%20%20%20%20%20%20%20%20FeatureUnion%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%20PolynomialTrendForecaster%2C%0A%20%20%20%20%20%20%20%20Ridge%2C%0A%20%20%20%20%20%20%20%20RollingStatisticsTransformer%2C%0A%20%20%20%20%20%20%20%20SeasonalNaive%2C%0A%20%20%20%20%20%20%20%20StandardScaler%2C%0A%20%20%20%20%20%20%20%20fetch_electricity_demand%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%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.%20Prepare%20Data%0A%0A%20%20%20%20We%20load%20the%20Electricity%20Demand%20dataset%20and%20aggregate%20Victorian%20demand%0A%20%20%20%20to%20daily%20frequency.%20This%20provides%20a%20univariate%20series%20with%20clear%20daily%0A%20%20%20%20and%20weekly%20patterns%20for%20demonstrating%20pipeline%20composition.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(fetch_electricity_demand%2C%20mo%2C%20pl%2C%20train_test_split)%3A%0A%20%20%20%20_elec%20%3D%20fetch_electricity_demand().frame%0A%20%20%20%20elec%20%3D%20_elec.group_by_dynamic(%22time%22%2C%20every%3D%221d%22).agg(pl.col(%22vic__demand%22).mean().alias(%22demand%22)).drop_nulls()%0A%20%20%20%20y_train%2C%20y_test%20%3D%20train_test_split(elec%2C%20test_size%3D0.15)%0A%20%20%20%20horizon%20%3D%20len(y_test)%0A%20%20%20%20mo.md(f%22**Daily%20electricity%20demand**%3A%20Train%3D%7Blen(y_train)%7D%2C%20Test%3D%7Blen(y_test)%7D%22)%0A%20%20%20%20return%20elec%2C%20horizon%2C%20y_test%2C%20y_train%0A%0A%0A%40app.cell%0Adef%20_(elec%2C%20plot_time_series)%3A%0A%20%20%20%20plot_time_series(elec%2C%20title%3D%22Daily%20Victorian%20Electricity%20Demand%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.%20FeaturePipeline%3A%20Sequential%20Transformers%0A%0A%20%20%20%20%5B%60FeaturePipeline%60%5D(%2Fpages%2Fapi%2Fgenerated%2Fyohou.compose.feature_pipeline.FeaturePipeline%2F)%20applies%20steps%20sequentially.%20The%20combined%0A%20%20%20%20%60observation_horizon%60%20is%20the%20**sum**%20of%20all%20steps.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(FeaturePipeline%2C%20LagTransformer%2C%20StandardScaler%2C%20mo%2C%20y_train)%3A%0A%20%20%20%20fp%20%3D%20FeaturePipeline(%0A%20%20%20%20%20%20%20%20steps%3D%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20(%22lags%22%2C%20LagTransformer(lag%3D%5B1%2C%207%5D))%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20(%22scale%22%2C%20StandardScaler())%2C%0A%20%20%20%20%20%20%20%20%5D%2C%0A%20%20%20%20)%0A%20%20%20%20fp.fit(y_train)%0A%20%20%20%20fp_out%20%3D%20fp.transform(y_train)%0A%0A%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20f%22**FeaturePipeline%20output**%3A%20%7Bfp_out.shape%7D%5Cn%5Cn%22%0A%20%20%20%20%20%20%20%20f%22**Columns**%3A%20%7Bfp_out.columns%7D%5Cn%5Cn%22%0A%20%20%20%20%20%20%20%20f%22**observation_horizon**%3A%20%7Bfp.observation_horizon%7D%20%22%0A%20%20%20%20%20%20%20%20f%22(sum%3A%20LagTransformer%3D%7B7%7D%20%2B%20StandardScaler%3D%7B0%7D)%22%0A%20%20%20%20)%0A%20%20%20%20return%20(fp_out%2C)%0A%0A%0A%40app.cell%0Adef%20_(fp_out%2C%20plot_time_series)%3A%0A%20%20%20%20plot_time_series(fp_out%2C%20title%3D%22FeaturePipeline%20Output%3A%20Lagged%20%2B%20Scaled%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.%20FeatureUnion%20Inside%20FeaturePipeline%0A%0A%20%20%20%20Branch%20in%20parallel%20(FeatureUnion)%2C%20then%20apply%20a%20sequential%20step.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(%0A%20%20%20%20FeaturePipeline%2C%0A%20%20%20%20FeatureUnion%2C%0A%20%20%20%20LagTransformer%2C%0A%20%20%20%20RollingStatisticsTransformer%2C%0A%20%20%20%20StandardScaler%2C%0A%20%20%20%20mo%2C%0A%20%20%20%20y_train%2C%0A)%3A%0A%20%20%20%20fp_nested%20%3D%20FeaturePipeline(%0A%20%20%20%20%20%20%20%20steps%3D%5B%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%20%20%20%20%20%22features%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20FeatureUnion(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20transformer_list%3D%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20(%22lags%22%2C%20LagTransformer(lag%3D%5B1%2C%207%5D))%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20(%22rolling%22%2C%20RollingStatisticsTransformer(window_size%3D7%2C%20statistics%3D%22mean%22))%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%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)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20(%22scale%22%2C%20StandardScaler())%2C%0A%20%20%20%20%20%20%20%20%5D%2C%0A%20%20%20%20)%0A%20%20%20%20fp_nested.fit(y_train)%0A%20%20%20%20fp_nested_out%20%3D%20fp_nested.transform(y_train)%0A%0A%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20f%22**FeaturePipeline(FeatureUnion%20%2B%20StandardScaler)**%5Cn%5Cn%22%0A%20%20%20%20%20%20%20%20f%22Output%20columns%3A%20%7Bfp_nested_out.columns%7D%5Cn%5Cn%22%0A%20%20%20%20%20%20%20%20f%22observation_horizon%3A%20%7Bfp_nested.observation_horizon%7D%22%0A%20%20%20%20)%0A%20%20%20%20return%20fp_nested%2C%20fp_nested_out%0A%0A%0A%40app.cell%0Adef%20_(fp_nested_out%2C%20plot_time_series)%3A%0A%20%20%20%20plot_time_series(fp_nested_out%2C%20title%3D%22Nested%20Pipeline%20Output%3A%20Union%20%2B%20Scale%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.%20FeaturePipeline%20as%20feature_transformer%0A%0A%20%20%20%20Use%20the%20nested%20pipeline%20as%20%60feature_transformer%60%20in%20a%20forecaster.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(%0A%20%20%20%20MeanAbsoluteError%2C%0A%20%20%20%20PointReductionForecaster%2C%0A%20%20%20%20Ridge%2C%0A%20%20%20%20SeasonalNaive%2C%0A%20%20%20%20fp_nested%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%20fc_fp%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%3Dfp_nested%2C%0A%20%20%20%20)%0A%20%20%20%20fc_fp.fit(y_train%2C%20forecasting_horizon%3Dhorizon)%0A%20%20%20%20y_pred_fp%20%3D%20fc_fp.predict(forecasting_horizon%3Dhorizon)%0A%0A%20%20%20%20_naive%20%3D%20SeasonalNaive(seasonality%3D7)%0A%20%20%20%20_naive.fit(y_train%2C%20forecasting_horizon%3Dhorizon)%0A%20%20%20%20_y_pred_naive%20%3D%20_naive.predict(forecasting_horizon%3Dhorizon)%0A%0A%20%20%20%20_scorer%20%3D%20MeanAbsoluteError()%0A%20%20%20%20_scorer.fit(y_train)%0A%20%20%20%20_mae_fp%20%3D%20float(_scorer.score(y_test%2C%20y_pred_fp))%0A%20%20%20%20_mae_naive%20%3D%20float(_scorer.score(y_test%2C%20_y_pred_naive))%0A%0A%20%20%20%20mo.md(f%22**FeaturePipeline%20%2B%20Ridge%20MAE**%3A%20%7B_mae_fp%3A.2f%7D%5Cn%5Cn**SeasonalNaive%20MAE**%3A%20%7B_mae_naive%3A.2f%7D%22)%0A%20%20%20%20return%20fc_fp%2C%20y_pred_fp%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%5B%60plot_forecast%60%5D(%2Fpages%2Fapi%2Fgenerated%2Fyohou.plotting.forecasting.plot_forecast%2F)%20displays%20the%20nested%20%5B%60FeaturePipeline%60%5D(%2Fpages%2Fapi%2Fgenerated%2Fyohou.compose.feature_pipeline.FeaturePipeline%2F)%20predictions%0A%20%20%20%20against%20the%20held-out%20test%20data.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(plot_forecast%2C%20y_pred_fp%2C%20y_test%2C%20y_train)%3A%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_fp%2C%0A%20%20%20%20%20%20%20%20y_train%3Dy_train%2C%0A%20%20%20%20%20%20%20%20n_history%3D30%2C%0A%20%20%20%20%20%20%20%20title%3D%22FeaturePipeline(Union%20%2B%20Scale)%20%2B%20Ridge%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.%20DecompositionPipeline%20with%20Feature%20Engineering%0A%0A%20%20%20%20Multi-level%20nesting%3A%20Decomposition%20removes%20trend%2C%20then%20the%20residual%0A%20%20%20%20model%20uses%20FeatureUnion.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(%0A%20%20%20%20DecompositionPipeline%2C%0A%20%20%20%20FeatureUnion%2C%0A%20%20%20%20LagTransformer%2C%0A%20%20%20%20PointReductionForecaster%2C%0A%20%20%20%20PolynomialTrendForecaster%2C%0A%20%20%20%20Ridge%2C%0A%20%20%20%20RollingStatisticsTransformer%2C%0A%20%20%20%20horizon%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%20_union_deep%20%3D%20FeatureUnion(%0A%20%20%20%20%20%20%20%20transformer_list%3D%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20(%22lags%22%2C%20LagTransformer(lag%3D%5B1%2C%207%5D))%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20(%22rolling%22%2C%20RollingStatisticsTransformer(window_size%3D7%2C%20statistics%3D%5B%22mean%22%2C%20%22std%22%5D))%2C%0A%20%20%20%20%20%20%20%20%5D%2C%0A%20%20%20%20)%0A%0A%20%20%20%20fc_deep%20%3D%20DecompositionPipeline(%0A%20%20%20%20%20%20%20%20forecasters%3D%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20(%22trend%22%2C%20PolynomialTrendForecaster(degree%3D1))%2C%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%20%20%20%20%20%22residual%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20PointReductionForecaster(%0A%20%20%20%20%20%20%20%20%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%20%20%20%20%20%20%20%20%20feature_transformer%3D_union_deep%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)%2C%0A%20%20%20%20%20%20%20%20%5D%2C%0A%20%20%20%20)%0A%20%20%20%20fc_deep.fit(y_train%2C%20forecasting_horizon%3Dhorizon)%0A%20%20%20%20_y_pred_deep%20%3D%20fc_deep.predict(forecasting_horizon%3Dhorizon)%0A%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%20_y_pred_deep%2C%0A%20%20%20%20%20%20%20%20y_train%3Dy_train%2C%0A%20%20%20%20%20%20%20%20n_history%3D30%2C%0A%20%20%20%20%20%20%20%20title%3D%22Decomposition%20%2B%20FeatureUnion%20Residual%22%2C%0A%20%20%20%20)%0A%20%20%20%20return%20(fc_deep%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%206.%20Compare%20All%20Pipelines%0A%0A%20%20%20%20%5B%60MeanAbsoluteError%60%5D(%2Fpages%2Fapi%2Fgenerated%2Fyohou.metrics.point.MeanAbsoluteError%2F)%20scores%20each%20pipeline%20configuration%20on%20the%20test%0A%20%20%20%20data%2C%20showing%20whether%20the%20added%20complexity%20improves%20accuracy.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(MeanAbsoluteError%2C%20fc_deep%2C%20fc_fp%2C%20horizon%2C%20mo%2C%20pl%2C%20y_test%2C%20y_train)%3A%0A%20%20%20%20_scorer%20%3D%20MeanAbsoluteError()%0A%20%20%20%20_scorer.fit(y_train)%0A%20%20%20%20_models%20%3D%20%7B%0A%20%20%20%20%20%20%20%20%22FeaturePipeline%20%2B%20Ridge%22%3A%20fc_fp%2C%0A%20%20%20%20%20%20%20%20%22Decomposition%20%2B%20Union%22%3A%20fc_deep%2C%0A%20%20%20%20%7D%0A%20%20%20%20_rows%20%3D%20%5B%5D%0A%20%20%20%20for%20_name%2C%20_fc%20in%20_models.items()%3A%0A%20%20%20%20%20%20%20%20_pred%20%3D%20_fc.predict(forecasting_horizon%3Dhorizon)%0A%20%20%20%20%20%20%20%20_mae%20%3D%20float(_scorer.score(y_test%2C%20_pred))%0A%20%20%20%20%20%20%20%20_rows.append(%7B%22Pipeline%22%3A%20_name%2C%20%22MAE%22%3A%20round(_mae%2C%202)%7D)%0A%0A%20%20%20%20mo.ui.table(pl.DataFrame(_rows))%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%5BCompose%20Feature%20Pipelines%5D(%2Fpages%2Fhow-to%2Fcompose-feature-pipelines%2F)%20for%20the%20full%20guide%0A%20%20%20%20-%20%5BUse%20Preprocessing%20Transformers%5D(%2Fpages%2Fhow-to%2Fuse-preprocessing-transformers%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
1d77bf0d8bb80f4aab5ab3f97fb302ab