EDA and Feature Engineeringpandas 3.0 · scikit-learn 1.9 · seaborn 0.13 · Python 3.12+
Dashboard
0%
1
Curious builder0 XP earned · 300 to level 2
0 daysFinish a lesson to begin
Badge collection0 of 6 unlocked
72 small wins to finish your pathNext lesson →

SimpleImputer in scikit-learn

SimpleImputer is a scikit-learn transformer that learns a fill value per column when it is fitted and applies it when it transforms, which is the same arithmetic as the pandas techniques with the fill value remembered as part of a fitted object.

Last updated: 12 Oct, 2026 · pandas 3.0 · scikit-learn 1.9

Every technique up to Missing as its own category computed its fill value and applied it in the same line. That is fine in a notebook and wrong the moment there is a test set, because the test rows must be filled with the values learned from the training rows.

A chooser for a missing value technique. For a numeric column: a symmetric column with few gaps takes the mean, a skewed one takes the median, a column whose shape must survive takes a random sample, and a column whose gaps mean something takes an indicator column, an end of distribution value or an arbitrary value. For a text column: few gaps take the most frequent category, and many gaps or meaningful gaps take a Missing label. The right hand column gives the matching SimpleImputer strategy for each: mean, median, most_frequent and constant, with add_indicator for the indicator column.

Syntax: SimpleImputer(strategy=...) takes 'mean', 'median', 'most_frequent' or 'constant' with a fill_value. fit learns the values, transform applies them, statistics_ holds what was learned, and add_indicator=True appends the flag column.

python
imputer = SimpleImputer(strategy='median')
imputer.fit(X_train[['Age']])         # learn 28.0 from the training rows only
imputer.transform(X_test[['Age']])    # fill the test rows with that same 28.0
imputer.statistics_                   # the learned fill value per column
SimpleImputer(strategy='constant', fill_value='Missing')   # the text version

Setting up scikit-learn and the data

set_config(transform_output='pandas') makes every scikit-learn transformer return a DataFrame with column names instead of a bare numpy array, which removes the usual pd.DataFrame(result, columns=...) wrapping. Run this block first, then the example under it.

python
import pandas as pd
from sklearn.impute import SimpleImputer
from sklearn import set_config

set_config(transform_output='pandas')     # transformers return frames, not arrays
url = ("https://raw.githubusercontent.com/krishnaik06/"
       "Feature-Engineering-Live-sessions/master/titanic.csv")
df = pd.read_csv(url, usecols=['Age', 'Fare', 'Embarked', 'Cabin'])

Learning the fill value and reading it back

Fitting on two numeric columns learns two medians, and statistics_ prints them. They are the same numbers the pandas techniques computed, which is the point: nothing new is being calculated, it is being remembered.

ExampleTwo medians, learned and applied
num = SimpleImputer(strategy='median')
filled = num.fit_transform(df[['Age', 'Fare']])
print("learned fill values:", num.statistics_)
print("nulls before:", df[['Age', 'Fare']].isnull().sum().to_dict())
print("nulls after: ", filled.isnull().sum().to_dict())
print(type(filled).__name__, "with columns", list(filled.columns))
  • The learned values are 28.0 and 14.4542. The first is the Age median that Mean and median imputation filled with by hand; the second is the Fare median.
  • 177 nulls become 0. Fare had none to begin with, and the imputer still learned a fill value for it, ready for a test set that does have one.
  • The result is a DataFrame. That is set_config(transform_output='pandas') at work; without it the same call returns a numpy array with no column names.
  • The fill value lives in the object. num can now be applied to any frame with those two columns, which is what makes it safe across a split.

The strategies for a text column

'most_frequent' is frequent category imputation and 'constant' with a fill_value is the Missing label, the two techniques the categorical lessons did by hand. Both work on text columns, where mean and median would raise.

Examplemost_frequent and constant on two text columns
cat = SimpleImputer(strategy='most_frequent')
embarked = cat.fit_transform(df[['Embarked']])
print("most frequent port:", cat.statistics_)
print("nulls left:", int(embarked['Embarked'].isnull().sum()))

const = SimpleImputer(strategy='constant', fill_value='Missing')
cabin = const.fit_transform(df[['Cabin']])
print(cabin['Cabin'].value_counts().head(3).to_string())

The port of embarkation is filled with S, which is the mode of that column, and the 687 missing cabins become the label Missing, which is now the largest level by a distance. The next largest levels are two real cabins, each shared by four passengers.

Keeping the indicator in the same call

add_indicator=True appends one extra column per input column that had a gap, named missingindicator_<column>. It is the technique from Capturing NaN with an indicator column without the two lines of numpy, and the flag is built before the fill, in the right order, by the transformer.

ExampleOne column in, two columns out
both = SimpleImputer(strategy='median', add_indicator=True)
out = both.fit_transform(df[['Age']])
print("columns out:", list(out.columns))
print(out.head(7).to_string())
print("rows flagged:", int(out['missingindicator_Age'].sum()))

Row 5 reads 28.0 with its indicator at 1.0, which is the same pair of values the hand written version produced. The indicator comes back as a float because the output is one numeric block.

SimpleImputer vs fillna

fillnaSimpleImputer
Where the fill value livesin a variable you manageinside the fitted object
Across a train and test splityou must remember to reuse the training valuefit on train, transform on test
Works inside a Pipelinenoyes
Strategiesanything you can computemean, median, most_frequent, constant
Indicator columntwo lines of numpyadd_indicator=True
Random sample imputationyes, by handno, it is not a strategy
Returnsa Seriesa frame with transform_output='pandas', otherwise an array

Where SimpleImputer belongs

  • Any model that will meet new data. The fill value has to come from the training rows, and a fitted transformer is what carries it.
  • Inside a ColumnTransformer. Numeric columns take the median strategy and text columns the constant one, in one object, which is what ColumnTransformer assembles.
  • As one step of a pipeline. Imputer, encoder, scaler and model fitted together on the training fold only, which is the shape Pipelines and pandas output builds.
Watch out. fit_transform on the whole dataset before splitting defeats the point: the median then comes from the test rows as well as the training ones. Split first, call fit_transform on the training part and transform on the test part, which is what Split first, then fit covers.
Try it yourself
  • Change strategy='median' to 'mean' and read what statistics_ prints.
  • Remove the set_config line and check what type fit_transform returns.
  • Fit an imputer on the first 600 rows and transform the last 291, then compare statistics_ with the median of the whole column.

Little by little, you're building something great.