Sam Austinon October 19, 202610 min read

Permutation Feature Importance Explained with Scikit-learn

Permutation Feature Importance Explained with Scikit-learn
Contents

Figure 1: Permutation Feature Importance Explained with Scikit-learn — the bar chart your forest prints, and the four ways it quietly lies to you

Your random forest says feature #3 is the second most important thing in your data. Feature #3 is a column of random noise. That is not a rare bug; it is the default output of the most commonly printed importance chart in machine learning.

Permutation feature importance is the usual fix for that kind of embarrassment. It works in four lines of code and it fails in a few predictable ways. I ran the real scikit-learn implementation on data where I already knew the truth, so I can show you both sides.

Permutation importance answers a narrow question well, and it misleads you in three specific situations: training data, correlated features, and the wrong metric.

How Permutation Importance Works

The scikit-learn docs define permutation importance as a technique that involves randomly shuffling the values of a single feature and observing the resulting degradation of the model's score. Shuffling breaks the link between that feature and the target, so the score drop tells you how much the model relied on it.

The algorithm has three steps:

  1. Score the fitted model on a dataset. This is your baseline.
  2. Shuffle one column, rescore, and repeat K times. That is what n_repeats controls.
  3. Subtract: importance equals the baseline score minus the average shuffled score.

Three things follow directly from that recipe:

  • It is model-agnostic. The docs say it can be applied to any fitted estimator.
  • It is metric-dependent. It measures lost score, whatever score you choose.
  • It is model-specific. Per the docs, it reflects how important a feature is for a particular model, not the feature's intrinsic value.

That last point is the one to keep in mind when someone asks what a feature is worth. The number describes this model on this data, not the world.

The Scikit-learn Call

Here is the signature from the docs:

from sklearn.inspection import permutation_importance

result = permutation_importance(
    estimator, X, y, *,
    scoring=None, n_repeats=5, n_jobs=None,
    random_state=None, sample_weight=None, max_samples=1.0,
)

result.importances_mean   # average score drop per feature
result.importances_std    # spread across the repeats
result.importances        # the raw array, one row per repeat

Pass a fitted model, a dataset, and a random_state for reproducibility. I ran the docs' own six-row example and got importances_mean of [0.4667, 0, 0] and importances_std of [0.2211, 0, 0], which matches the documented values up to rounding. Starting from the documented example is a good habit, because it proves your setup can reproduce a known result before you trust it on your own data.

My Test Setup

My data has five columns. Two drive the label, with x₀ strongly and x₁ more weakly, and three are pure noise: one continuous, one binary, and one with four levels. I trained 100 fully grown random-forest trees on 700 rows, held out 300, and averaged over 20 datasets.

from sklearn.ensemble import RandomForestClassifier

model = RandomForestClassifier(n_estimators=100, random_state=0).fit(X_train, y_train)
print(model.score(X_train, y_train), model.score(X_test, y_test))

Because I built the noise columns myself, I know exactly which features deserve zero importance, which is the part most tutorials skip.

Train vs Test: Why You Want Held-Out Data

The forest scored 1.000 accuracy on training data and 0.810 on held-out data. It memorized its homework. Here is what each importance measure reported:

Measure x₀ x₁ Noise (continuous) Noise (binary) Noise (4 levels)
Impurity (MDI) 0.530 0.265 0.149 0.018 0.038
Permutation, train 0.402 0.214 0.089 0.024 0.050
Permutation, test 0.249 0.087 −0.001 0.001 −0.001

Look at the noise columns:

  • Impurity importance hands the continuous noise feature 0.149, more than eight times what the binary noise feature receives. The docs describe the same tilt: impurity importance favors high-cardinality features over low-cardinality ones such as binary columns.
  • Permutation importance on the training set also credits noise, because the forest memorized it. The docs warn that features which look important on training data but not on held-out data may be causing overfitting.
  • Permutation importance on the held-out set puts every noise feature at about zero. That is the answer you want.

The takeaway is simple: compute permutation importance on held-out data. It costs you one extra split and it removes the entire first class of misleading results.

Correlated Features Split the Credit

Now the harder case. I made x_b a near-copy of x_a, with a correlation of about 0.995. The label depends on x_a and an independent x_c, and never on x_b.

Setup x_a x_b x_c
Model without the copy 0.248 – 0.090
Model with the copy, shuffle one at a time 0.134 0.078 0.093
Model with the copy, shuffle x_a and x_b together 0.264 (group) – 0.094

Three things show up in that table:

  1. x_a drops from 0.248 to 0.134 once its copy exists, because the model still has x_b to lean on when x_a is shuffled. The docs warn that this results in a lower reported importance value for both features, even though they might actually be important.
  2. x_b collects 0.078 for a signal it never carried. The model learned to use the copy as a stand-in, so the ranking now contains a feature that the label has never touched.
  3. Shuffling the pair together restores about 0.264, close to the copy-free 0.248. I wrote that grouped shuffle myself, because sklearn's function shuffles columns one at a time.
# sklearn shuffles one column at a time, so group correlated columns yourself
def group_importance(model, X, y, groups, n_repeats=30, seed=0):
    rng = np.random.default_rng(seed)
    base = model.score(X, y)
    drops = []
    for _ in range(n_repeats):
        Xs = X.copy()
        for col in groups:
            Xs[col] = rng.permutation(Xs[col].to_numpy())
        drops.append(base - model.score(Xs, y))
    return float(np.mean(drops))

The docs' suggested remedy is to cluster correlated features and keep one feature per cluster before you read the ranking. Related research warns about more than dilution. An abstract on permute-and-predict methods says they can produce diagnostics that are highly misleading, particularly when there is strong dependence among features, because they force the model to extrapolate into regions with little data. A random-forest study reports that the unconditional importance measure attributed a higher share of importance to correlated predictors. I read abstracts only for both, so the detail is worth checking in the papers.

How Much Should You Trust the Plus-or-Minus?

The docs show a display rule: report the features whose mean importance exceeds twice the standard deviation. I tested that rule on 60 fresh datasets, using 30 shuffles per feature and a 300-row test set.

Feature Mean importance Reported std Real spread across datasets Flagged as important
x₀ 0.2533 0.0259 0.0266 100%
x₁ 0.0861 0.0181 0.0202 98%
Noise (continuous) −0.0005 0.0097 0.0126 7%
Noise (binary) −0.0016 0.0065 0.0071 3%
Noise (4 levels) 0.0000 0.0089 0.0090 3%

The rule worked here. Signal features were flagged almost always, and noise only 3% to 7% of the time, which is about what you would expect from chance at that threshold.

The caveat is what the reported standard deviation actually covers. It captures shuffling randomness for one fitted model on one test set. The real spread, which also includes a new dataset and a retrained model, ran a little larger on every feature. With 60 datasets that spread estimate itself carries roughly 9% error, so only the continuous-noise gap of about 30% looks real. Treat the printed plus-or-minus as a floor, not as the full uncertainty.

Your Metric Changes the Answer

Permutation importance measures lost score, so the scoring choice matters. I trained a logistic regression on imbalanced data with about 6% positives and scored it three ways in a single call:

Metric x₀ (strong) x₁ (weak) x₂ (noise)
Accuracy 0.0037 ± 0.0011 0.0000 ± 0.0009 −0.0009 ± 0.0005
ROC AUC 0.2963 ± 0.0240 0.0394 ± 0.0068 −0.0018 ± 0.0009
F1 0.0527 ± 0.0078 −0.0036 ± 0.0226 −0.0265 ± 0.0134

Test accuracy was 0.936, which is exactly what always predicting the negative class scores. The model produced only 9 positives out of 2,500, so accuracy barely noticed when the strongest feature was shuffled, reporting a drop of 0.0037 for a feature it depends on.

F1 rests on those few positive predictions, so its numbers jump around, and the negative value it gives the pure-noise feature is a fluke of that noise rather than a finding. ROC AUC, which judges the ranking instead of the hard predictions, shows the true picture at 0.2963 for the strongest feature.

The docs warn that metric choice can be critical for imbalanced classification. My rule of thumb: on rare-event problems, use a ranking metric such as ROC AUC or average precision, and read it alongside a second metric rather than alone.

Common Pitfalls

  1. Reading importances from training data. The model memorized the noise, so the ranking reflects memorization. Use held-out data or cross-validation.
  2. Trusting a badly fitted model. The docs add that a poor model can produce misleading importances for features that a good model would use properly.
  3. Ignoring the metric. Accuracy on imbalanced data will hide real signal, because the majority class dominates the score.
  4. Reporting correlated features separately. Shuffling one at a time splits the credit and can hand importance to a column the label never uses. Cluster or group first.
  5. Using the default n_repeats=5 for a decision. Raise it to 30 or more and read importances_std before you rank anything.
  6. Treating negatives near zero as findings. Shuffled noise sometimes scores slightly better by luck. Zero means zero.
  7. Presenting it as feature value. It describes this fitted model on this dataset, not the underlying process.
Checklist before you publish an importance chart:
[ ] computed on held-out data or cross-validation, never on train rows
[ ] scoring metric chosen to match the decision being made
[ ] n_repeats raised, importances_std read, and the spread treated as a floor
[ ] correlated features clustered or shuffled as a group
[ ] negatives near zero reported as zero
[ ] the sentence you write says "this model", not "this feature"

Best Book to Read

Three books cover this ground well, and the first one is the reason most explanations of permutation importance sound the way they do:

CoverBookDescriptionGet it
Cover of “Interpretable Machine Learning” Interpretable Machine Learningby Christoph Molnar the reference behind most explanations of feature importance, including the train-versus-test warning and the correlated-feature caveat. The author also publishes a free online edition, so check its current terms before you rely on it. Start here. View on Amazon
Cover of “Interpretable Machine Learning with Python” Interpretable Machine Learning with Pythonby Serg Masís the practical counterpart, with runnable code for permutation importance, SHAP and partial dependence on tabular problems. Buy this if you want to run the methods rather than read about them. View on Amazon
Hands-On Machine Learning with Scikit-Learn, Keras and PyTorchby Aurélien Géron the best companion for the scikit-learn side, covering trees, evaluation metrics and cross-validation in the order you actually need them. View on Amazon

As an Amazon Associate I earn from qualifying purchases. Links on this page are affiliate links, and I may earn a commission at no extra cost to you.

Check the edition date before you buy. Methodology books age slowly, but code samples do not age at all.

Unlock AI That Actually Works

Get lifetime access to GPT-6 Astra, Claude Fable 5.1, Gemini 3.5, Grok 4.5, and more — all in one platform. Build websites, apps, videos, content, and digital products from a single command. No monthly fees. No tool-hopping.

Click here to get GPTAstra Max now — one-time payment, lifetime access.

Frequently Asked Questions

Why is permutation importance better than impurity importance?

Impurity importance is computed while the tree is being built, on the data the tree was fitted to, so it rewards features the model memorized and it tilts toward high-cardinality columns. The scikit-learn docs describe that tilt directly, noting that impurity importance favors high-cardinality features over low-cardinality ones such as binary columns. Permutation importance measures how much the score degrades when a column is shuffled, so it works on any fitted estimator and on held-out data. In my tests impurity importance gave a pure-noise continuous column 0.149 while permutation importance on the test set put every noise column at about zero.

Should I compute permutation importance on training or test data?

Use held-out data, ideally wrapped in cross-validation. On training data the model has already memorized the noise, so shuffling a noise column still costs score and the importance looks meaningful. In my run the forest reached 1.000 accuracy on training rows and 0.810 on held-out rows, and permutation importance on the training set credited noise columns with 0.089, 0.024 and 0.050. On the test set those same columns came out at roughly zero. The scikit-learn docs warn that features that look important on training data but not on held-out data are a sign of overfitting.

Why do two correlated features both show low importance?

Because they share the credit. When two columns carry the same information, shuffling one leaves the other available, so the model barely loses score and both features look weaker than they are. The scikit-learn docs describe this as a lower reported importance for both features even though they might actually be important. In my test a feature dropped from 0.248 to 0.134 once a near-copy with a correlation of about 0.995 existed, while the copy collected 0.078 for a signal it never actually carried.

How many n_repeats do I need for a stable permutation importance?

Enough that the standard deviation is small relative to the mean, which in practice meant 30 shuffles per feature in my tests. The default is 5, which is fine for a quick look and too few for a ranking decision. Be careful how you read the printed spread, though. The reported standard deviation covers shuffling randomness for one fitted model on one test set. The real spread across new datasets and retrained models was slightly larger in my run, so treat the printed plus-or-minus as a floor rather than the full uncertainty.

Does permutation importance depend on the scoring metric?

Yes, heavily, because it measures lost score and the score is your choice. On imbalanced data with about 6% positives, a logistic regression scored 0.936 accuracy, which is exactly the score of always predicting the negative class, so shuffling the strongest feature barely registered in accuracy. ROC AUC, which judges the ranking rather than the hard predictions, showed a drop of 0.2963 for the same feature. The scikit-learn docs note that choosing the right scoring metric can be critical for imbalanced classification, so pick the metric that matches your decision.

Can permutation importance be negative?

Yes, and that is expected. A negative value means the model scored slightly better after the column was shuffled, which happens when a useless feature is swapped for a luckier arrangement of the same values. In my tests noise columns produced values such as minus 0.001 and minus 0.0005, and the binary noise column reached minus 0.0265 under F1. Treat anything near or below zero as no importance rather than as evidence that the feature hurts the model.

Wrapping Up

Permutation importance asks a clean question: how much worse does the model get without this feature's information? It answers that well on held-out data with a sensible metric, and it misleads on training data, among correlated features, and with the wrong score.

So run it on held-out data, group your correlated features, and check the metric before you trust the bar chart. Then go and tell your stakeholders why feature #3 is not really second — and bring the noise column that proves it.

Which of the three failure modes has bitten you: memorized noise, duplicated columns, or an accuracy score on rare events?

What are You Looking For?

esc