Missing Value Imputation
Decide, don't delete
Missingness is information, not just a problem: you choose between dropping, simple imputation, model-based imputation, and explicit missing flags based on the mechanism behind the gaps.
What you will learn
- Profile missingness as MCAR / MAR / MNAR and choose a strategy per mechanism
- Implement mean, median, mode, constant, KNN and iterative imputers
- Decide when to drop rows/columns vs impute, and when a missing-flag column helps
What is this?
Missing value imputation replaces absent values so downstream models see a complete matrix. The choice of method depends on the mechanism: MCAR (missing completely at random), MAR (missing at random given observed predictors), or MNAR (missing related to the value itself). Dropping is sometimes right: when missingness is tiny (<5%) and MCAR, or when a column is mostly empty.
Why do you need it?
- Most model classes and many libraries reject NaN directly; a complete matrix is a hard prerequisite.
- Imputation keeps the row's other (informative) columns — dropping whole rows wastes signal.
- A missing-flag encodes the 'something unusual happened here' signal that imputation alone erases.
- The mechanism decides the method: MCAR is safe to drop, MNAR demands domain reasoning.
Mental model
Filling a pothole you haven't inspected
Blind mean-imputation
If the pothole hides a broken pipe, filling it with gravel is worse than flagging it. Missingness mechanics tell you whether gravel, a patch, or a yellow flag is the right move.
How it works
- Profile: df.isna().sum() per column, plus by-group missingness to spot MAR patterns.
- Mechanism guess: if missingness correlates with observed features → MAR; if it flags extreme values → MNAR.
- Pick method: drop (tiny MCAR), constant/mean/median (low missingness), KNN/iterative (arbitrary patterns), model-based (rich observed predictors).
- For trees, NaN can be handled natively (sklearn HistGradientBoosting); for linear models and neural nets, impute.
- Evaluate imputation at model level: score with and without, on the validation fold — not on imputation-error alone.
Choosing an imputation strategy.
Examples
SimpleImputer with a missing-flag
import pandas as pdimport numpy as npfrom sklearn.impute import SimpleImputer, KNNImputer df = pd.DataFrame({ "age": [34, 28, np.nan, 45, np.nan, 52], "income": [70_000, 88_000, 120_000, np.nan, 90_000, 84_000],}) imputer_mean = SimpleImputer(strategy="median")arr = imputer_mean.fit_transform(df)df_filled = pd.DataFrame(arr, columns=df.columns) df_filled["age_missing"] = df["age"].isna().astype(int)df_filled["income_missing"] = df["income"].isna().astype(int) print(df_filled)print("imputer medians:", imputer_mean.statistics_) knn = KNNImputer(n_neighbors=2)print("KNN result:")print(knn.fit_transform(df))The median imputer fills NaN with learned medians, and separate is_missing flags preserve the fact that a value was absent — a signal some models should learn from.
In production
- Credit scoring: income missingness strongly predicts default risk (MNAR-ish) — keep the flag, impute conservatively.
- IOT sensor floods: impute last-observed-value; missingness counts feed a separate 'signal quality' model.
- Healthcare: MAR missingness driven by lab-ordering patterns; use MICE/iterative with domain priors.
Best practices
- Impute with the mechanism in mind, not habit; impute median for skewed columns, mean for symmetric ones; fit the imputer on train folds only; add flags when missingness exceeds ~5% or the mechanism is MNAR; re-run imputation checks after every ETL change.
Common mistakes
- Using the column mean on heavily skewed data; imputing with the global mean per-fold (should be fold-local); deleting rows when other columns are intact; assuming all NaN are equal (None vs empty-string vs 0 are different bugs); imputing test data with the test column's own statistics.
Debugging tips
- Compare missingness percentage train vs prod — drift here is a pipeline bug, not a modeling tweak.
- Plot a heatmap of column-pair missingness to spot shared-cause gaps.
- After imputation confirm no NaN survived: df_filled.isna().sum().sum() == 0.
Interview checkpoint
Quick quiz
Which missingness mechanism implies the gap is related to the unobserved value itself?
Your income column is 40% missing. Best first move?