Skip to main content
v2026.11,858 entries · CC-BY 4.0
LAC HealthMedical & Research SuppliesReagents, PPE & instruments — chain-of-custody documented.Fast, traceable sourcing built for regulated research environments, from bench consumables to instrumentation.Shop lac.us CodeCASRAIlac.us

Sklearn Confusion Matrix: confusion_matrix() and ConfusionMatrixDisplay

How to compute and plot a confusion matrix with scikit-learn, read the rows and columns correctly, and normalize it.

Written and maintained by CASRAI Editorial Board

Last updated

Short answer: sklearn.metrics.confusion_matrix(y_true, y_pred) returns a square NumPy array in which row i, column j counts observations known to be in class i and predicted to be in class j. Rows are the true class and columns are the predicted class. Researchers and analysts use it to see which classes a classifier confuses, and to recover the counts behind precision, recall and F1.

What it is used for in research

A single accuracy figure hides which errors a classifier makes. The confusion matrix keeps them apart: for a binary problem it holds the true negatives, false positives, false negatives and true positives from which precision and recall, the F1 score and the points of an ROC curve are all computed. Searches for “confusion matrix sklearn” and “sklearn confusion matrix” land on the same two tools in scikit-learn: the function confusion_matrix (computes the array) and the class ConfusionMatrixDisplay (draws it).

Install or access

The scikit-learn installation page gives this command, ideally run inside a virtual environment:

pip install -U scikit-learn

A conda route is also documented: conda create -n sklearn-env -c conda-forge scikit-learn. Then import the pieces used below:

from sklearn.metrics import confusion_matrix, ConfusionMatrixDisplay

Step-by-step: compute and plot a confusion matrix

1. Get true labels and predictions

Fit a classifier on a training split and predict on held-out data (see train/test split in Python and logistic regression in scikit-learn). Evaluating on the training data inflates every cell on the diagonal.

2. Compute the matrix

The signature in the current documentation is confusion_matrix(y_true, y_pred, *, labels=None, sample_weight=None, normalize=None). The scikit-learn documentation’s 8-sample binary example:

y_true = [0, 0, 0, 1, 1, 1, 1, 1]
y_pred = [0, 1, 0, 1, 0, 1, 0, 1]
cm = confusion_matrix(y_true, y_pred)
# array([[2, 1],
#        [2, 3]])

3. Unpack a binary matrix into tn, fp, fn, tp

tn, fp, fn, tp = confusion_matrix(y_true, y_pred).ravel().tolist()
# (2, 1, 2, 3)

The documented layout is TN = C[0,0], FP = C[0,1], FN = C[1,0], TP = C[1,1], so the order tn, fp, fn, tp follows row-by-row flattening.

4. Plot it with ConfusionMatrixDisplay

Since scikit-learn 1.0 the recommended route is a class method rather than the constructor:

ConfusionMatrixDisplay.from_predictions(y_true, y_pred)
# or, from a fitted classifier (or a Pipeline ending in one):
ConfusionMatrixDisplay.from_estimator(clf, X_test, y_test)

Both accept labels, sample_weight, normalize, display_labels, include_values, xticks_rotation, values_format, cmap (default 'viridis'), ax and colorbar. If you already hold an array, build the display manually:

cm = confusion_matrix(y_test, predictions, labels=clf.classes_)
disp = ConfusionMatrixDisplay(confusion_matrix=cm, display_labels=clf.classes_)
disp.plot()

Output and how to read it

Rows are true classes, columns are predicted classes

For the 8-sample example the matrix is [[2, 1], [2, 3]]: of 3 class-0 observations, 2 were predicted 0 and 1 was predicted 1; of 5 class-1 observations, 2 were predicted 0 and 3 were predicted 1. The scikit-learn user guide notes that other references, Wikipedia among them, may use the opposite axis convention, so check the orientation before comparing numbers across tools.

Multiclass example

For y_true = [2, 0, 2, 2, 0, 1] and y_pred = [0, 0, 2, 2, 0, 2] the documented result is:

array([[2, 0, 0],
       [0, 0, 1],
       [1, 0, 2]])

Row 1 (the single true class-1 observation) has its only entry in column 2, so class 1 was never predicted correctly; row 2 shows one class-2 observation predicted as class 0. Cells on the diagonal are correct predictions.

The normalize argument

The normalize argument divides the counts so that the matrix shows proportions. It takes 'true', 'pred', 'all' or None (raw counts). Applied to the same 8-sample example (counts [[2, 1], [2, 3]]):

normalize= Divides by Result Reads as
'all' whole population (8) [[0.25, 0.125], [0.25, 0.375]] share of all observations (this array is the documented output)
'true' row sums (3 and 5) [[0.667, 0.333], [0.4, 0.6]] each row sums to 1; the diagonal is per-class recall
'pred' column sums (4 and 4) [[0.5, 0.25], [0.5, 0.75]] each column sums to 1; the diagonal is per-class precision

The 'true' and 'pred' rows are computed by hand from the documented rule ('true' over rows, 'pred' over columns) rather than quoted from the docs; the 'all' row is the documented output.

From the matrix to precision, recall and F1

With tn = 2, fp = 1, fn = 2, tp = 3: precision = tp / (tp + fp) = 3/4 = 0.75; recall = tp / (tp + fn) = 3/5 = 0.60; F1 = 2 × 0.75 × 0.60 / (0.75 + 0.60) ≈ 0.667; accuracy = (tn + tp) / 8 = 0.625. These are arithmetic on the example, not scikit-learn output.

Troubleshooting

  • Class order surprises. With labels=None, the labels present in y_true or y_pred are used in sorted order. Pass labels=[...] to fix the order or select a subset. With strings: y_true=["cat","ant","cat","cat","ant","bird"], y_pred=["ant","ant","cat","cat","ant","cat"] and labels=["ant","bird","cat"] give the same 3 × 3 array shown above.
  • Normalising a plot. ConfusionMatrixDisplay.plot() takes display options only; normalize and labels are applied when the matrix is computed. Set them in from_predictions / from_estimator or in confusion_matrix itself.
  • Number formatting. values_format=None uses 'd' or '.2g', whichever is shorter; set values_format='.2f' (or similar) if you want fixed decimals on a normalised plot.
  • from_estimator errors. It needs an already fitted classifier, or a Pipeline whose last step is a classifier. For other models, compute predictions yourself and use from_predictions.
  • Swapped arguments. The order is (y_true, y_pred). Reversing them transposes the matrix, so false positives and false negatives trade places.
  • Sweeping thresholds. confusion_matrix_at_thresholds(y_true, y_score) returns tns, fps, fns, tps, thresholds, with thresholds being the distinct score values in decreasing order; this is the raw material for an ROC curve.

Free alternatives

Tool Function Orientation
scikit-learn (Python) sklearn.metrics.confusion_matrix, ConfusionMatrixDisplay Rows = true class, columns = predicted class
yardstick (R, tidymodels) conf_mat(data, truth, estimate, dnn = c("Prediction", "Truth"), case_weights = NULL, ...) Rows = prediction, columns = truth (the reverse of scikit-learn)

Because yardstick puts predictions in rows, a table copied between the two ecosystems must be transposed before cell-by-cell comparison. For plotting a matrix as a colour grid by hand, see heatmaps in seaborn.

FAQ

How do I make a confusion matrix in sklearn?

Call confusion_matrix(y_true, y_pred) from sklearn.metrics with the true labels first, then predictions. Wrap the result in ConfusionMatrixDisplay, or call ConfusionMatrixDisplay.from_predictions(y_true, y_pred), to plot it.

Are rows true or predicted in the sklearn confusion matrix?

Rows are the true class and columns are the predicted class; C[i, j] counts observations known to be in group i and predicted in group j.

How do I get TP, FP, FN and TN for a binary problem?

tn, fp, fn, tp = confusion_matrix(y_true, y_pred).ravel(), relying on the documented layout TN = C[0,0], FP = C[0,1], FN = C[1,0], TP = C[1,1].

How do I show percentages instead of counts?

Pass normalize='true' (row-wise), 'pred' (column-wise) or 'all' (whole population) to confusion_matrix or to the display class methods.

Sources

Follow CASRAI

Research-administration guidance, standards updates and independent tool reviews.

Ask CASRAI · free to try

Ask about Sklearn Confusion Matrix: confusion_matrix() and ConfusionMatrixDisplay

Ask your first 2 questions free below. Subscribers get 150 a day for $29 a month.

An AI assistant specialized in research administration. It cites the sources behind every answer, labels web answers and says when it can't answer.

Answers draw on CASRAI's guides and dictionary plus the federal and funder documents we index: Federal Register, Grants.gov, Regulations.gov and UKRI.

Works on this site and inside Claude, Cursor and the AI tools you already use.

Everything CASRAI publishes — this page, the dictionary, the guides and the news — stays free to read, with no account and no card.

Referenced across the research world

University of Cambridge logoColumbia University logoCrossref logoUniversity of Edinburgh logoHarvard University logoUniversity of Oxford logoPrinceton University logoStanford School of Medicine logoUniversity College London logoORCID logoUniversity of Cambridge logoColumbia University logoCrossref logoUniversity of Edinburgh logoHarvard University logoUniversity of Oxford logoPrinceton University logoStanford School of Medicine logoUniversity College London logoORCID logo
  • University of Cambridge logo
  • Columbia University logo
  • Crossref logo
  • University of Edinburgh logo
  • Harvard University logo
  • University of Oxford logo
  • Princeton University logo
  • Stanford School of Medicine logo
  • University College London logo
  • ORCID logo

View CASRAI adoption →