Python Tutorial
Confusion Matrix
A table that breaks classification results into correct and incorrect predictions per class, revealing exactly where a model errs.
What It Shows
For binary classification the confusion matrix has four cells:
| Predicted Positive | Predicted Negative | |
|---|---|---|
| Actual Positive | True Positive (TP) | False Negative (FN) |
| Actual Negative | False Positive (FP) | True Negative (TN) |
Accuracy alone hides problems on imbalanced data. The confusion matrix shows whether errors are false alarms (FP) or missed cases (FN) — which matters enormously in medicine, fraud, and safety.
Building the Matrix
import numpy as np
from sklearn.metrics import confusion_matrix, ConfusionMatrixDisplay
actual = np.array([1, 0, 1, 1, 0, 1, 0, 0, 1, 0])
predicted = np.array([1, 0, 1, 0, 0, 1, 1, 0, 1, 0])
cm = confusion_matrix(actual, predicted)
print(cm)
# [[TN FP]
# [FN TP]]
ConfusionMatrixDisplay(cm, display_labels=[0, 1]).plot()scikit-learn orders labels ascending, so row/column 0 is the negative class and 1 is the positive class: the top-left cell is TN and the bottom-right is TP.
Metrics Derived From It
| Metric | Formula | Answers |
|---|---|---|
| Accuracy | (TP+TN) / total | Overall fraction correct |
| Precision | TP / (TP+FP) | Of predicted positives, how many are right? |
| Recall (Sensitivity) | TP / (TP+FN) | Of actual positives, how many did we catch? |
| Specificity | TN / (TN+FP) | Of actual negatives, how many did we clear? |
| F1-score | 2·P·R / (P+R) | Harmonic mean of precision and recall |
from sklearn.metrics import classification_report
print(classification_report(actual, predicted, digits=3))Precision vs Recall Trade-off
Raising the decision threshold usually increases precision but lowers recall, and vice versa. Choose based on the cost of each error type:
- Spam filter: favor precision — a wrongly blocked real email is costly.
- Cancer screening: favor recall — a missed case is far worse than a false alarm.
Multiclass Confusion Matrices
With more than two classes the matrix is N×N; the diagonal holds correct predictions and off-diagonal cells show which classes get confused with which.
cm = confusion_matrix(y_true, y_pred, labels=["cat", "dog", "fox"])
ConfusionMatrixDisplay(cm, display_labels=["cat", "dog", "fox"]).plot(cmap="Blues")Best Practices
- Never judge imbalanced problems on accuracy alone — inspect the matrix.
- Pick precision or recall as your primary metric based on real-world error costs.
- Normalize the matrix (
normalize="true") to compare rates across classes of different sizes. - Report the full
classification_reportso reviewers see every class.
Try It Yourself
Exercise 1: Given TP=40, FP=10, FN=5, TN=45, compute precision and recall.
Show solution
TP, FP, FN = 40, 10, 5
print("precision:", TP / (TP + FP)) # 0.8
print("recall: ", TP / (TP + FN)) # 0.888...Exercise 2: For cancer screening, would you prioritize precision or recall?
Show solution
Recall — missing a real case (false negative) is far more costly than a false alarm.
📘 Real-World Deep Dive
Knowing <strong>ML Confusion Matrix (scikit-learn)</strong> well is what turns scikit-learn from a curiosity into a daily tool — you'll reach for it in nearly every real project.
Real-Life Scenario
An end-to-end usage of ML Confusion Matrix that you'd actually see in a data pipeline or analytics notebook.
Real-Life Example
import numpy as np
from sklearn.metrics import confusion_matrix
y_true = [0, 1, 0, 1, 1, 0]
y_pred = [0, 1, 1, 1, 0, 0]
print(confusion_matrix(y_true, y_pred))Expected Output
(see source)Common mistakes
fitexpects numeric arrays;OneHotEncoder/LabelEncoderare easy to forget for categorical features.- Calling
predicton a model trained on unscaled data and then scaling inputs at inference time silently degrades accuracy. train_test_split(X, y)requires both arrays;train_test_split(X)for unsupervised learning slips past static checkers.- Treating ML Confusion Matrix as a black box without reading the docs — the API has subtle defaults that bite when you scale.
🚀 Performance & Best Practices
- Wrap preprocessing + estimator in a
Pipelinesofit/predictstay reproducible. - Use
joblib/picklefor serialising models, not the entire Python state. - For > 100 k rows, switch to
HistGradientBoostingClassifierorcuML. - When working with scikit-learn, prefer vectorised / batched operations over Python loops.
🧪 Try It Yourself
- Reproduce the snippet on a representative slice of your own data.
- Profile the snippet with
cProfileortimeitand find the single biggest improvement. - Generalise the snippet into a small, reusable function you can drop into future projects.