How to Compute a Confusion Matrix in Python

Compute a multi-class confusion matrix from true and predicted labels using pure Python dictionaries and nested lists, then format it for readable output.

Easy Python 3.9+ Aug 9, 2026 ML engineering pipelines 14 views 0 copies

Python code

29 lines
Python 3.9+
from collections import defaultdict

def compute_confusion_matrix(y_true, y_pred, labels):
    """Compute confusion matrix using Python dicts and nested lists."""
    label_index = {label: i for i, label in enumerate(labels)}
    matrix = [[0] * len(labels) for _ in range(len(labels))]
    
    for true, pred in zip(y_true, y_pred):
        i = label_index[true]
        j = label_index[pred]
        matrix[i][j] += 1
    return matrix

def format_confusion_matrix(matrix, labels):
    """Return a formatted string representation of the confusion matrix."""
    header = "      " + "  ".join(f"{label:>5}" for label in labels)
    lines = [header]
    for i, label in enumerate(labels):
        row = f"{label:>5} " + "  ".join(f"{val:>5}" for val in matrix[i])
        lines.append(row)
    return "\n".join(lines)

if __name__ == "__main__":
    y_true = ["cat", "dog", "cat", "cat", "dog", "bird", "bird", "dog"]
    y_pred = ["cat", "dog", "dog", "cat", "bird", "cat", "bird", "dog"]
    labels = ["cat", "dog", "bird"]
    
    cm = compute_confusion_matrix(y_true, y_pred, labels)
    print(format_confusion_matrix(cm, labels))

Output

stdout
cat   dog  bird
  cat    2     1     0
  dog    1     2     1
  bird   1     0     1

How it works

This implementation builds a mapping from each label to its row/column index in the matrix. For every pair of true and predicted labels, it increments the corresponding cell, counting how many times a true class was predicted as another class. Using nested lists for the matrix keeps the code dependency-free and straightforward for small datasets. The formatting function aligns columns with fixed-width padding, making the result easy to read and share.

Common mistakes

  • Assuming labels are sorted or unique in the given order; always pass an explicit labels list.
  • Using y_true and y_pred lists of different lengths, causing zip to silently truncate.
  • Forgetting that label_index lookup raises KeyError for unseen labels; consider using .get() with a default.
  • Using a single flat list instead of nested lists for the matrix, causing index errors.

Variations

  1. Use numpy arrays and sklearn.metrics.confusion_matrix for a battle-tested version with built-in normalization.
  2. Return a dictionary of dictionaries (e.g., {true: {pred: count}}) for easier access in code.

Real-world use cases

  • Evaluating a classification model's per-class performance during model validation or feature selection.
  • Debugging imbalanced datasets by spotting classes that the model frequently confuses with others.
  • Building a custom monitoring script that flags when confusion patterns change across model retraining.

Sponsored

Run this sample

Open the browser IDE to tweak the example and see results without installing anything.

Open editor

More from ML engineering pipelines

Related tutorials and quizzes for this topic.