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 iny_trueory_predare used in sorted order. Passlabels=[...]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"]andlabels=["ant","bird","cat"]give the same 3 × 3 array shown above. - Normalising a plot.
ConfusionMatrixDisplay.plot()takes display options only;normalizeandlabelsare applied when the matrix is computed. Set them infrom_predictions/from_estimatoror inconfusion_matrixitself. - Number formatting.
values_format=Noneuses'd'or'.2g', whichever is shorter; setvalues_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)returnstns, 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
- scikit-learn, sklearn.metrics.confusion_matrix (read 2026-11-07).
- scikit-learn, ConfusionMatrixDisplay (read 2026-11-07).
- scikit-learn user guide, Model evaluation: confusion matrix (read 2026-11-07).
- scikit-learn, Installing scikit-learn (read 2026-11-07).
- tidymodels, yardstick conf_mat() (read 2026-11-07).







