How to plot a confusion matrix in Python

Learn how to plot a confusion matrix in Python. Discover different methods, tips, real-world applications, and how to debug common errors.

How to plot a confusion matrix in Python
Published on: 
Tue
Mar 3, 2026
Updated on: 
Thu
Mar 5, 2026
The Replit Team Logo Image
The Replit Team

A confusion matrix is a key tool to evaluate classification model performance. Python libraries like Scikit-learn and Seaborn offer simple ways to visualize these matrices for clear, insightful analysis.

In this article, you'll explore several techniques to plot a confusion matrix. You'll find practical tips, real-world applications, and common debugging advice to help you master this essential data science skill.

Using sklearn.metrics to plot a basic confusion matrix

from sklearn.metrics import confusion_matrix, ConfusionMatrixDisplay
import matplotlib.pyplot as plt

y_true = [0, 1, 0, 1, 0, 1, 0, 1]
y_pred = [0, 1, 1, 1, 0, 0, 0, 1]
cm = confusion_matrix(y_true, y_pred)
ConfusionMatrixDisplay(cm).plot()
plt.show()--OUTPUT--# Output will be a basic 2x2 confusion matrix plot showing:
# [[3 1]
#  [1 3]]

The process starts with the confusion_matrix function, which computes the core data for your plot. It compares the ground truth labels, y_true, against your model's predictions in y_pred. The function's output isn't a visual—it's a simple numerical array representing the results.

That array is then passed to ConfusionMatrixDisplay. This class is a convenient wrapper that handles all the plotting logic. Calling .plot() on it uses Matplotlib to render the final, easy-to-read visual without needing manual setup.

Basic visualization techniques

While the default plot is a great starting point, you can unlock deeper insights by using Seaborn, customizing colors and labels, or normalizing the matrix.

Using seaborn for enhanced visualization

import seaborn as sns
import matplotlib.pyplot as plt
from sklearn.metrics import confusion_matrix

y_true = [0, 1, 0, 1, 0, 1, 0, 1]
y_pred = [0, 1, 1, 1, 0, 0, 0, 1]
cm = confusion_matrix(y_true, y_pred)
sns.heatmap(cm, annot=True, fmt='d', cmap='Blues')
plt.xlabel('Predicted')
plt.ylabel('Actual')
plt.show()--OUTPUT--# Output will be a heatmap-style confusion matrix with values:
# [[3 1]
#  [1 3]]

Seaborn’s heatmap function offers more granular control over your confusion matrix's appearance. It transforms the raw numerical array into a color-coded grid, making it easier to interpret model performance visually. Key parameters allow for fine-tuning:

  • annot=True displays the actual count inside each cell.
  • fmt='d' ensures these numbers are formatted as integers.
  • cmap='Blues' sets the color scheme, where darker shades typically represent higher values.

Finally, adding explicit labels with plt.xlabel() and plt.ylabel() clarifies which axis represents predicted versus actual values.

Customizing colors and labels

from sklearn.metrics import confusion_matrix, ConfusionMatrixDisplay
import matplotlib.pyplot as plt

y_true = [0, 1, 0, 1, 0, 1, 0, 1]
y_pred = [0, 1, 1, 1, 0, 0, 0, 1]
cm = confusion_matrix(y_true, y_pred)
labels = ['Negative', 'Positive']
disp = ConfusionMatrixDisplay(cm, display_labels=labels)
disp.plot(cmap='viridis', values_format='d', colorbar=False)
plt.title('Confusion Matrix with Custom Labels')
plt.show()--OUTPUT--# Output will be a confusion matrix with:
# - Custom labels 'Negative' and 'Positive' on axes
# - Green-purple 'viridis' color scheme
# - Title "Confusion Matrix with Custom Labels"

You can make your confusion matrix more intuitive by swapping default numerical labels for descriptive text. It’s a simple change: just pass a list of strings, like ['Negative', 'Positive'], to the display_labels parameter when creating the ConfusionMatrixDisplay object. This makes the axes immediately understandable.

  • The cmap parameter in the plot() method lets you change the color scheme; for example, 'viridis' provides a different visual feel.
  • You can also hide the color scale by setting colorbar=False for a cleaner look.

Normalizing the confusion matrix

import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.metrics import confusion_matrix

y_true = [0, 1, 0, 1, 0, 1, 0, 1]
y_pred = [0, 1, 1, 1, 0, 0, 0, 1]
cm = confusion_matrix(y_true, y_pred)
cm_normalized = cm.astype('float') / cm.sum(axis=1)[:, np.newaxis]
sns.heatmap(cm_normalized, annot=True, fmt='.2f', cmap='YlGnBu')
plt.xlabel('Predicted')
plt.ylabel('Actual')
plt.show()--OUTPUT--# Output will be a normalized heatmap showing proportions:
# [[0.75 0.25]
#  [0.25 0.75]]

Normalizing a confusion matrix shifts the focus from raw counts to proportions. This is particularly helpful when your classes are imbalanced because it shows you the percentage of correct and incorrect predictions for each class, not just the absolute numbers.

  • The core of the operation is dividing each row's values by that row's sum, achieved with cm.astype('float') / cm.sum(axis=1)[:, np.newaxis].
  • When plotting, using fmt='.2f' formats the annotations to two decimal places, making the proportions easy to read.

Advanced visualization techniques

Beyond static images, you can build interactive plots with plotly, handle complex multi-class problems, and embed key performance metrics directly into your visualization.

Creating an interactive confusion matrix with plotly

import plotly.figure_factory as ff
import numpy as np
from sklearn.metrics import confusion_matrix

y_true = [0, 1, 0, 1, 0, 1, 0, 1]
y_pred = [0, 1, 1, 1, 0, 0, 0, 1]
cm = confusion_matrix(y_true, y_pred)
x = ['Predicted 0', 'Predicted 1']
y = ['Actual 0', 'Actual 1']
fig = ff.create_annotated_heatmap(z=cm, x=x, y=y, colorscale='Viridis')
fig.update_layout(title_text='Interactive Confusion Matrix')
fig.show()--OUTPUT--# Output will be an interactive confusion matrix visualization
# that allows hovering, zooming, and panning

Plotly elevates your confusion matrix from a static image to an interactive tool, allowing you to hover over cells for details, zoom, and pan. This is especially useful for presentations or deeper exploration. The process is straightforward with Plotly's figure_factory.

  • The main function, ff.create_annotated_heatmap, builds the entire visual from your confusion matrix data passed as the z parameter.
  • You simply provide lists of strings for the x and y axis labels for clarity.
  • Calling fig.show() renders the final interactive chart in a new window or browser tab.

Handling multi-class confusion matrices

from sklearn.metrics import confusion_matrix, ConfusionMatrixDisplay
import matplotlib.pyplot as plt

y_true = [0, 1, 2, 0, 1, 2, 0, 1, 2]
y_pred = [0, 2, 1, 0, 1, 2, 0, 2, 1]
class_names = ['Class A', 'Class B', 'Class C']
cm = confusion_matrix(y_true, y_pred)
disp = ConfusionMatrixDisplay(cm, display_labels=class_names)
disp.plot(cmap=plt.cm.Blues, xticks_rotation=45)
plt.show()--OUTPUT--# Output will be a 3x3 confusion matrix with:
# [[3 0 0]
#  [0 1 2]
#  [0 2 1]]

Plotting a multi-class confusion matrix uses the same tools you've already seen, but the resulting grid is larger. The diagonal still represents correct predictions, while the off-diagonal cells reveal exactly how your model confuses one class for another—for instance, mislabeling 'Class B' as 'Class C'.

  • Using the display_labels parameter is crucial here to replace numerical indices with meaningful names, making the larger matrix easy to interpret.
  • The xticks_rotation=45 argument in the plot() method tilts the x-axis labels to prevent them from overlapping, a handy trick for improving readability.

Including performance metrics with the confusion matrix

import matplotlib.pyplot as plt
import numpy as np
from sklearn.metrics import confusion_matrix, accuracy_score, precision_score, recall_score

y_true = [0, 1, 0, 1, 0, 1, 0, 1]
y_pred = [0, 1, 1, 1, 0, 0, 0, 1]
cm = confusion_matrix(y_true, y_pred)
accuracy = accuracy_score(y_true, y_pred)
precision = precision_score(y_true, y_pred)
recall = recall_score(y_true, y_pred)

fig, ax = plt.subplots(figsize=(8, 6))
im = ax.imshow(cm, cmap=plt.cm.Reds)
ax.set_title(f'Accuracy={accuracy:.2f}, Precision={precision:.2f}, Recall={recall:.2f}')
ax.set_xlabel('Predicted label')
ax.set_ylabel('True label')
ax.set_xticks([0, 1]); ax.set_yticks([0, 1])
ax.set_xticklabels(['0', '1']); ax.set_yticklabels(['0', '1'])
plt.show()--OUTPUT--# Output will be a confusion matrix with performance metrics in the title:
# Accuracy=0.75, Precision=0.75, Recall=0.75

A confusion matrix is even more powerful when paired with key performance metrics. This approach gives you a single, comprehensive view of your model's performance at a glance.

  • First, you calculate metrics like accuracy, precision, and recall using functions from sklearn.metrics such as accuracy_score and precision_score.
  • The key step is embedding these values directly into the plot’s title. This is done using an f-string inside the ax.set_title() method.

This technique provides immediate context, linking the visual data in the matrix to crucial performance indicators.

Move faster with Replit

Replit is an AI-powered development platform that transforms natural language into working applications. Describe what you want to build, and Replit Agent creates it—complete with databases, APIs, and deployment.

For the visualization techniques we've explored, Replit Agent can turn them into production-ready tools. You could build:

  • A model performance dashboard that automatically generates confusion matrices for different classification experiments.
  • An interactive web app where users can paste their y_true and y_pred data to generate a downloadable plot.
  • A monitoring service that tracks a live ML model, visualizing its confusion matrix to detect performance drift over time.

Describe your app idea, and Replit Agent writes the code, tests it, and fixes issues automatically, all in your browser. Try Replit Agent and bring your concepts to life.

Common errors and challenges

Even with powerful tools, a few common pitfalls can trip you up when plotting a confusion matrix in Python.

  • A frequent error is passing raw prediction arrays directly to ConfusionMatrixDisplay. This class expects the pre-computed matrix array returned by the confusion_matrix function, not the original y_true and y_pred data. Feeding it the wrong input will cause an error because its job is to visualize the matrix, not to calculate it.
  • Accidentally swapping the y_true and y_pred arguments in the confusion_matrix function is an easy mistake to make. It won't raise an error, but it will flip your plot’s axes. This leads to misinterpreting true positives as false negatives and vice versa, so always double-check the order to ensure your analysis is sound.
  • The confusion_matrix function requires discrete class labels, like 0 or 1, not the raw probability scores from your model. Passing probabilities will result in an error. You must first convert these scores into final class predictions, typically by applying a threshold like 0.5, before generating the matrix.

Incorrectly initializing ConfusionMatrixDisplay

A common tripwire is initializing ConfusionMatrixDisplay with the wrong data. Unlike confusion_matrix, it doesn't accept the y_true and y_pred arrays. Its sole job is to plot the matrix you've already calculated. See what happens when you mix them up.

from sklearn.metrics import confusion_matrix, ConfusionMatrixDisplay
import matplotlib.pyplot as plt

y_true = [0, 1, 0, 1, 0, 1, 0, 1]
y_pred = [0, 1, 1, 1, 0, 0, 0, 1]

cm = confusion_matrix(y_true, y_pred)
disp = ConfusionMatrixDisplay(cm)  # Missing parameter name
disp.plot()
plt.show()

The issue is subtle because the code runs without crashing. It relies on passing cm positionally, but omitting the explicit confusion_matrix= parameter name can cause silent errors if the wrong data is supplied. See the safer approach below.

from sklearn.metrics import confusion_matrix, ConfusionMatrixDisplay
import matplotlib.pyplot as plt

y_true = [0, 1, 0, 1, 0, 1, 0, 1]
y_pred = [0, 1, 1, 1, 0, 0, 0, 1]

cm = confusion_matrix(y_true, y_pred)
disp = ConfusionMatrixDisplay(confusion_matrix=cm, display_labels=[0, 1])
disp.plot()
plt.show()

The corrected code explicitly names the parameter: ConfusionMatrixDisplay(confusion_matrix=cm). While positional arguments can work, naming them makes your code more robust and readable, preventing silent bugs if you accidentally pass the wrong data. This practice is especially important when a class constructor like ConfusionMatrixDisplay has multiple optional parameters. It ensures the correct values are assigned, making your code less error-prone and easier to debug down the line.

Swapping y_true and y_pred arguments

It's a subtle but critical mistake to swap the y_true and y_pred arguments in the confusion_matrix function. Your code will run without errors, but the resulting plot will be inverted, leading to a completely wrong interpretation of your model's performance.

This silent failure can be tricky to spot. Take a look at the following code, where the arguments are accidentally reversed, to see how easily it can happen.

from sklearn.metrics import confusion_matrix, ConfusionMatrixDisplay
import matplotlib.pyplot as plt

y_true = [0, 1, 0, 1, 0, 1, 0, 1]
y_pred = [0, 1, 1, 1, 0, 0, 0, 1]

cm = confusion_matrix(y_pred, y_true)  # Wrong order!
disp = ConfusionMatrixDisplay(confusion_matrix=cm, display_labels=[0, 1])
disp.plot()
plt.show()

When confusion_matrix receives y_pred first, it treats your model's predictions as the actual truth. This inverts the resulting matrix, leading to incorrect analysis. See how to fix the argument order in the code below.

from sklearn.metrics import confusion_matrix, ConfusionMatrixDisplay
import matplotlib.pyplot as plt

y_true = [0, 1, 0, 1, 0, 1, 0, 1]
y_pred = [0, 1, 1, 1, 0, 0, 0, 1]

cm = confusion_matrix(y_true, y_pred)  # Correct order
disp = ConfusionMatrixDisplay(confusion_matrix=cm, display_labels=[0, 1])
disp.plot()
plt.show()

The corrected code ensures the arguments are in the right order: confusion_matrix(y_true, y_pred). The function always expects the ground truth labels first, followed by the model's predictions. Reversing them won't crash your code, but it will flip the matrix, leading to incorrect conclusions about your model's performance. Always double-check this order, especially when your results look strange. It's a simple fix for a potentially misleading error.

Using probabilities instead of class predictions

Your model might output probabilities, but the confusion_matrix function only understands final decisions, like 0 or 1. Passing it raw probability scores instead of class labels will break your code. The example below shows this common error in action.

from sklearn.metrics import confusion_matrix, ConfusionMatrixDisplay
import matplotlib.pyplot as plt

y_true = [0, 1, 0, 1, 0, 1, 0, 1]
y_pred_proba = [0.2, 0.7, 0.6, 0.9, 0.3, 0.4, 0.3, 0.8]

cm = confusion_matrix(y_true, y_pred_proba)  # Error: using probabilities directly
ConfusionMatrixDisplay(confusion_matrix=cm, display_labels=[0, 1]).plot()
plt.show()

The confusion_matrix function triggers an error because it received a list of float probabilities, y_pred_proba, instead of discrete class labels. It isn't built to handle continuous data. See how to fix this below.

from sklearn.metrics import confusion_matrix, ConfusionMatrixDisplay
import matplotlib.pyplot as plt

y_true = [0, 1, 0, 1, 0, 1, 0, 1]
y_pred_proba = [0.2, 0.7, 0.6, 0.9, 0.3, 0.4, 0.3, 0.8]

y_pred = [1 if prob >= 0.5 else 0 for prob in y_pred_proba]
cm = confusion_matrix(y_true, y_pred)
ConfusionMatrixDisplay(confusion_matrix=cm, display_labels=[0, 1]).plot()
plt.show()

The corrected code first converts raw probability scores into definite class predictions. It uses a list comprehension to iterate through the probabilities, assigning a class of 1 if a score is >= 0.5 and 0 otherwise. This step creates the discrete y_pred array that the confusion_matrix function requires. You'll need to do this whenever your model outputs probabilities instead of final class labels, which is common with many classification algorithms.

Real-world applications

Beyond debugging, these plots are essential for evaluating real-world models, from analyzing customer sentiment to assessing critical medical diagnostic tools.

Evaluating a sentiment analysis model with confusion_matrix

For sentiment analysis, a confusion matrix visualizes how well your model distinguishes between categories like 'Positive,' 'Negative,' and 'Neutral,' revealing common misclassifications.

from sklearn.metrics import confusion_matrix, ConfusionMatrixDisplay
import matplotlib.pyplot as plt

# Example sentiment analysis predictions (0: Negative, 1: Neutral, 2: Positive)
true_sentiment = [0, 0, 1, 1, 2, 2, 0, 1, 2, 0, 1, 2]
pred_sentiment = [0, 1, 1, 1, 2, 0, 0, 1, 2, 0, 2, 2]

labels = ['Negative', 'Neutral', 'Positive']
cm = confusion_matrix(true_sentiment, pred_sentiment)
disp = ConfusionMatrixDisplay(cm, display_labels=labels)
disp.plot(cmap='Blues')
plt.title('Sentiment Analysis Model Evaluation')
plt.show()

This code snippet shows how to visualize a multi-class sentiment model's results. It starts by defining the ground truth labels in true_sentiment and the model's output in pred_sentiment.

  • The confusion_matrix function takes these lists to compute the raw performance data.
  • Next, ConfusionMatrixDisplay is initialized with this data and a list of custom labels to make the axes readable.

Finally, the .plot() method renders the matrix using a 'Blues' color scheme, giving you a clear visual summary of its performance.

Applying cost analysis to medical diagnosis confusion matrices

In critical applications like medical diagnosis, you can use a confusion matrix to weigh the real-world costs of misclassifications, since a false negative is often far more severe than a false positive.

from sklearn.metrics import confusion_matrix, ConfusionMatrixDisplay
import matplotlib.pyplot as plt

# Medical diagnosis predictions (0: No disease, 1: Disease present)
true_diagnosis = [0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1]
pred_diagnosis = [0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1]

cm = confusion_matrix(true_diagnosis, pred_diagnosis)
tn, fp, fn, tp = cm.ravel()

# Calculate costs
cost_fp = 100  # Cost of false positive (unnecessary treatment)
cost_fn = 500  # Cost of false negative (missed diagnosis)
total_cost = fp * cost_fp + fn * cost_fn

disp = ConfusionMatrixDisplay(cm, display_labels=['Healthy', 'Disease'])
disp.plot(cmap='Reds')
plt.title(f'Medical Diagnosis Costs: ${total_cost}\nFP: ${fp*cost_fp}, FN: ${fn*cost_fn}')
plt.show()

This code demonstrates how to extract specific values from a confusion matrix for custom calculations. It goes beyond just plotting the matrix by using a specific function to access its individual components.

  • The cm.ravel() method flattens the 2x2 matrix into a one-dimensional array, which lets you easily unpack the true negatives, false positives, false negatives, and true positives into separate variables.
  • These raw counts are then used to calculate a total_cost, applying different weights to each type of error.
  • Finally, an f-string dynamically inserts this calculated cost directly into the plot’s title.

Get started with Replit

Turn these techniques into a real tool. Tell Replit agent to "build a web app that accepts CSV data and plots a normalized confusion matrix" or "create a dashboard comparing two models with side-by-side confusion matrices."

The agent writes the code, tests for errors, and deploys your app from a single prompt. Start building with Replit.

Get started free

Create and deploy websites, automations, internal tools, data pipelines and more in any programming language without setup, downloads or extra tools. All in a single cloud workspace with AI built in.

Get started for free

Create & deploy websites, automations, internal tools, data pipelines and more in any programming language without setup, downloads or extra tools. All in a single cloud workspace with AI built in.