Plotting Missing Values in Pandas – Visualizing NaNs Effectively 2026

Data Manipulation Mar 18, 2026

Plotting Missing Values in Pandas – Visualizing NaNs Effectively 2026

Visualizing missing values is one of the best ways to understand their pattern and impact. In 2026, combining Pandas with Seaborn and Matplotlib allows you to create clear, insightful visualizations that reveal where and how missing data occurs in your dataset.

TL;DR — Best Visualization Methods

  • sns.heatmap(df.isna()) – Missing value heatmap (most popular)
  • Bar plot of missing percentages per column
  • Row-wise missing value distribution

1. Missing Values Heatmap (Most Recommended)

import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt

# Create a small demo file so this example runs as-is
# (in your project, sales_data.csv is your real export)
import numpy as np
rng = np.random.default_rng(42)
n = 60
pd.DataFrame({
    "order_date": pd.date_range("2026-01-01", periods=n, freq="3D"),
    "customer_id": rng.integers(101, 121, n),
    "customer_name": rng.choice([" ana", "ben ", "chen", "dana"], n),
    "product_id": rng.integers(1, 9, n),
    "region": rng.choice(["North", "South", "East", "West"], n),
    "category": rng.choice(["Electronics", "Computers", "Books", "Home"], n),
    "payment_method": rng.choice(["card", "cash", "wallet"], n),
    "segment": rng.choice(["consumer", "business"], n),
    "quantity": rng.integers(1, 6, n),
    "amount": rng.uniform(50, 2500, n).round(2),
    "discount": rng.choice([0.0, 0.05, 0.1, 0.2], n),
}).assign(
    price=lambda d: (d["amount"] / d["quantity"]).round(2),
    profit=lambda d: (d["amount"] * rng.uniform(0.05, 0.3, n)).round(2),
).assign(
    ship_date=lambda d: d["order_date"] + pd.Timedelta(days=2),
    status=lambda d: np.where(d["amount"] > 2000, "priority", "standard"),
).to_csv("sales_data.csv", index=False)

df = pd.read_csv("sales_data.csv", parse_dates=["order_date"])

plt.figure(figsize=(12, 8))
sns.heatmap(
    df.isna(),
    cbar=False,
    cmap="viridis",
    yticklabels=False
)
plt.title("Missing Values Heatmap (White = Missing)")
plt.xlabel("Columns")
plt.ylabel("Rows")
plt.show()

2. Bar Plot – Missing Values Percentage per Column

missing_pct = df.isna().mean() * 100

plt.figure(figsize=(12, 6))
missing_pct.sort_values(ascending=False).plot(
    kind="bar",
    color="tomato",
    edgecolor="black"
)
plt.title("Percentage of Missing Values by Column")
plt.ylabel("Missing Percentage (%)")
plt.xlabel("Column Name")
plt.xticks(rotation=45, ha="right")
plt.tight_layout()
plt.show()

3. Distribution of Missing Values per Row

row_missing = df.isna().sum(axis=1)

plt.figure(figsize=(10, 6))
row_missing.value_counts().sort_index().plot(
    kind="bar",
    color="steelblue"
)
plt.title("Number of Missing Values per Row")
plt.xlabel("Number of Missing Values in a Row")
plt.ylabel("Count of Rows")
plt.show()

4. Best Practices for Visualizing Missing Values in 2026

  • Use a heatmap (sns.heatmap(df.isna())) as your primary visualization — it shows the pattern clearly
  • Create a bar plot of missing percentages sorted from highest to lowest
  • Visualize the distribution of missing values per row to spot problematic records
  • Use a consistent color scheme (e.g., dark colors for present, bright for missing)
  • Always include a clear title and proper labels

Why Visualise Missing Data

Counts tell you how much is missing, but a plot shows where. A bar chart of missing counts ranks the worst columns, and a heatmap of isna() shows whether gaps appear in blocks, for example all columns failing on the same days.

Patterns matter for the fix. Random gaps can often be filled, while blocks of gaps suggest a collection problem that filling would hide.

Worked Example: Bar Chart and Heatmap

Synthetic readings with a block of gaps are plotted two ways.

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

rng = np.random.default_rng(0)
df = pd.DataFrame(rng.normal(size=(30, 4)), columns=["a", "b", "c", "d"])
df.loc[10:14, ["b", "c"]] = np.nan
df.loc[rng.choice(30, 3, replace=False), "d"] = np.nan

fig, axes = plt.subplots(1, 2, figsize=(10, 4))
df.isna().sum().plot.bar(ax=axes[0], title="Missing per column", rot=0)
sns.heatmap(df.isna(), cbar=False, ax=axes[1])
axes[1].set_title("Missing pattern by row")
fig.tight_layout()
print(df.isna().sum().tolist())

The heatmap reveals that columns b and c fail together for rows 10 to 14, while gaps in d are scattered. That difference would be invisible in counts alone.

Practical Tips

For very large tables, sort rows by date or by group before drawing the heatmap, so related gaps line up, or plot a random sample of rows to keep the chart readable.

Common Mistakes

  • Plotting only counts and missing block patterns.
  • Using a colour bar that adds nothing for booleans.
  • Plotting huge tables without sampling or sorting.
  • Filling block gaps without investigating the cause.

Quick Summary

  • Bar charts rank columns by missing count.
  • Heatmaps of isna() show patterns.
  • Block gaps hint at collection problems.
  • Choose the fix after seeing the pattern.

Conclusion

Visualizing missing values is much more insightful than just counting them. In 2026, the combination of a missing value heatmap, percentage bar plot, and row-wise distribution gives you a complete understanding of missing data patterns in your dataset. These visualizations should be one of the first steps in your exploratory data analysis (EDA) process.

Next steps:

  • Create a missing values heatmap and a percentage bar plot for one of your current datasets to understand the missing data pattern

Related Tutorials

Share:
Last updated: March 2026
Built-in Functions in Python 2026 str() in Python 2026: String Conversion + Modern Formatting & Best Practices memoryview with JAX in Python 2026: Zero-Copy NumPy → JAX Array Interop + Efficient ML Examples memoryview with TensorFlow in Python 2026: Zero-Copy NumPy → Tensor Interop + GPU Pinning & ML Examples memoryview with TensorFlow in Python 2026: Zero-Copy NumPy → Tensor Interop + ML Examples memoryview with NumPy & PyTorch in Python 2026: Zero-Copy Views, Efficient Slicing & ML Interop Examples memoryview with NumPy in Python 2026: Zero-Copy Views, Efficient Slicing & Real ML Examples memoryview() in Python 2026: Zero-Copy Magic for Large Binary Data + Real Examples import() in Python 2026: How It Works, Security Risks & Modern Alternatives (importlib) zip() in Python 2026: How to Use It, strict=True Behavior & Real-World Examples vars() in Python 2026: Accessing Object Namespace + Modern Introspection Patterns type() in Python 2026: Dynamic Type Inspection & Object Creation + Modern Patterns tuple() in Python 2026: Immutable Sequences + Modern Patterns & Best Practices super() in Python 2026: Method Resolution & Modern Inheritance Patterns sum() in Python 2026: Summing Iterables + Modern Numeric Patterns & Best Practices staticmethod() in Python 2026: Static Methods, Utility Functions & Modern Best Practices sorted() in Python 2026: Sorting Iterables + Modern Patterns & Best Practices slice() in Python 2026: Creating Slice Objects + Modern Patterns & Best Practices setattr() in Python 2026: Dynamic Attribute Setting + Modern Patterns & Safety set() in Python 2026: Mutable Sets Creation + Modern Patterns & Best Practices