# 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. - Published: 2026-09-02 - Updated: 2026-09-02 - Author: Anthony Fillion-Maillet - Reading time: 5 min --- Scikit-learn pipelines transformeren chaotische preprocessing-code in reproduceerbare, productie-klare workflows. Met [versie 1.9](https://scikit-learn.org/stable/whats_new/v1.9.html), 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. ## 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](https://scikit-learn.org/stable/whats_new/v1.9.html) 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 --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/nl/blog/data-science/scikit-learn-pipelines-feature-engineering-interview-2026