How to normalize data with scikit-learn in Machine Learning
Normalizing data is one of the most important preprocessing steps in Machine Learning. When your features live on very different scales — for example, age between 0 and 100 and salary between 0 and 100000 — algorithms such as KNN, logistic regression or neural networks tend to give more weight to the higher-magnitude features, which hurts the model. Not every algorithm needs scaling — decision trees and random forests are indifferent to it — but for distance-based or gradient-based models, normalization is essential. Here you'll learn how to normalize data with scikit-learn, using StandardScaler and MinMaxScaler, and how to do it the right way: fitting the scaler only on the training data.
Prerequisites
- Python 3.9 or newer installed.
- The scikit-learn, pandas and NumPy libraries (install with
pip install scikit-learn pandas numpy). - Knowing how to load a dataset into a pandas DataFrame.
Step 1: Understand the difference between standardizing and normalizing
The two most common methods produce different results, so it helps to know when to use each one:
- Standardization (StandardScaler): rescales each feature to have a mean of 0 and a standard deviation of 1. It's the default choice for most algorithms and works well when the data is roughly normally distributed.
- Min-Max normalization (MinMaxScaler): squeezes the values into a fixed range, usually between 0 and 1. It's handy when you need known bounds, for example in neural networks or image processing.
The intuition is simple: algorithms that measure distances between points, like KNN, add up the differences across all features; if one feature has much larger values, it dominates the calculation and the others barely count. In both cases the golden rule is the same: fit the scaler only on the training data and apply the exact same transformation to the test data.
Step 2: Prepare and split the data
Before scaling, split the data into training and test sets. This makes sure that information from the test set never influences the mean and standard deviation — a common mistake known as data leakage. The random_state just makes the split reproducible.
import pandas as pd
from sklearn.model_selection import train_test_split
# Two features on very different scales
data = pd.DataFrame({
"age": [22, 35, 58, 45, 28, 60, 31],
"salary": [18000, 42000, 95000, 61000, 27000, 88000, 35000]
})
X_train, X_test = train_test_split(data, test_size=0.3, random_state=42)
print(X_train)
Step 3: Apply the StandardScaler
Create the scaler, fit it with fit on the training data and transform both sets with transform. Notice that fit is never called on the test data.
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
# fit_transform on the training set only
X_train_scaled = scaler.fit_transform(X_train)
# transform (no fit) on the test set
X_test_scaled = scaler.transform(X_test)
print(X_train_scaled)
The fit_transform method is just a shortcut for fit followed by transform. Applied to the training set, it learns the mean and standard deviation of each column and immediately returns the standardized values, which now vary around 0 (with negative and positive numbers).
Rule of thumb: callfitonly once, always on the training set. The test data only goes throughtransform.
Step 4: Alternative with the MinMaxScaler
If you'd rather map the values to a 0–1 range, just swap the scaler class — the fit and transform logic stays exactly the same.
from sklearn.preprocessing import MinMaxScaler
scaler_mm = MinMaxScaler(feature_range=(0, 1))
X_train_mm = scaler_mm.fit_transform(X_train)
X_test_mm = scaler_mm.transform(X_test)
Step 5: Put the scaler in a Pipeline (recommended)
In real projects, the best approach is to combine the scaler and the model in a Pipeline. That way scaling is always applied in the right order and you avoid repeating code or forgetting to transform new data.
from sklearn.pipeline import Pipeline
from sklearn.neighbors import KNeighborsClassifier
pipeline = Pipeline([
("scaler", StandardScaler()),
("model", KNeighborsClassifier(n_neighbors=3))
])
# pipeline.fit(X_train, y_train) # fits scaler + model in one call
When you call pipeline.fit, the scaler is fitted only on the training data and the model already receives the scaled values — all automatically.
Check the result
To confirm that standardization worked, check the statistics of the scaled training columns. With StandardScaler, each column's mean should be very close to 0 and its standard deviation close to 1.
import numpy as np
print("Mean:", np.round(X_train_scaled.mean(axis=0), 2))
print("Std:", np.round(X_train_scaled.std(axis=0), 2))
# Mean: [ 0. -0.] Std: [1. 1.]
If you used MinMaxScaler, confirm that, on the training set, each column's minimum is 0 and its maximum is 1. On the test data it's normal to see values slightly outside that range — that's expected and not an error.
Conclusion
You can now normalize data in Machine Learning with scikit-learn, choose between StandardScaler and MinMaxScaler and, most importantly, avoid data leakage by fitting the scaler only on the training set. The natural next step is to put this preprocessing inside a full Pipeline with a model and measure the impact on performance. Here's a tip to try: on your own dataset, does standardization improve the model's accuracy compared with the raw data?