Skill Tracer
Skills › Scientific Agent Skills › scikit-learn › pipelines_and_composition.md

pipelines_and_composition.md

631 lines · skills/scikit-learn/references/pipelines_and_composition.md at 5f2bc03

Open on GitHub
1# Pipelines and Composite Estimators Reference
2
3Targets scikit-learn 1.9.1. Snippets with caller-supplied data/columns are illustrative; fit all learned preprocessing inside the training folds when estimating predictive performance.
4

Pipelines and Composite Estimators Reference

Targets scikit-learn 1.9.1. Snippets with caller-supplied data/columns are illustrative; fit all learned preprocessing inside the training folds when estimating predictive performance.

Raw lines 1 to 4
5## Overview
6
7Pipelines chain multiple processing steps into a single estimator, preventing data leakage and simplifying code. They enable reproducible workflows and seamless integration with cross-validation and hyperparameter tuning.
8

Overview

Pipelines chain multiple processing steps into a single estimator, preventing data leakage and simplifying code. They enable reproducible workflows and seamless integration with cross-validation and hyperparameter tuning.

Raw lines 5 to 8
9## Pipeline Basics

Pipeline Basics

Raw lines 9 to 10
11### Creating a Pipeline
13**Pipeline (`sklearn.pipeline.Pipeline`)**
14- Chains transformers with a final estimator
15- All intermediate steps must implement `fit` and `transform` (`fit_transform` is optional)
16- Final step can be any estimator (transformer, classifier, regressor, clusterer)
17- Example:
18```python
19from sklearn.pipeline import Pipeline
20from sklearn.preprocessing import StandardScaler
21from sklearn.decomposition import PCA
22from sklearn.linear_model import LogisticRegression
24pipeline = Pipeline([
25 ('scaler', StandardScaler()),
26 ('pca', PCA(n_components=10)),
27 ('classifier', LogisticRegression())
28])
30# Fit the entire pipeline
31pipeline.fit(X_train, y_train)
33# Predict using the pipeline
34y_pred = pipeline.predict(X_test)
35y_proba = pipeline.predict_proba(X_test)
36```

Creating a Pipeline

**Pipeline (sklearn.pipeline.Pipeline)**

  • Chains transformers with a final estimator
  • All intermediate steps must implement fit and transform (fit_transform is optional)
  • Final step can be any estimator (transformer, classifier, regressor, clusterer)
  • Example:
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA
from sklearn.linear_model import LogisticRegression

pipeline = Pipeline([
    ('scaler', StandardScaler()),
    ('pca', PCA(n_components=10)),
    ('classifier', LogisticRegression())
])

# Fit the entire pipeline
pipeline.fit(X_train, y_train)

# Predict using the pipeline
y_pred = pipeline.predict(X_test)
y_proba = pipeline.predict_proba(X_test)
Raw lines 11 to 37
38### Using make_pipeline
40**make_pipeline**
41- Convenient constructor that auto-generates step names
42- Example:
43```python
44from sklearn.pipeline import make_pipeline
45from sklearn.preprocessing import StandardScaler
46from sklearn.svm import SVC
48pipeline = make_pipeline(
49 StandardScaler(),
50 PCA(n_components=10),
51 SVC(kernel='rbf')
54pipeline.fit(X_train, y_train)
55```

Using make_pipeline

make_pipeline

  • Convenient constructor that auto-generates step names
  • Example:
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.svm import SVC

pipeline = make_pipeline(
    StandardScaler(),
    PCA(n_components=10),
    SVC(kernel='rbf')
)

pipeline.fit(X_train, y_train)
Raw lines 38 to 56
57## Accessing Pipeline Components

Accessing Pipeline Components

Raw lines 57 to 58
59### Accessing Steps
61```python
62# By index
63scaler = pipeline.steps[0][1]
65# By name
66scaler = pipeline.named_steps['scaler']
67pca = pipeline.named_steps['pca']
69# Using indexing syntax
70scaler = pipeline['scaler']
71pca = pipeline['pca']
73# Get all step names
74print(pipeline.named_steps.keys())
75```

Accessing Steps

# By index
scaler = pipeline.steps[0][1]

# By name
scaler = pipeline.named_steps['scaler']
pca = pipeline.named_steps['pca']

# Using indexing syntax
scaler = pipeline['scaler']
pca = pipeline['pca']

# Get all step names
print(pipeline.named_steps.keys())
Raw lines 59 to 76
77### Setting Parameters
79```python
80# Set parameters using double underscore notation
81pipeline.set_params(
82 pca__n_components=15,
83 classifier__C=0.1
86# Or during creation
87pipeline = Pipeline([
88 ('scaler', StandardScaler()),
89 ('pca', PCA(n_components=10)),
90 ('classifier', LogisticRegression(C=1.0))
91])
92```

Setting Parameters

# Set parameters using double underscore notation
pipeline.set_params(
    pca__n_components=15,
    classifier__C=0.1
)

# Or during creation
pipeline = Pipeline([
    ('scaler', StandardScaler()),
    ('pca', PCA(n_components=10)),
    ('classifier', LogisticRegression(C=1.0))
])
Raw lines 77 to 93
94### Accessing Attributes
96```python
97# Access fitted attributes
98pca_components = pipeline.named_steps['pca'].components_
99explained_variance = pipeline.named_steps['pca'].explained_variance_ratio_
101# Access intermediate transformations
102X_scaled = pipeline.named_steps['scaler'].transform(X_test)
103X_pca = pipeline.named_steps['pca'].transform(X_scaled)
104```

Accessing Attributes

# Access fitted attributes
pca_components = pipeline.named_steps['pca'].components_
explained_variance = pipeline.named_steps['pca'].explained_variance_ratio_

# Access intermediate transformations
X_scaled = pipeline.named_steps['scaler'].transform(X_test)
X_pca = pipeline.named_steps['pca'].transform(X_scaled)
Raw lines 94 to 105
106## Hyperparameter Tuning with Pipelines

Hyperparameter Tuning with Pipelines

Raw lines 106 to 107
108### Grid Search with Pipeline
110```python
111from sklearn.model_selection import GridSearchCV
112from sklearn.pipeline import Pipeline
113from sklearn.preprocessing import StandardScaler
114from sklearn.svm import SVC
116pipeline = Pipeline([
117 ('scaler', StandardScaler()),
118 ('classifier', SVC())
119])
121param_grid = {
122 'classifier__C': [0.1, 1, 10, 100],
123 'classifier__gamma': ['scale', 'auto', 0.001, 0.01],
124 'classifier__kernel': ['rbf', 'linear']
127grid_search = GridSearchCV(pipeline, param_grid, cv=5, n_jobs=-1)
128grid_search.fit(X_train, y_train)
130print(f"Best parameters: {grid_search.best_params_}")
131print(f"Best score: {grid_search.best_score_:.3f}")
132```

Grid Search with Pipeline

from sklearn.model_selection import GridSearchCV
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.svm import SVC

pipeline = Pipeline([
    ('scaler', StandardScaler()),
    ('classifier', SVC())
])

param_grid = {
    'classifier__C': [0.1, 1, 10, 100],
    'classifier__gamma': ['scale', 'auto', 0.001, 0.01],
    'classifier__kernel': ['rbf', 'linear']
}

grid_search = GridSearchCV(pipeline, param_grid, cv=5, n_jobs=-1)
grid_search.fit(X_train, y_train)

print(f"Best parameters: {grid_search.best_params_}")
print(f"Best score: {grid_search.best_score_:.3f}")
Raw lines 108 to 133
134### Tuning Multiple Pipeline Steps
136```python
137pipeline = Pipeline([
138 ('scaler', StandardScaler()),
139 ('pca', PCA()),
140 ('classifier', SVC())
141])
142# Every PCA candidate must fit within min(n_training_fold, n_features).
143param_grid = {
144 # PCA parameters
145 'pca__n_components': [5, 10, 20, 50],
147 # Classifier parameters
148 'classifier__C': [0.1, 1, 10],
149 'classifier__kernel': ['rbf', 'linear']
152grid_search = GridSearchCV(pipeline, param_grid, cv=5)
153grid_search.fit(X_train, y_train)
154```

Tuning Multiple Pipeline Steps

pipeline = Pipeline([
    ('scaler', StandardScaler()),
    ('pca', PCA()),
    ('classifier', SVC())
])
# Every PCA candidate must fit within min(n_training_fold, n_features).
param_grid = {
    # PCA parameters
    'pca__n_components': [5, 10, 20, 50],

    # Classifier parameters
    'classifier__C': [0.1, 1, 10],
    'classifier__kernel': ['rbf', 'linear']
}

grid_search = GridSearchCV(pipeline, param_grid, cv=5)
grid_search.fit(X_train, y_train)
Raw lines 134 to 155
156## ColumnTransformer

ColumnTransformer

Raw lines 156 to 157
158### Basic Usage
160**ColumnTransformer (`sklearn.compose.ColumnTransformer`)**
161- Apply different preprocessing to different columns
162- Prevents preprocessing leakage only when placed inside the estimator passed to cross-validation
163- Example:
164```python
165from sklearn.compose import ColumnTransformer
166from sklearn.preprocessing import StandardScaler, OneHotEncoder
167from sklearn.impute import SimpleImputer
169# Define column groups
170numeric_features = ['age', 'income', 'hours_per_week']
171categorical_features = ['gender', 'occupation', 'native_country']
173# Create preprocessor
174preprocessor = ColumnTransformer(
175 transformers=[
176 ('num', StandardScaler(), numeric_features),
177 ('cat', OneHotEncoder(handle_unknown='ignore'), categorical_features)
178 ],
179 remainder='passthrough' # Keep other columns unchanged
182X_transformed = preprocessor.fit_transform(X)
183```

Basic Usage

**ColumnTransformer (sklearn.compose.ColumnTransformer)**

  • Apply different preprocessing to different columns
  • Prevents preprocessing leakage only when placed inside the estimator passed to cross-validation
  • Example:
from sklearn.compose import ColumnTransformer
from sklearn.preprocessing import StandardScaler, OneHotEncoder
from sklearn.impute import SimpleImputer

# Define column groups
numeric_features = ['age', 'income', 'hours_per_week']
categorical_features = ['gender', 'occupation', 'native_country']

# Create preprocessor
preprocessor = ColumnTransformer(
    transformers=[
        ('num', StandardScaler(), numeric_features),
        ('cat', OneHotEncoder(handle_unknown='ignore'), categorical_features)
    ],
    remainder='passthrough'  # Keep other columns unchanged
)

X_transformed = preprocessor.fit_transform(X)
Raw lines 158 to 184
185### With Pipeline Steps
187```python
188from sklearn.pipeline import Pipeline
190numeric_transformer = Pipeline(steps=[
191 ('imputer', SimpleImputer(strategy='median')),
192 ('scaler', StandardScaler())
193])
195categorical_transformer = Pipeline(steps=[
196 ('imputer', SimpleImputer(strategy='constant', fill_value='missing')),
197 ('onehot', OneHotEncoder(handle_unknown='ignore'))
198])
200preprocessor = ColumnTransformer(
201 transformers=[
202 ('num', numeric_transformer, numeric_features),
203 ('cat', categorical_transformer, categorical_features)
204 ]
207# Full pipeline with model
208full_pipeline = Pipeline([
209 ('preprocessor', preprocessor),
210 ('classifier', LogisticRegression())
211])
213full_pipeline.fit(X_train, y_train)
214```

With Pipeline Steps

from sklearn.pipeline import Pipeline

numeric_transformer = Pipeline(steps=[
    ('imputer', SimpleImputer(strategy='median')),
    ('scaler', StandardScaler())
])

categorical_transformer = Pipeline(steps=[
    ('imputer', SimpleImputer(strategy='constant', fill_value='missing')),
    ('onehot', OneHotEncoder(handle_unknown='ignore'))
])

preprocessor = ColumnTransformer(
    transformers=[
        ('num', numeric_transformer, numeric_features),
        ('cat', categorical_transformer, categorical_features)
    ]
)

# Full pipeline with model
full_pipeline = Pipeline([
    ('preprocessor', preprocessor),
    ('classifier', LogisticRegression())
])

full_pipeline.fit(X_train, y_train)
Raw lines 185 to 215
216### Using make_column_transformer
218```python
219from sklearn.compose import make_column_transformer
221preprocessor = make_column_transformer(
222 (StandardScaler(), numeric_features),
223 (OneHotEncoder(handle_unknown='ignore'), categorical_features),
224 remainder='passthrough'
226```

Using make_column_transformer

from sklearn.compose import make_column_transformer

preprocessor = make_column_transformer(
    (StandardScaler(), numeric_features),
    (OneHotEncoder(handle_unknown='ignore'), categorical_features),
    remainder='passthrough'
)
Raw lines 216 to 227
228### Column Selection
230```python
231# By column names (if X is DataFrame)
232preprocessor = ColumnTransformer([
233 ('num', StandardScaler(), ['age', 'income']),
234 ('cat', OneHotEncoder(), ['gender', 'occupation'])
235])
237# By column indices
238preprocessor = ColumnTransformer([
239 ('num', StandardScaler(), [0, 1, 2]),
240 ('cat', OneHotEncoder(), [3, 4])
241])
243# By boolean mask
244numeric_mask = [True, True, True, False, False]
245categorical_mask = [False, False, False, True, True]
247preprocessor = ColumnTransformer([
248 ('num', StandardScaler(), numeric_mask),
249 ('cat', OneHotEncoder(), categorical_mask)
250])
252# By callable
253def is_numeric(X):
254 return X.select_dtypes(include=['number']).columns.tolist()
256preprocessor = ColumnTransformer([
257 ('num', StandardScaler(), is_numeric)
258])
259```

Column Selection

# By column names (if X is DataFrame)
preprocessor = ColumnTransformer([
    ('num', StandardScaler(), ['age', 'income']),
    ('cat', OneHotEncoder(), ['gender', 'occupation'])
])

# By column indices
preprocessor = ColumnTransformer([
    ('num', StandardScaler(), [0, 1, 2]),
    ('cat', OneHotEncoder(), [3, 4])
])

# By boolean mask
numeric_mask = [True, True, True, False, False]
categorical_mask = [False, False, False, True, True]

preprocessor = ColumnTransformer([
    ('num', StandardScaler(), numeric_mask),
    ('cat', OneHotEncoder(), categorical_mask)
])

# By callable
def is_numeric(X):
    return X.select_dtypes(include=['number']).columns.tolist()

preprocessor = ColumnTransformer([
    ('num', StandardScaler(), is_numeric)
])
Raw lines 228 to 260
261### Getting Feature Names
263```python
264# Fit before requesting names
265preprocessor.fit(X_train)
266output_features = preprocessor.get_feature_names_out()
267print(f"Input features: {X_train.columns.tolist()}")
268print(f"Output features: {output_features}")
269```

Getting Feature Names

# Fit before requesting names
preprocessor.fit(X_train)
output_features = preprocessor.get_feature_names_out()
print(f"Input features: {X_train.columns.tolist()}")
print(f"Output features: {output_features}")
Raw lines 261 to 270
271### Remainder Handling
273```python
274# Drop unspecified columns (default)
275preprocessor = ColumnTransformer([...], remainder='drop')
277# Pass through unchanged
278preprocessor = ColumnTransformer([...], remainder='passthrough')
280# Apply transformer to remaining columns
281preprocessor = ColumnTransformer([...], remainder=StandardScaler())
282```

Remainder Handling

# Drop unspecified columns (default)
preprocessor = ColumnTransformer([...], remainder='drop')

# Pass through unchanged
preprocessor = ColumnTransformer([...], remainder='passthrough')

# Apply transformer to remaining columns
preprocessor = ColumnTransformer([...], remainder=StandardScaler())
Raw lines 271 to 283
284## FeatureUnion
286### Basic Usage
288**FeatureUnion (`sklearn.pipeline.FeatureUnion`)**
289- Concatenates results of multiple transformers
290- Transformers are applied in parallel
291- Example:
292```python
293from sklearn.pipeline import FeatureUnion
294from sklearn.decomposition import PCA
295from sklearn.feature_selection import SelectKBest
297# Combine PCA and feature selection
298feature_union = FeatureUnion([
299 ('pca', PCA(n_components=10)),
300 ('select_best', SelectKBest(k=20))
301])
303X_combined = feature_union.fit_transform(X_train, y_train)
304print(f"Combined features: {X_combined.shape[1]}") # 10 + 20 = 30
305```

Basic Usage

**FeatureUnion (sklearn.pipeline.FeatureUnion)**

  • Concatenates results of multiple transformers
  • Transformers are applied in parallel
  • Example:
from sklearn.pipeline import FeatureUnion
from sklearn.decomposition import PCA
from sklearn.feature_selection import SelectKBest

# Combine PCA and feature selection
feature_union = FeatureUnion([
    ('pca', PCA(n_components=10)),
    ('select_best', SelectKBest(k=20))
])

X_combined = feature_union.fit_transform(X_train, y_train)
print(f"Combined features: {X_combined.shape[1]}")  # 10 + 20 = 30
Raw lines 286 to 306
307### With Pipeline
309```python
310from sklearn.pipeline import Pipeline, FeatureUnion
311from sklearn.preprocessing import StandardScaler
312from sklearn.decomposition import PCA, TruncatedSVD
314# Create feature union
315feature_union = FeatureUnion([
316 ('pca', PCA(n_components=10)),
317 ('svd', TruncatedSVD(n_components=10))
318])
320# Full pipeline
321pipeline = Pipeline([
322 ('scaler', StandardScaler()),
323 ('features', feature_union),
324 ('classifier', LogisticRegression())
325])
327pipeline.fit(X_train, y_train)
328```

With Pipeline

from sklearn.pipeline import Pipeline, FeatureUnion
from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA, TruncatedSVD

# Create feature union
feature_union = FeatureUnion([
    ('pca', PCA(n_components=10)),
    ('svd', TruncatedSVD(n_components=10))
])

# Full pipeline
pipeline = Pipeline([
    ('scaler', StandardScaler()),
    ('features', feature_union),
    ('classifier', LogisticRegression())
])

pipeline.fit(X_train, y_train)
Raw lines 307 to 329
330### Weighted Feature Union
332```python
333# Apply weights to transformers
334feature_union = FeatureUnion(
335 transformer_list=[
336 ('pca', PCA(n_components=10)),
337 ('select_best', SelectKBest(k=20))
338 ],
339 transformer_weights={
340 'pca': 2.0, # Give PCA features double weight
341 'select_best': 1.0
342 }
344```

Weighted Feature Union

# Apply weights to transformers
feature_union = FeatureUnion(
    transformer_list=[
        ('pca', PCA(n_components=10)),
        ('select_best', SelectKBest(k=20))
    ],
    transformer_weights={
        'pca': 2.0,  # Give PCA features double weight
        'select_best': 1.0
    }
)
Raw lines 330 to 345
346## Advanced Pipeline Patterns

Advanced Pipeline Patterns

Raw lines 346 to 347
348### Caching Pipeline Steps
350```python
351from sklearn.pipeline import Pipeline
352from tempfile import mkdtemp
353from shutil import rmtree
355# Cache intermediate results
356cachedir = mkdtemp()
357pipeline = Pipeline([
358 ('scaler', StandardScaler()),
359 ('pca', PCA(n_components=50)),
360 ('classifier', LogisticRegression())
361], memory=cachedir)
363pipeline.fit(X_train, y_train)
365# Clean up cache
366rmtree(cachedir)
367```

Caching Pipeline Steps

from sklearn.pipeline import Pipeline
from tempfile import mkdtemp
from shutil import rmtree

# Cache intermediate results
cachedir = mkdtemp()
pipeline = Pipeline([
    ('scaler', StandardScaler()),
    ('pca', PCA(n_components=50)),
    ('classifier', LogisticRegression())
], memory=cachedir)

pipeline.fit(X_train, y_train)

# Clean up cache
rmtree(cachedir)
Raw lines 348 to 368
369### Nested Pipelines
371```python
372from sklearn.pipeline import Pipeline
373from sklearn.compose import ColumnTransformer
374from sklearn.feature_extraction.text import CountVectorizer, TfidfTransformer
375from sklearn.preprocessing import StandardScaler
376from sklearn.linear_model import LogisticRegression
378# Inner pipeline for text processing
379text_pipeline = Pipeline([
380 ('vect', CountVectorizer()),
381 ('tfidf', TfidfTransformer())
382])
384# Outer pipeline combining text and numeric features
385full_pipeline = Pipeline([
386 ('features', ColumnTransformer([
387 ('text', text_pipeline, 'text'), # Scalar selects 1D text input
388 ('numeric', StandardScaler(), ['age', 'income'])
389 ])),
390 ('classifier', LogisticRegression())
391])
392```

Nested Pipelines

from sklearn.pipeline import Pipeline
from sklearn.compose import ColumnTransformer
from sklearn.feature_extraction.text import CountVectorizer, TfidfTransformer
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression

# Inner pipeline for text processing
text_pipeline = Pipeline([
    ('vect', CountVectorizer()),
    ('tfidf', TfidfTransformer())
])

# Outer pipeline combining text and numeric features
full_pipeline = Pipeline([
    ('features', ColumnTransformer([
        ('text', text_pipeline, 'text'),  # Scalar selects 1D text input
        ('numeric', StandardScaler(), ['age', 'income'])
    ])),
    ('classifier', LogisticRegression())
])
Raw lines 369 to 393
394### Custom Transformers in Pipelines
396```python
397from sklearn.base import BaseEstimator, TransformerMixin
399class TextLengthExtractor(TransformerMixin, BaseEstimator):
400 def fit(self, X, y=None):
401 return self
403 def transform(self, X):
404 return [[len(text)] for text in X]
406pipeline = Pipeline([
407 ('length', TextLengthExtractor()),
408 ('scaler', StandardScaler()),
409 ('classifier', LogisticRegression())
410])
411```

Custom Transformers in Pipelines

from sklearn.base import BaseEstimator, TransformerMixin

class TextLengthExtractor(TransformerMixin, BaseEstimator):
    def fit(self, X, y=None):
        return self

    def transform(self, X):
        return [[len(text)] for text in X]

pipeline = Pipeline([
    ('length', TextLengthExtractor()),
    ('scaler', StandardScaler()),
    ('classifier', LogisticRegression())
])
Raw lines 394 to 412
413### Slicing Pipelines
415```python
416# Get sub-pipeline
417sub_pipeline = pipeline[:2] # First two steps
419# Get specific range
420middle_steps = pipeline[1:3]
421```

Slicing Pipelines

# Get sub-pipeline
sub_pipeline = pipeline[:2]  # First two steps

# Get specific range
middle_steps = pipeline[1:3]
Raw lines 413 to 422
423## TransformedTargetRegressor

TransformedTargetRegressor

Raw lines 423 to 424
425### Basic Usage
427**TransformedTargetRegressor**
428- Transforms target variable before fitting
429- Automatically inverse-transforms predictions
430- Example:
431```python
432from sklearn.compose import TransformedTargetRegressor
433from sklearn.preprocessing import QuantileTransformer
434from sklearn.linear_model import LinearRegression
436model = TransformedTargetRegressor(
437 regressor=LinearRegression(),
438 transformer=QuantileTransformer(output_distribution='normal')
441model.fit(X_train, y_train)
442y_pred = model.predict(X_test) # Automatically inverse-transformed
443```

Basic Usage

TransformedTargetRegressor

  • Transforms target variable before fitting
  • Automatically inverse-transforms predictions
  • Example:
from sklearn.compose import TransformedTargetRegressor
from sklearn.preprocessing import QuantileTransformer
from sklearn.linear_model import LinearRegression

model = TransformedTargetRegressor(
    regressor=LinearRegression(),
    transformer=QuantileTransformer(output_distribution='normal')
)

model.fit(X_train, y_train)
y_pred = model.predict(X_test)  # Automatically inverse-transformed
Raw lines 425 to 444
445### With Functions
447```python
448import numpy as np
450model = TransformedTargetRegressor(
451 regressor=LinearRegression(),
452 func=np.log1p,
453 inverse_func=np.expm1
456model.fit(X_train, y_train)
457```

With Functions

import numpy as np

model = TransformedTargetRegressor(
    regressor=LinearRegression(),
    func=np.log1p,
    inverse_func=np.expm1
)

model.fit(X_train, y_train)
Raw lines 445 to 458
459## Complete Example: End-to-End Pipeline
461```python
462import pandas as pd
463from sklearn.compose import ColumnTransformer
464from sklearn.pipeline import Pipeline
465from sklearn.preprocessing import StandardScaler, OneHotEncoder
466from sklearn.impute import SimpleImputer
467from sklearn.decomposition import PCA
468from sklearn.ensemble import RandomForestClassifier
469from sklearn.model_selection import GridSearchCV
471# Define feature types
472numeric_features = ['age', 'income', 'hours_per_week']
473categorical_features = ['gender', 'occupation', 'education']
475# Numeric preprocessing pipeline
476numeric_transformer = Pipeline(steps=[
477 ('imputer', SimpleImputer(strategy='median')),
478 ('scaler', StandardScaler())
479])
481# Categorical preprocessing pipeline
482categorical_transformer = Pipeline(steps=[
483 ('imputer', SimpleImputer(strategy='constant', fill_value='missing')),
484 ('onehot', OneHotEncoder(handle_unknown='ignore', sparse_output=False))
485])
487# Combine preprocessing
488preprocessor = ColumnTransformer(
489 transformers=[
490 ('num', numeric_transformer, numeric_features),
491 ('cat', categorical_transformer, categorical_features)
492 ]
495# Full pipeline
496pipeline = Pipeline([
497 ('preprocessor', preprocessor),
498 ('pca', PCA(n_components=0.95)), # Keep 95% variance
499 ('classifier', RandomForestClassifier(random_state=42))
500])
502# Hyperparameter tuning
503param_grid = {
504 'preprocessor__num__imputer__strategy': ['mean', 'median'],
505 'pca__n_components': [0.90, 0.95, 0.99],
506 'classifier__n_estimators': [100, 200],
507 'classifier__max_depth': [10, 20, None]
510grid_search = GridSearchCV(
511 pipeline, param_grid,
512 cv=5, scoring='accuracy',
513 n_jobs=-1, verbose=1
516grid_search.fit(X_train, y_train)
518print(f"Best parameters: {grid_search.best_params_}")
519print(f"Best CV score: {grid_search.best_score_:.3f}")
520print(f"Test score: {grid_search.score(X_test, y_test):.3f}")
522# Make predictions
523best_pipeline = grid_search.best_estimator_
524y_pred = best_pipeline.predict(X_test)
525y_proba = best_pipeline.predict_proba(X_test)
526```

Complete Example: End-to-End Pipeline

import pandas as pd
from sklearn.compose import ColumnTransformer
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler, OneHotEncoder
from sklearn.impute import SimpleImputer
from sklearn.decomposition import PCA
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import GridSearchCV

# Define feature types
numeric_features = ['age', 'income', 'hours_per_week']
categorical_features = ['gender', 'occupation', 'education']

# Numeric preprocessing pipeline
numeric_transformer = Pipeline(steps=[
    ('imputer', SimpleImputer(strategy='median')),
    ('scaler', StandardScaler())
])

# Categorical preprocessing pipeline
categorical_transformer = Pipeline(steps=[
    ('imputer', SimpleImputer(strategy='constant', fill_value='missing')),
    ('onehot', OneHotEncoder(handle_unknown='ignore', sparse_output=False))
])

# Combine preprocessing
preprocessor = ColumnTransformer(
    transformers=[
        ('num', numeric_transformer, numeric_features),
        ('cat', categorical_transformer, categorical_features)
    ]
)

# Full pipeline
pipeline = Pipeline([
    ('preprocessor', preprocessor),
    ('pca', PCA(n_components=0.95)),  # Keep 95% variance
    ('classifier', RandomForestClassifier(random_state=42))
])

# Hyperparameter tuning
param_grid = {
    'preprocessor__num__imputer__strategy': ['mean', 'median'],
    'pca__n_components': [0.90, 0.95, 0.99],
    'classifier__n_estimators': [100, 200],
    'classifier__max_depth': [10, 20, None]
}

grid_search = GridSearchCV(
    pipeline, param_grid,
    cv=5, scoring='accuracy',
    n_jobs=-1, verbose=1
)

grid_search.fit(X_train, y_train)

print(f"Best parameters: {grid_search.best_params_}")
print(f"Best CV score: {grid_search.best_score_:.3f}")
print(f"Test score: {grid_search.score(X_test, y_test):.3f}")

# Make predictions
best_pipeline = grid_search.best_estimator_
y_pred = best_pipeline.predict(X_test)
y_proba = best_pipeline.predict_proba(X_test)
Raw lines 459 to 527
528## Visualization
530### Displaying Pipelines
532```python
533# In Jupyter notebooks, pipelines display as diagrams
534from sklearn import set_config
535set_config(display='diagram')
537pipeline # Displays visual diagram
538```

Displaying Pipelines

# In Jupyter notebooks, pipelines display as diagrams
from sklearn import set_config
set_config(display='diagram')

pipeline  # Displays visual diagram
Raw lines 530 to 539
540### Text Representation
542```python
543# Print pipeline structure
544print(pipeline)
546# Get detailed parameters
547print(pipeline.get_params())
548```

Text Representation

# Print pipeline structure
print(pipeline)

# Get detailed parameters
print(pipeline.get_params())
Raw lines 540 to 549
550## Best Practices

Best Practices

Raw lines 550 to 551
552### Always Use Pipelines
553- Prevents data leakage
554- Ensures consistency between training and prediction
555- Makes code more maintainable
556- Enables easy hyperparameter tuning

Always Use Pipelines

  • Prevents data leakage
  • Ensures consistency between training and prediction
  • Makes code more maintainable
  • Enables easy hyperparameter tuning
Raw lines 552 to 557
558### Proper Pipeline Construction
559```python
560# Good: Preprocessing inside pipeline
561pipeline = Pipeline([
562 ('scaler', StandardScaler()),
563 ('model', LogisticRegression())
564])
565pipeline.fit(X_train, y_train)
567# Unsafe if these once-scaled values are subsequently passed to CV/search
568X_train_scaled = StandardScaler().fit_transform(X_train)
569model = LogisticRegression()
570model.fit(X_train_scaled, y_train)
571```

Proper Pipeline Construction

# Good: Preprocessing inside pipeline
pipeline = Pipeline([
    ('scaler', StandardScaler()),
    ('model', LogisticRegression())
])
pipeline.fit(X_train, y_train)

# Unsafe if these once-scaled values are subsequently passed to CV/search
X_train_scaled = StandardScaler().fit_transform(X_train)
model = LogisticRegression()
model.fit(X_train_scaled, y_train)
Raw lines 558 to 572
573### Use ColumnTransformer for Mixed Data
574Always use ColumnTransformer when you have both numerical and categorical features:
575```python
576preprocessor = ColumnTransformer([
577 ('num', StandardScaler(), numeric_features),
578 ('cat', OneHotEncoder(), categorical_features)
579])
580```

Use ColumnTransformer for Mixed Data

Always use ColumnTransformer when you have both numerical and categorical features:

preprocessor = ColumnTransformer([
    ('num', StandardScaler(), numeric_features),
    ('cat', OneHotEncoder(), categorical_features)
])
Raw lines 573 to 581
582### Name Your Steps Meaningfully
583```python
584# Good
585pipeline = Pipeline([
586 ('imputer', SimpleImputer()),
587 ('scaler', StandardScaler()),
588 ('pca', PCA(n_components=10)),
589 ('rf_classifier', RandomForestClassifier())
590])
592# Bad
593pipeline = Pipeline([
594 ('step1', SimpleImputer()),
595 ('step2', StandardScaler()),
596 ('step3', PCA(n_components=10)),
597 ('step4', RandomForestClassifier())
598])
599```

Name Your Steps Meaningfully

# Good
pipeline = Pipeline([
    ('imputer', SimpleImputer()),
    ('scaler', StandardScaler()),
    ('pca', PCA(n_components=10)),
    ('rf_classifier', RandomForestClassifier())
])

# Bad
pipeline = Pipeline([
    ('step1', SimpleImputer()),
    ('step2', StandardScaler()),
    ('step3', PCA(n_components=10)),
    ('step4', RandomForestClassifier())
])
Raw lines 582 to 600
601### Cache Expensive Transformations
602For repeated fitting (e.g., during grid search), cache expensive steps:
603```python
604from tempfile import mkdtemp
606cachedir = mkdtemp()
607pipeline = Pipeline([
608 ('expensive_preprocessing', ExpensiveTransformer()),
609 ('classifier', LogisticRegression())
610], memory=cachedir)
611```

Cache Expensive Transformations

For repeated fitting (e.g., during grid search), cache expensive steps:

from tempfile import mkdtemp

cachedir = mkdtemp()
pipeline = Pipeline([
    ('expensive_preprocessing', ExpensiveTransformer()),
    ('classifier', LogisticRegression())
], memory=cachedir)
Raw lines 601 to 612
613### Test Pipeline Compatibility
614Ensure all steps are compatible:
615- All intermediate steps must have fit() and transform()
616- Final step needs fit() and predict() (or transform())
617- Use set_output(transform='pandas') for DataFrame output
618```python
619# This is a transformer-only pipeline; a final classifier has no transform().
620transformers = Pipeline([
621 ('imputer', SimpleImputer()),
622 ('scaler', StandardScaler())
623]).set_output(transform='pandas')
624X_transformed = transformers.fit_transform(X_train[numeric_features])
625# OneHotEncoder must use sparse_output=False for pandas output.
626```

Test Pipeline Compatibility

Ensure all steps are compatible:

  • All intermediate steps must have fit() and transform()
  • Final step needs fit() and predict() (or transform())
  • Use set_output(transform='pandas') for DataFrame output
# This is a transformer-only pipeline; a final classifier has no transform().
transformers = Pipeline([
    ('imputer', SimpleImputer()),
    ('scaler', StandardScaler())
]).set_output(transform='pandas')
X_transformed = transformers.fit_transform(X_train[numeric_features])
# OneHotEncoder must use sparse_output=False for pandas output.
Raw lines 613 to 627
628## Upstream references
630- https://scikit-learn.org/stable/modules/compose.html
631- https://scikit-learn.org/stable/modules/generated/sklearn.pipeline.Pipeline.html

Upstream references

  • https://scikit-learn.org/stable/modules/compose.html
  • https://scikit-learn.org/stable/modules/generated/sklearn.pipeline.Pipeline.html
Raw lines 628 to 631