Designing Effective Data Preprocessing Pipelines

Learn how to design robust data preprocessing pipelines that prepare raw data for modeling.

Topics

Real-world datasets rarely come perfectly formatted for modeling. A well-designed data preprocessing pipeline ensures that you apply the same transformations consistently across training and production environments.

Why a Pipeline Rather Than a Script

The argument for pipelines is not tidiness, it is correctness. Any transformation that learns something from the data, such as a mean for imputation, a scale factor, or a category vocabulary, is a fitted parameter. If it is estimated on the full dataset before splitting, information from the validation and test sets leaks into training, and your estimate of model performance becomes optimistic.

A pipeline enforces the boundary structurally. Fitting happens on training folds only, and the learned parameters are then applied unchanged to held-out data. The same fitted object is what you serialise and deploy, which eliminates the classic failure where preprocessing in production drifts away from preprocessing in the notebook.

Handling Missing Values

Start by assessing the extent of missing data. Common strategies include dropping incomplete rows, filling numeric columns with the mean or median, and using the most frequent category for categorical features. Before choosing, it is worth asking why the values are missing, because the mechanism determines what is safe:

  • Missing completely at random (MCAR). Missingness is independent of the variables relevant to the analysis under the stated model. Complete-case analysis can then be unbiased for many targets, but it is inefficient and still depends on the sampling/design assumptions.
  • Missing at random (MAR). Missingness depends on observed variables, for example income being unreported more often in certain regions. Conditional imputation can recover the structure.
  • Missing not at random (MNAR). Missingness depends on the unobserved value itself, such as high earners declining to state income. No imputation fixes this; the missingness is informative and should be modelled explicitly.

Median/mode imputation with missingness indicators is a useful predictive baseline, not a universally valid inferential treatment of missing data. For statistical inference, the imputation model and uncertainty propagation need more care. If the missingness carries signal, the model can use it; if not, the indicator costs almost nothing.

Encoding Categorical Variables

Many machine learning algorithms require numeric inputs. Techniques like one-hot encoding or ordinal encoding convert categories into numbers. Scikit-learn's ColumnTransformer allows you to apply different encoders to different columns in a single pipeline.

Choose based on the variable and the model. One-hot encoding is the safe default for nominal categories, though it becomes unwieldy at high cardinality. Ordinal encoding is correct only when the categories genuinely have an order, such as small, medium, large; applying it to unordered categories invents a numeric relationship the model will happily exploit. Tree-based models tolerate ordinal encoding of nominal variables better than linear models do, since trees can split the ordering into arbitrary groups.

Two details cause production failures. Unseen categories at prediction time will raise errors unless the encoder is configured to ignore them, so set handle_unknown="ignore". And target encoding, which replaces a category with the mean outcome for that category, leaks the label directly unless it is computed within cross-validation folds.

Scaling and Normalization

Scaling matters when the algorithm or penalty depends on feature magnitude. Unregularized ordinary least squares predictions, for example, are invariant to linear rescaling of columns even though the coefficient units change. Standardization (mean of zero, unit variance) is typical for linear models, while min-max scaling keeps values between 0 and 1.

$$ z = \frac{x - \mu}{\sigma}, \qquad x_{\text{minmax}} = \frac{x - x_{\min}}{x_{\max} - x_{\min}} . $$

Which models actually need it is worth knowing precisely. Distance-based methods such as k-nearest neighbours, k-means, and SVMs with RBF kernels require scaling, because the distance metric is otherwise dominated by whichever feature has the largest units. Gradient descent converges faster on scaled features, which matters for neural networks and for linear models fitted iteratively. Regularised regression needs it because the penalty applies equally to all coefficients, so unscaled features are penalised inconsistently.

Standard axis-aligned decision trees and their common ensembles generally do not need scaling because threshold splits depend on feature ordering. This does not imply every tree implementation is invariant to every transformation, especially when preprocessing or oblique splits are involved. Scaling them wastes effort without causing harm.

When outliers are present, RobustScaler centres on the median and scales by the interquartile range, so extreme values do not distort the parameters used for everything else.

Putting It All Together

Use scikit-learn's Pipeline to chain preprocessing steps with your model. This approach guarantees that the exact same transformations are applied when predicting on new data, reducing the risk of data leakage and improving reproducibility.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import numpy as np
from sklearn.compose import ColumnTransformer
from sklearn.ensemble import RandomForestClassifier
from sklearn.impute import SimpleImputer
from sklearn.model_selection import cross_val_score
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import OneHotEncoder, StandardScaler

numeric_features = ["age", "income", "tenure_months"]
categorical_features = ["region", "plan_type"]

numeric_pipe = Pipeline([
    ("impute", SimpleImputer(strategy="median", add_indicator=True)),
    ("scale", StandardScaler()),
])

categorical_pipe = Pipeline([
    ("impute", SimpleImputer(strategy="most_frequent")),
    ("encode", OneHotEncoder(handle_unknown="ignore", sparse_output=False)),
])

preprocess = ColumnTransformer([
    ("num", numeric_pipe, numeric_features),
    ("cat", categorical_pipe, categorical_features),
])

model = Pipeline([
    ("prep", preprocess),
    ("clf", RandomForestClassifier(n_estimators=300, random_state=0)),
])

scores = cross_val_score(model, X, y, cv=5, scoring="roc_auc")
print(f"ROC AUC: {scores.mean():.3f} +/- {scores.std():.3f}")

Because the whole object is passed to cross_val_score, imputation statistics, category vocabularies, and scaling parameters are refitted inside every fold. That is what makes the reported score an honest estimate rather than a lower bound on optimism.

Deploying the Same Object

Serialise the fitted pipeline rather than the model alone:

1
2
3
4
5
6
7
8
import joblib

model.fit(X_train, y_train)
joblib.dump(model, "churn_model.joblib")

# in the serving process
loaded = joblib.load("churn_model.joblib")
predictions = loaded.predict_proba(new_records)[:, 1]

The serving code now takes raw records in the original schema. There is no separate preprocessing implementation to keep in sync, which removes an entire category of training-serving skew.

Two operational cautions. Pickled objects are tied to library versions, so pin your dependencies and record the versions alongside the artefact. And a pipeline validates nothing about incoming data: pair it with an explicit schema check so that a renamed column or a unit change fails loudly instead of silently producing predictions from garbage.

References

  • Pedregosa, F., et al. (2011). Scikit-learn: Machine learning in Python. Journal of Machine Learning Research, 12, 2825-2830.
  • Kuhn, M., & Johnson, K. (2019). Feature Engineering and Selection: A Practical Approach for Predictive Models. CRC Press.
  • van Buuren, S. (2018). Flexible Imputation of Missing Data (2nd ed.). CRC Press.
  • Sculley, D., et al. (2015). Hidden technical debt in machine learning systems. Advances in Neural Information Processing Systems, 28.

Embed interactive plots, widgets, and demos using <figure>, <iframe>, or <div class="interactive-embed"> containers. Ensure each embed includes descriptive captions for accessibility.

How to cite

Use the quick export buttons to save citations for reference managers or copy the formatted text directly.

Diogo Ribeiro (2021). Designing Effective Data Preprocessing Pipelines. Faculty of Media Arts and Design, Technical University of Porto. https://diogoribeiro7.github.io/data-science/data_preprocessing_pipelines/.

BibTeX

RIS

EndNote

Related posts

  • A Data Lake Is a Directory With Rules

  • Why Exact Post-Selection Confidence Intervals Can Be Enormous

Loading mathematical content