Scikit-Learn Pipelines in 2026: Feature Engineering en Sollicitatievragen

Scikit-learn pipelines met ColumnTransformer voor feature engineering beheersen. Best practices voor preprocessing en voorbereiding op ML-sollicitaties.

Scikit-Learn Pipelines in 2026: Feature Engineering en Sollicitatievragen

Scikit-learn pipelines transformeren chaotische preprocessing-code in reproduceerbare, productie-klare workflows. Met versie 1.9, uitgebracht in juni 2026, blijven Pipeline en ColumnTransformer de basis van elk serieus machine learning project, nu met callback-monitoring, verbeterde HTML-visualisatie en Array API-ondersteuning.

Sollicitatietip

Interviewers vragen vaak: "Hoe voorkom je data leakage tijdens cross-validatie?" Het antwoord is pipelines. Het fitten van preprocessing-stappen binnen de CV-loop zorgt ervoor dat testdata nooit scaling- of encoding-beslissingen beïnvloeden.

Waarom Pipelines Data Leakage Elimineren

Data leakage treedt op wanneer informatie uit de testset de preprocessing beïnvloedt. Een veelgemaakte fout: de StandardScaler fitten op de gehele dataset voordat wordt gesplitst. De scaler leert gemiddelde en variantie van testsamples, waardoor cross-validatiescores kunstmatig worden verhoogd.

Pipelines lossen dit op door transformers en estimators te ketenen in één object dat alle stappen samen fit:

python
# pipeline_basics.py
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import cross_val_score

# Create a pipeline that scales features and trains a classifier
pipeline = Pipeline([
    ('scaler', StandardScaler()),  # Step 1: Normalize features
    ('classifier', LogisticRegression(max_iter=1000))  # Step 2: Train model
])

# cross_val_score fits the scaler separately for each fold
# No data leakage: test fold is never seen during scaler.fit()
scores = cross_val_score(pipeline, X, y, cv=5, scoring='accuracy')
print(f"Mean accuracy: {scores.mean():.3f} (+/- {scores.std():.3f})")

Het Pipeline-object implementeert fit, predict en score, waardoor het uitwisselbaar is met elke scikit-learn estimator. Dit betekent dat GridSearchCV, RandomizedSearchCV en alle cross-validatie utilities werken zonder aanpassingen.

ColumnTransformer voor Gemengde Datatypen

Echte datasets bevatten numerieke kolommen (leeftijd, salaris) en categorische kolommen (land, producttype). ColumnTransformer past verschillende preprocessing toe op verschillende kolomsubsets en voegt de resultaten samen:

python
# column_transformer_example.py
from sklearn.compose import ColumnTransformer
from sklearn.preprocessing import StandardScaler, OneHotEncoder
from sklearn.impute import SimpleImputer
from sklearn.pipeline import Pipeline
import pandas as pd

# Sample dataset with mixed types
df = pd.DataFrame({
    'age': [25, 30, None, 45],
    'salary': [50000, 60000, 75000, None],
    'country': ['US', 'UK', 'DE', 'US'],
    'education': ['Bachelor', 'Master', 'PhD', 'Bachelor']
})

# Define numeric preprocessing: impute missing values, then scale
numeric_transformer = Pipeline([
    ('imputer', SimpleImputer(strategy='median')),
    ('scaler', StandardScaler())
])

# Define categorical preprocessing: impute with mode, then one-hot encode
categorical_transformer = Pipeline([
    ('imputer', SimpleImputer(strategy='most_frequent')),
    ('encoder', OneHotEncoder(handle_unknown='ignore', sparse_output=False))
])

# Combine transformers with ColumnTransformer
preprocessor = ColumnTransformer(
    transformers=[
        ('num', numeric_transformer, ['age', 'salary']),
        ('cat', categorical_transformer, ['country', 'education'])
    ],
    verbose_feature_names_out=True  # Prefix output names with transformer name
)

# Fit and inspect output shape
X_transformed = preprocessor.fit_transform(df)
print(f"Transformed shape: {X_transformed.shape}")
print(f"Feature names: {preprocessor.get_feature_names_out()}")

De parameter verbose_feature_names_out (uitgebreid in versie 1.6 om strings en callables te accepteren) regelt de naamgeving van output-features. Door deze op True te zetten wordt elke feature voorafgegaan door de transformernaam, waardoor naamconflicten worden voorkomen wanneer meerdere transformers vergelijkbare kolomnamen genereren.

Automatische Kolomselectie met make_column_selector

Hardgecodeerde kolomnamen falen wanneer datasets veranderen. make_column_selector selecteert kolommen automatisch op basis van dtype:

python
# automatic_column_selection.py
from sklearn.compose import ColumnTransformer, make_column_selector
from sklearn.preprocessing import StandardScaler, OneHotEncoder

# Select columns by dtype: no hardcoded column names
preprocessor = ColumnTransformer(
    transformers=[
        ('num', StandardScaler(), make_column_selector(dtype_include='number')),
        ('cat', OneHotEncoder(handle_unknown='ignore'),
         make_column_selector(dtype_include='object'))
    ],
    remainder='passthrough'  # Keep unprocessed columns as-is
)

# Works with any DataFrame that has numeric and object columns
X_processed = preprocessor.fit_transform(df)

De parameter remainder bepaalt wat er gebeurt met kolommen die niet door een transformer worden gematcht. Opties zijn 'drop' (standaard), 'passthrough' (ongewijzigd behouden) of een transformer om toe te passen op de resterende kolommen.

Klaar om je Data Science & ML gesprekken te halen?

Oefen met onze interactieve simulatoren, flashcards en technische tests.

Feature Engineering Binnen Pipelines

Pipelines gaan verder dan preprocessing en omvatten feature engineering-stappen. Aangepaste transformers erven van BaseEstimator en TransformerMixin:

python
# custom_transformer.py
from sklearn.base import BaseEstimator, TransformerMixin
import numpy as np

class DateFeatureExtractor(BaseEstimator, TransformerMixin):
    """Extract year, month, day, and weekday from datetime columns."""
    
    def __init__(self, date_column='date'):
        self.date_column = date_column
    
    def fit(self, X, y=None):
        # No fitting required for date extraction
        return self
    
    def transform(self, X):
        X = X.copy()
        dates = pd.to_datetime(X[self.date_column])
        
        # Extract temporal features
        X['year'] = dates.dt.year
        X['month'] = dates.dt.month
        X['day'] = dates.dt.day
        X['weekday'] = dates.dt.weekday
        X['is_weekend'] = (dates.dt.weekday >= 5).astype(int)
        
        # Drop original date column
        return X.drop(columns=[self.date_column])
    
    def get_feature_names_out(self, input_features=None):
        # Required for Pipeline.get_feature_names_out() to work
        return ['year', 'month', 'day', 'weekday', 'is_weekend']

# Integrate into a full pipeline
full_pipeline = Pipeline([
    ('date_features', DateFeatureExtractor(date_column='signup_date')),
    ('preprocessor', preprocessor),
    ('model', LogisticRegression())
])

Het implementeren van get_feature_names_out maakt het mogelijk voor de nieuwe HTML-representatie in scikit-learn 1.9 om output-featurenamen weer te geven, wat debugging en documentatie vereenvoudigt.

Hyperparameter Tuning Over Pipeline-Stappen

GridSearchCV tunet parameters over alle pipeline-stappen met de step__parameter naamgevingsconventie:

python
# pipeline_gridsearch.py
from sklearn.model_selection import GridSearchCV
from sklearn.ensemble import RandomForestClassifier

# Build pipeline with tunable components
pipeline = Pipeline([
    ('preprocessor', preprocessor),
    ('classifier', RandomForestClassifier(random_state=42))
])

# Define parameter grid: note the double underscore syntax
param_grid = {
    'preprocessor__num__imputer__strategy': ['mean', 'median'],
    'classifier__n_estimators': [100, 200],
    'classifier__max_depth': [10, 20, None]
}

# GridSearchCV handles all combinations automatically
grid_search = GridSearchCV(
    pipeline,
    param_grid,
    cv=5,
    scoring='f1_weighted',
    n_jobs=-1
)

grid_search.fit(X_train, y_train)
print(f"Best params: {grid_search.best_params_}")
print(f"Best score: {grid_search.best_score_:.3f}")

De dubbele underscore (__) geeft toegang tot geneste parameters. Voor ColumnTransformer bevat het pad de transformernaam: preprocessor__num__imputer__strategy bereikt de strategy-parameter van de imputer binnen de numerieke transformer.

Transformers Cachen met de memory Parameter

Complexe preprocessing op grote datasets kan traag zijn. De memory parameter cachet gefitte transformers naar schijf:

python
# cached_pipeline.py
from sklearn.pipeline import Pipeline
from tempfile import mkdtemp
import joblib

# Create a cache directory
cachedir = mkdtemp()

# Pipeline caches intermediate steps
cached_pipeline = Pipeline(
    [
        ('preprocessor', preprocessor),  # Cached after first fit
        ('classifier', RandomForestClassifier())
    ],
    memory=cachedir  # Or use joblib.Memory for more control
)

# First fit: slow (computes all transformations)
cached_pipeline.fit(X_train, y_train)

# Second fit with same X_train: fast (loads cached transformers)
cached_pipeline.fit(X_train, y_train)  # Skips preprocessor fitting

Caching is bijzonder nuttig tijdens hyperparameter-zoeken, waarbij dezelfde preprocessing wordt toegepast op meerdere modelconfiguraties. De finale estimator (classifier of regressor) wordt nooit gecachet, alleen tussentijdse transformers.

Training Monitoring met Callbacks (scikit-learn 1.9)

De callback-API in versie 1.9 voegt voortgangsmonitoring toe aan pipelines:

python
# callback_monitoring.py
from sklearn.callback import ProgressBar, ScoringMonitor
from sklearn.linear_model import LogisticRegression

# Display progress during GridSearchCV
with ProgressBar():
    grid_search = GridSearchCV(
        pipeline,
        param_grid,
        cv=5,
        n_jobs=1  # Progress bars require single-threaded execution
    )
    grid_search.fit(X_train, y_train)

# Monitor scoring metrics per iteration
with ScoringMonitor(X_val, y_val, scoring='accuracy') as monitor:
    logreg = LogisticRegression(solver='lbfgs', max_iter=500)
    logreg.fit(X_train, y_train)
    print(f"Scores per iteration: {monitor.scores_}")

Callbacks werken met Pipeline, StandardScaler, LogisticRegression (LBFGS-solver) en alle Search CV-klassen. Deze experimentele functie biedt inzicht in langlopende fits zonder aangepaste logging-code.

FeatureUnion voor Parallelle Feature-Extractie

FeatureUnion voert meerdere transformers parallel uit en voegt hun output horizontaal samen:

python
# feature_union_example.py
from sklearn.pipeline import FeatureUnion, Pipeline
from sklearn.decomposition import PCA
from sklearn.feature_selection import SelectKBest, f_classif

# Combine PCA and univariate selection
feature_union = FeatureUnion([
    ('pca', PCA(n_components=5)),
    ('select', SelectKBest(f_classif, k=10))
])

# Pipeline: preprocess, combine features, classify
pipeline = Pipeline([
    ('preprocessor', preprocessor),
    ('features', feature_union),
    ('classifier', LogisticRegression())
])

# Output contains 5 PCA components + 10 selected features = 15 features
pipeline.fit(X_train, y_train)

Versie 1.7.2 voegde validatie toe die ervoor zorgt dat alle transformers 2D-outputs retourneren. Dit detecteert fouten vroegtijdig wanneer een aangepaste transformer per ongeluk een 1D-array retourneert.

Serialisatie en Deployment

Pipelines worden geserialiseerd met joblib, waarbij alle gefitte parameters behouden blijven:

python
# model_deployment.py
import joblib

# Save the entire pipeline (preprocessing + model)
joblib.dump(pipeline, 'churn_predictor.pkl')

# Load in production
loaded_pipeline = joblib.load('churn_predictor.pkl')

# Predict on new data (same preprocessing automatically applied)
new_data = pd.DataFrame({'age': [32], 'salary': [65000], ...})
predictions = loaded_pipeline.predict(new_data)

Het geserialiseerde bestand bevat alles: kolomselectie-logica, gefitte scaler-gemiddelden en varianties, encoder-categorieën en modelgewichten. Deployment vereist alleen scikit-learn en joblib, geen aangepaste preprocessing-code.

Versiecompatibiliteit

Pipelines geserialiseerd met scikit-learn 1.8 laden mogelijk niet correct op 1.9 als ze verouderde parameters gebruiken. De scikit-learn versie moet altijd worden vastgezet in productie en upgrades moeten worden getest voor deployment.

Sollicitatievragen over Scikit-Learn Pipelines

V: Wat is data leakage en hoe voorkomen pipelines het?

Data leakage treedt op wanneer informatie uit de validatie- of testset de modeltraining beïnvloedt. Het klassieke voorbeeld: een StandardScaler fitten op de gehele dataset voordat wordt gesplitst. Het gemiddelde en de standaarddeviatie van de scaler bevatten testsamples, waardoor cross-validatiescores kunstmatig worden verhoogd.

Pipelines voorkomen dit door preprocessing in te kapselen binnen de cross-validatie-loop. Wanneer cross_val_score pipeline.fit() aanroept, fit elke fold de scaler alleen op trainingsdata. Testfold-statistieken lekken nooit naar de preprocessing.

V: Hoe krijgt men toegang tot en wijzigt men parameters van geneste pipeline-stappen?

De dubbele underscore-syntax wordt gebruikt: pipeline.set_params(classifier__n_estimators=200). Voor ColumnTransformer wordt de transformernaam opgenomen: pipeline.set_params(preprocessor__num__scaler__with_mean=False). De methode get_params() retourneert alle geneste parameters als een plat dictionary, nuttig voor inspectie en logging.

V: Wanneer moet FeatureUnion vs ColumnTransformer worden gebruikt?

ColumnTransformer past verschillende transformers toe op verschillende kolommen van dezelfde input. FeatureUnion past verschillende transformers toe op de gehele input en voegt horizontaal samen. ColumnTransformer wordt gebruikt voor heterogene kolompreprocessing (numeriek vs. categorisch). FeatureUnion wordt gebruikt voor feature-augmentatie, waarbij meerdere extractiemethoden (PCA, polynomiale features, domeinspecifieke extractors) complementaire features produceren uit dezelfde data.

V: Hoe debugt men een falende pipeline?

verbose=True instellen op de pipeline om staptimings te zien. Toegang tot tussenresultaten met pipeline.named_steps['step_name'].transform(X) voor een gefitte pipeline. In scikit-learn 1.9 toont de HTML-representatie gefitte attributen en output-featurenamen, wat debugging in Jupyter notebooks vereenvoudigt. Voor diepere inspectie pipeline[:-1].fit_transform(X, y) gebruiken om de output vlak voor de finale estimator te krijgen.

Productie-Checklist voor ML Pipelines

  • Scikit-learn versie vastzetten in requirements om serialisatieproblemen te voorkomen
  • make_column_selector gebruiken in plaats van hardgecodeerde kolomnamen wanneer datasets kunnen veranderen
  • handle_unknown='ignore' instellen op OneHotEncoder om nieuwe categorieën in productie te verwerken
  • Dure preprocessing cachen tijdens ontwikkeling met de memory parameter
  • Pipeline output-shape en featurenamen valideren in tests voor deployment
  • get_feature_names_out() opslaan samen met het model voor feature-importantie-analyse
  • Pipeline gebruiken binnen GridSearchCV, nooit andersom, om correct CV-gedrag te garanderen

Begin met oefenen!

Test je kennis met onze gespreksimulatoren en technische tests.

Dagelijkse challenge

Zie jij de bug in Data Science & ML?

Een echt codefragment, een verborgen bug, één poging per dag. Zonder account uit te proberen.

Anthony Fillion-Maillet

Geschreven door

Anthony Fillion-Maillet

Oprichter van SharpSkill

Al meer dan 10 jaar fullstack-ontwikkelaar. Hij leidt SharpSkill en staat in voor alles wat hier verschijnt.

Bijgewerkt op 2 september 2026

Delen

Gerelateerde artikelen