How to plot a confusion matrix in Python

Learn how to plot a confusion matrix in Python. Explore different methods, tips, real-world applications, and common error debugging.

How to plot a confusion matrix in Python
Published on: 
Tue
Mar 3, 2026
Updated on: 
Wed
Apr 1, 2026
The Replit Team

A confusion matrix is a crucial tool to evaluate machine learning model performance. Python provides powerful libraries that simplify the visualization and interpretation of these matrices for any classification task.

In this article, you'll learn several techniques to plot a confusion matrix. We'll cover practical tips, real-world applications, and common debugging advice to help you master this essential 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 sklearn.metrics module simplifies this process into two main steps. This approach separates the calculation of the matrix from its visualization, giving you more flexibility.

  • First, the confusion_matrix() function computes the matrix from your true and predicted labels. This gives you the raw data as an array.
  • Next, you pass that array to ConfusionMatrixDisplay(). Its .plot() method then uses matplotlib to create a clean, labeled visualization automatically.

Basic visualization techniques

Beyond the default plot, you can gain deeper insights by using seaborn for enhanced visuals, customizing colors and labels, and normalizing the matrix values.

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]]

For a more polished visual, you can use seaborn. Its heatmap() function transforms the matrix array into an intuitive, color-coded grid that makes identifying patterns much quicker. This gives you more control over the plot's final look.

  • The annot=True parameter is key; it displays the numerical values in each cell.
  • fmt='d' ensures these annotations are formatted as integers.
  • You can customize the look with cmap='Blues', which sets a color gradient.
  • Using plt.xlabel() and plt.ylabel() adds clear axis labels for better readability.

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"

Customizing your plot makes it much easier to interpret at a glance. You can replace the default numeric labels with more descriptive names, like 'Negative' and 'Positive', by passing a list to the display_labels parameter in ConfusionMatrixDisplay. Further tweaks are made directly in the .plot() method.

  • The cmap argument lets you change the color scheme; for example, 'viridis' gives a green and purple gradient.
  • You can remove the color scale legend by setting colorbar=False.
  • Adding a plt.title() gives your visualization clear context.

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 converts raw counts into proportions, which is especially useful for evaluating performance on imbalanced datasets. You can do this by dividing each value in a row by the sum of that row. The code accomplishes this with the expression cm.astype('float') / cm.sum(axis=1)[:, np.newaxis], which calculates the distribution of predictions for each true class.

  • This process turns each row into a set of percentages, showing where the predictions for that class landed.
  • When plotting the normalized matrix, using fmt='.2f' in your heatmap function formats the cell annotations as two-decimal floats.

Advanced visualization techniques

To take your analysis further, you can build interactive plots with plotly, handle complex multi-class scenarios, 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

For interactive plots, plotly is a powerful alternative to static visualizations. The library's figure_factory module simplifies the process with its create_annotated_heatmap() function, which generates a plot you can explore directly.

  • You pass your confusion matrix array to the z parameter.
  • The x and y parameters take lists of strings to set your axis labels.
  • The final output lets you hover over cells for details, zoom, and pan, making it easier to analyze complex results.

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]]

Visualizing a multi-class confusion matrix follows the same logic as a binary one. The key is making the plot easy to read when you have more than two categories. By passing a list of your class names to the display_labels parameter, you replace the default numeric indices with clear, descriptive labels.

  • This makes it easy to see exactly which classes the model is confusing—for example, predicting 'Class C' when the actual was 'Class B'.
  • You can also use the xticks_rotation argument in the .plot() method to tilt the x-axis labels, preventing them from overlapping.

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

You can create a more informative plot by embedding performance metrics directly into the visualization. This gives you an at-a-glance summary of your model's performance alongside the confusion matrix itself.

  • First, calculate metrics like accuracy, precision, and recall using functions from sklearn.metrics such as accuracy_score() and precision_score().
  • Then, use an f-string to format these values directly into the plot’s title with ax.set_title(). This combines the raw counts from the matrix with essential performance scores in one clear view.

Move faster with Replit

Mastering individual techniques is one thing, but building a complete application is another. Replit is an AI-powered development platform that helps you bridge that gap. It comes with all Python dependencies pre-installed, so you can skip setup and start coding instantly. From there, Agent 4 can take your idea to a working product—handling the code, databases, APIs, and deployment directly from your description.

Instead of piecing together visualization code, you can describe the entire tool you need. For example, you could build:

  • A model performance dashboard that automatically generates and updates a confusion matrix using seaborn after each training run.
  • An interactive evaluation tool that uses plotly to compare confusion matrices from multiple models side-by-side.
  • A classification report generator that calculates precision and recall from a normalized confusion matrix to flag underperforming classes.

Simply describe your app, and Replit will write the code, test it, and fix issues automatically, all within your browser.

Common errors and challenges

Even with powerful tools, a few common mistakes can trip you up when plotting a confusion matrix, leading to incorrect interpretations.

  • Incorrectly initializing ConfusionMatrixDisplay
  • A frequent mistake is passing your labels directly to ConfusionMatrixDisplay. This class expects the already-computed confusion matrix array, not the raw y_true and y_pred lists. Always remember the two-step process: first, generate the matrix with confusion_matrix(y_true, y_pred), and then pass the resulting array to ConfusionMatrixDisplay.
  • Swapping y_true and y_pred arguments
  • It's easy to accidentally swap the y_true and y_pred arguments when calling confusion_matrix(). This simple mix-up flips the matrix's axes, causing you to misread true positives as false negatives and vice versa. Double-check that you're passing the actual labels first and the predicted labels second, as the correct order is crucial for an accurate evaluation.
  • Using probabilities instead of class predictions
  • The confusion_matrix() function requires discrete class labels (e.g., 0, 1, 2), not probability scores. Feeding it raw probabilities from your model, like 0.8 or 0.2, will raise an error. Before plotting, you must convert these probabilities into final class predictions, typically by applying a threshold such as classifying any probability above 0.5 as class 1.

Incorrectly initializing ConfusionMatrixDisplay

It’s easy to mix up the inputs for confusion_matrix() and ConfusionMatrixDisplay. The display function's job is only to plot, so it needs the matrix array itself. Passing it the raw y_true and y_pred lists will cause an error, as shown 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(cm) # Missing parameter name
disp.plot()
plt.show()

The error occurs because ConfusionMatrixDisplay is initialized incorrectly, even though the cm array is correct. The function is strict about how it receives the matrix data. See the corrected implementation in the following snippet.

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 fix is to explicitly pass the matrix using the keyword argument confusion_matrix=cm when initializing ConfusionMatrixDisplay. It’s a common slip-up, but the function is strict and requires you to name this parameter. By correctly labeling the input, you ensure the display class knows exactly what data it's supposed to plot. You should also provide the display_labels at this stage for a more readable chart.

Swapping y_true and y_pred arguments

It's easy to accidentally swap the y_true and y_pred arguments when calling confusion_matrix(). This simple mix-up flips the matrix's axes, causing you to misread true positives as false negatives and vice versa. The code below shows this 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 = [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()

The function call confusion_matrix(y_pred, y_true) reverses the expected input order. This generates a matrix that incorrectly maps predictions to actuals, making your model's evaluation unreliable. The corrected implementation below ensures the arguments are passed correctly.

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 works by passing the arguments in the required order: confusion_matrix(y_true, y_pred). The function expects the ground truth first and predictions second to build the matrix correctly. Since this mix-up won't trigger an error, it's a good habit to verify the order every time you evaluate a model. This simple check prevents you from misreading your model's performance, ensuring your analysis is based on an accurate matrix.

Using probabilities instead of class predictions

The confusion_matrix() function requires discrete class labels, not the raw probability scores your model outputs. Feeding it probabilities directly will raise an error because it doesn't know how to categorize them. The code below shows what happens when you make this mistake.

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 function fails because y_pred_proba contains continuous values, not the discrete class labels it expects. This mismatch triggers an error. The following code demonstrates the correct approach to prepare your data before plotting.

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 solution is to convert the probability scores into definite class labels before passing them to confusion_matrix(). The corrected code uses a list comprehension to apply a 0.5 threshold, turning any probability at or above this value into class 1 and the rest into class 0. This step is essential whenever your model outputs probabilities instead of final predictions, as the function requires discrete categories to build the matrix correctly.

Real-world applications

Beyond just code and debugging, confusion matrices are critical for making informed decisions in real-world applications.

Evaluating a sentiment analysis model with confusion_matrix

In sentiment analysis, a confusion matrix helps you see exactly where your model gets confused, for instance, by misclassifying a 'Negative' review as 'Neutral'.

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 example applies the confusion matrix to a practical sentiment analysis task. The code visualizes how well a model distinguishes between different sentiment categories.

  • It first calculates the matrix using confusion_matrix() with lists of true and predicted sentiment labels.
  • Next, it prepares the plot with ConfusionMatrixDisplay, using the display_labels parameter to map numeric classes like 0 and 1 to meaningful terms such as 'Negative' and 'Neutral'.

This step is vital for making the final visualization clear, showing exactly which sentiments the model confuses.

Applying cost analysis to medical diagnosis confusion matrices

In critical applications like medical diagnosis, not all prediction errors are equal, and you can use a confusion matrix to quantify the real-world costs of false positives versus false negatives.

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 attach real-world costs to model errors. It uses cm.ravel() to flatten the matrix, making it easy to unpack the values for true negatives, false positives, false negatives, and true positives directly into variables.

  • It assigns different costs to false positives (cost_fp) and false negatives (cost_fn), reflecting that a missed diagnosis is more severe than an unnecessary treatment.
  • The total cost is calculated and embedded in the plot’s title, giving you a clear, impact-based summary of the model's performance beyond simple accuracy.

Get started with Replit

Turn these techniques into a working application. Just tell Replit Agent what you need: “build a dashboard to compare model performance” or “create a tool that calculates diagnosis costs from a confusion matrix.”

It will write the code, test for errors, and deploy your application directly from your instructions. Start building with Replit.

Build your first app today

Describe what you want to build, and Replit Agent writes the code, handles the infrastructure, and ships it live. Go from idea to real product, all in your browser.

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.