← All Posts
Deep Learning · Data Preparation and Preprocessing · Feature Scaling

Robust Scaling

Robust scaling centers a feature using its median and scales it using a quantile-based spread. The usual spread is the interquartile range (IQR), the gap between the 75th and 25th percentiles.

The aim is to keep a few extreme observations from setting the coordinate system for everyone else. The observations themselves are retained. Robust scaling is not clipping, outlier removal, or a robust prediction model.

Locate the middle and the middle-half spread

Use the nine sorted training values 0, 1, 2, 3, 4, 5, 6, 7, and 100. The median is 4. Under the linear percentile convention used here, the 25th percentile is 2 and the 75th percentile is 6. Their difference is 4.

Percentiles follow position in the sorted sample
0
1
2Q1
3
4median
5
6Q3
7
100
IQR = Q3 − Q1 = 6 − 2 = 4. Equal spacing between these cards represents rank, not numerical distance. The gap from 7 to 100 is much larger than the others.

Let $m$ be the saved training median and let $r=Q_3-Q_1$ be the saved positive IQR. The default transform is

$$z=\frac{x-m}{r}.$$

The value 6 becomes (6 − 4) / 4 = 0.5: half an IQR above the fitted median. The value 100 becomes (100 − 4) / 4 = 24. It remains extreme.

Move the largest value and compare the fitted statistics

In this example, increasing the largest value from 8 to 100 does not move the median or either quartile. Their positions in the sorted sample are unchanged. The mean and standard deviation, however, use the magnitudes of all observations.

Change one extreme value, keep the other eight fixed
Median / IQRCenter: 4.000
Scale: 4.000
6 → 0.500
Mean / stdCenter: 14.222
Scale: 30.404
6 → -0.270
Robust scaling sends the largest value 100 to 24.000. It is still present and far from the center.
These are two scalers fitted to the same changing reference sample, not two prediction-time refits. The median/IQR stability shown here depends on the chosen sample and which observation changes.

With a large extreme value, standardization can compress the ordinary values into a small part of the transformed scale. Robust scaling can preserve more separation among those ordinary values, while making the extreme observation large relative to the IQR. Which representation helps the downstream estimator still needs validation.

Quantile definitions are part of the fitted transform

Percentiles in a finite sample sometimes lie between observed values. This note uses linear interpolation at position $p(n-1)$ in a sorted sample of $n$ values, with positions starting at 0 and $p$ between 0 and 1.

For [1, 2, 3, 4], the 25th-percentile position is 0.75, giving 1.75. The 75th percentile is 3.25, so the IQR is 1.5. A textbook rule that takes the median of each half can give different numbers. Match the percentile convention when reproducing a stored scaler.

Other quantile ranges and unit-variance calibration

quantile_range can use endpoints other than 25 and 75. A wider interval can involve more of the tails. In the nine-value example, the 90th percentile interpolates between 7 and the largest value, so it does change when that extreme value moves.

Default robust scaling gives a nonzero training IQR of 1 under the same percentile convention. It does not generally give variance 1. Scikit-learn’s unit_variance=True adjusts the divisor using a normal-distribution reference for the chosen quantiles; it does not force an arbitrary observed distribution to have unit variance.

A zero IQR does not always mean a constant feature

Consider eight zeros and one value of 10. The median, Q1, and Q3 are all zero, even though the feature varies. There is no positive IQR to divide by.

Scikit-learn’s default configuration uses a scale factor of 1 for a zero IQR. In this example, 10 stays 10 after transformation. Inspect zero and very small fitted spreads, especially for discrete or zero-heavy features. A small IQR can amplify tail values substantially.

Python: inspect the saved median and IQR

The reference pool below is the original sample with largest value 100. Future inputs reuse its center and spread. The inverse multiplies by the saved scale and adds the saved median.

Run the transform and zero-IQR example
import numpy as np
from sklearn.preprocessing import RobustScaler

train = np.array([0., 1., 2., 3., 4., 5., 6., 7., 100.]).reshape(-1, 1)
scaler = RobustScaler().fit(train)
new = np.array([[-4.], [6.], [100.]])
print(scaler.center_.tolist())
print(scaler.scale_.tolist())
print(scaler.transform(new).ravel().tolist())
assert np.allclose(scaler.inverse_transform(scaler.transform(new)), new)

# Nonconstant data can still have a zero IQR.
mostly_zero = np.array([0.] * 8 + [10.]).reshape(-1, 1)
zero_iqr = RobustScaler().fit(mostly_zero)
assert zero_iqr.scale_.tolist() == [1.]
assert zero_iqr.transform([[10.]]).tolist() == [[10.]]
[4.0]
[4.0]
[-2.0, 0.5, 24.0]

A robust coordinate system is only one part of the model

Robust scaling does not limit a transformed value to a fixed interval. It does not remove skewness or make least squares robust to unusual observations. A model may still be strongly influenced by the retained extreme inputs or targets.

The fitted median and quantiles can also move when enough observations change, when the sample is small, or when a different population is used. “Robust” describes reduced sensitivity of these statistics to some contamination patterns; it is not immunity to data problems.

Choose the scaler and model together. If clipping, a nonlinear transform, or a robust loss is justified by the task, treat it as a separate operation with a separate effect. The clipping example shows the information that saturation loses.

Fit only the permitted reference data

Learn medians and quantiles inside each cross-validation training fold. Do not fit them on validation data or update them independently of the model at serving time. Preserve the feature order, quantile settings, and fitted statistics.

Centering a sparse matrix can destroy sparsity. Consider with_centering=False or another appropriate transform when sparse storage matters; that configuration does not produce median-zero columns. See scaling sparse data. Missing-data treatment also needs its own explicit policy.

C++: the same interpolation convention

This implementation sorts a finite reference feature, interpolates quantiles, and saves the median and IQR. It implements the default 25–75% range and unit-variance option off. Sorting costs O(n log n); each later lookup uses the two saved numbers.

Compile the median/IQR scaler
#include <algorithm>
#include <cmath>
#include <iomanip>
#include <iostream>
#include <stdexcept>
#include <vector>

// Input is already sorted; p is a fraction from 0 to 1.
double quantile(const std::vector<double>& sorted, double p) {
    if (sorted.empty() || !std::isfinite(p) || p < 0 || p > 1)
        throw std::invalid_argument("invalid quantile input");
    const double position = (sorted.size() - 1) * p;
    const auto lo = static_cast<std::size_t>(std::floor(position));
    const auto hi = static_cast<std::size_t>(std::ceil(position));
    return sorted[lo] + (position - lo) * (sorted[hi] - sorted[lo]);
}
class RobustScaler {
    double center = 0, scale = 1;
public:
    explicit RobustScaler(std::vector<double> reference) {
        if (reference.empty()) throw std::invalid_argument("empty reference");
        for (double x : reference)
            if (!std::isfinite(x)) throw std::invalid_argument("finite values required");
        std::sort(reference.begin(), reference.end());
        center = quantile(reference, .5);
        const double iqr = quantile(reference, .75) - quantile(reference, .25);
        if (iqr > 0) scale = iqr;
    }
    double transform(double x) const { return (x - center) / scale; }
    double inverse(double z) const { return center + z * scale; }
};
int main() {
    const RobustScaler encoder({0,1,2,3,4,5,6,7,100});
    std::cout << std::fixed << std::setprecision(2);
    for (double x : {-4,6,100})
        std::cout << x << " -> " << encoder.transform(x) << "\n";
}
-4.00 -> -2.00
6.00 -> 0.50
100.00 -> 24.00

References and further reading