How to Compose Feature Pipelines¶
This guide shows you how to combine transformers into feature engineering pipelines using FeaturePipeline, FeatureUnion, and ColumnTransformer. Use this when a single transformer is not enough and you need sequential processing, parallel feature branches, or both.
Prerequisites¶
- Familiarity with transformers (How to Use Preprocessing Transformers)
- Understanding of feature pipelines (Feature Pipelines)
Try it interactively
Combine lag features, rolling statistics, EMA, and scaling in parallel with FeatureUnion and automatic observation horizon resolution.
ViewOpen in marimoNest FeaturePipeline, FeatureUnion, and DecompositionPipeline for multi-level feature engineering with trend-season-residual decomposition.
ViewOpen in marimoCombine ColumnForecaster, FeaturePipeline, FeatureUnion, and DecompositionPipeline on panel data with per-group scoring on KDD Cup air quality.
ViewOpen in marimoChain Transformers Sequentially¶
FeaturePipeline chains transformers so that the output of each step feeds into the next. Pass a list of (name, transformer) tuples:
from yohou.compose import FeaturePipeline
from yohou.preprocessing import LagTransformer
from yohou.stationarity import SeasonalDifferencing
pipeline = FeaturePipeline([
("diff", SeasonalDifferencing(seasonality=12)),
("lags", LagTransformer(lag=[1, 2, 3])),
])
pipeline.fit(y_train)
y_transformed = pipeline.transform(y_train)
Steps execute in order: SeasonalDifferencing removes the seasonal component, then LagTransformer creates autoregressive features from the differenced series.
The pipeline's observation_horizon is the cumulative sum across all steps, since each step's output feeds into the next:
Run Transformers in Parallel¶
FeatureUnion runs multiple transformers on the same input and concatenates their outputs column-wise:
from yohou.compose import FeatureUnion
from yohou.preprocessing import LagTransformer, RollingStatisticsTransformer
features = FeatureUnion([
("lags", LagTransformer(lag=[1, 3, 6, 12])),
("rolling", RollingStatisticsTransformer(window_size=12, statistics=["mean", "std"])),
])
features.fit(y_train)
y_features = features.transform(y_train)
Since all branches receive the same input, the union's observation_horizon is the maximum across its transformers (not the sum):
Apply Different Transformers to Different Columns¶
ColumnTransformer routes each column subset to a dedicated transformer, then concatenates the results. Use this instead of FeatureUnion when different columns need different treatment:
from yohou.compose import ColumnTransformer
from yohou.preprocessing import LagTransformer, RollingStatisticsTransformer
ct = ColumnTransformer(
transformers=[
("lags", LagTransformer(lag=[1, 2, 3]), ["temperature"]),
("rolling", RollingStatisticsTransformer(window_size=7), ["humidity"]),
],
remainder="drop",
)
ct.fit(y_train)
y_features = ct.transform(y_train)
Set remainder="passthrough" to keep columns not assigned to any transformer. Set it to a transformer instance to apply a default transformation to unmatched columns:
ct = ColumnTransformer(
transformers=[
("rolling", RollingStatisticsTransformer(window_size=7), ["humidity"]),
],
remainder=LagTransformer(lag=[1, 2, 3]), # default for all other columns
)
Like FeatureUnion, the observation_horizon is the maximum across all column transformers (including the remainder).
Nest Sequential and Parallel Stages¶
Place a FeatureUnion or ColumnTransformer inside a FeaturePipeline to first apply a shared preprocessing step, then branch into parallel feature extractors:
from yohou.compose import FeaturePipeline, FeatureUnion
from yohou.preprocessing import LagTransformer, RollingStatisticsTransformer
from yohou.stationarity import SeasonalDifferencing
feature_transformer = FeaturePipeline([
("diff", SeasonalDifferencing(seasonality=12)),
("features", FeatureUnion([
("lags", LagTransformer(lag=[1, 3, 6, 12])),
("rolling", RollingStatisticsTransformer(window_size=12, statistics=["mean", "std"])),
])),
])
The same pattern works with ColumnTransformer when features need column-specific treatment:
from yohou.compose import FeaturePipeline, ColumnTransformer
from yohou.preprocessing import LagTransformer, RollingStatisticsTransformer
feature_transformer = FeaturePipeline([
("features", ColumnTransformer(
transformers=[
("lags", LagTransformer(lag=[1, 3, 6]), ["temperature"]),
("rolling", RollingStatisticsTransformer(window_size=12), ["humidity"]),
],
remainder="passthrough",
)),
])
Pass the composed transformer to a forecaster:
from yohou.point import PointReductionForecaster
from sklearn.linear_model import Ridge
forecaster = PointReductionForecaster(
estimator=Ridge(),
feature_transformer=feature_transformer,
)
forecaster.fit(y_train, forecasting_horizon=12)
predictions = forecaster.predict()
Access Named Steps¶
Use named_steps on a FeaturePipeline and named_transformers on a FeatureUnion to inspect or retrieve individual components after construction:
pipeline = FeaturePipeline([
("diff", SeasonalDifferencing(seasonality=12)),
("lags", LagTransformer(lag=[1, 3, 6, 12])),
])
pipeline.named_steps["diff"] # SeasonalDifferencing(seasonality=12)
pipeline.named_steps["lags"].lag # [1, 3, 6, 12]
Bracket indexing also works by position or name:
pipeline[0] # first step
pipeline["diff"] # same as named_steps["diff"]
pipeline[0:1] # slice returns a new FeaturePipeline
Tune Nested Parameters¶
Both FeaturePipeline and FeatureUnion support get_params / set_params with double-underscore notation for nested access:
feature_transformer.set_params(features__lags__lag=[1, 2, 3])
feature_transformer.get_params()["features__lags__lag"] # [1, 2, 3]
This integrates with hyperparameter search. See How to Tune Hyperparameters for details.
Use Pipelines with Panel Data¶
Pipelines work with panel data automatically. When column names use the __ naming convention, each transformer applies independently to each group's columns. No special configuration is needed:
forecaster = PointReductionForecaster(
estimator=Ridge(),
feature_transformer=feature_transformer,
)
# Transformers apply per group automatically
forecaster.fit(y_panel, forecasting_horizon=12)
For background on panel data conventions and setup, see How to Work with Panel Data.
See Also¶
- How to Use Preprocessing Transformers for individual transformer patterns
- How to Tune Hyperparameters for searching over pipeline parameters
- Feature Pipelines for the conceptual architecture