density plot
data visualization
statistical graphs
plotting techniques
data analysis

How to create a density plot

ML System Design practice on Codemia

Design recommenders, ranking systems and training pipelines the way ML interviews actually ask for them, with worked solutions.

Practice ML system design

Introduction

A density plot shows the shape of a continuous distribution without the rigid bins of a histogram. It is especially useful when you want to compare groups, inspect skew, or get a smoother view of how values are concentrated.

What a Density Plot Represents

Most density plots are built with kernel density estimation, often shortened to KDE. Instead of counting points into fixed bins, KDE places a small smooth curve around each observation and adds those curves together. The result is a continuous line that estimates where data values are dense and where they are sparse.

Two ideas matter more than anything else:

  • the x-axis shows the range of values in your data
  • the y-axis shows estimated density, not raw counts

That second point is easy to miss. The y-axis is not “number of rows.” The total area under the curve is one, which means the chart is normalized. If you need counts, a histogram is often a better first plot.

Creating a Basic Density Plot in Python

A practical way to build one is with seaborn, which sits on top of Matplotlib. The example below generates two sets of values and draws a density plot for one of them.

python
1import numpy as np
2import seaborn as sns
3import matplotlib.pyplot as plt
4
5rng = np.random.default_rng(42)
6values = rng.normal(loc=70, scale=10, size=500)
7
8sns.kdeplot(values, fill=True)
9plt.xlabel("Score")
10plt.ylabel("Density")
11plt.title("Density plot of scores")
12plt.show()

This example is fully runnable. The curve peaks where scores are most common and tapers off toward the tails.

Understanding Bandwidth

The smoothness of the curve is controlled by bandwidth. A smaller bandwidth follows local variation more closely, which can make the line noisy. A larger bandwidth produces a smoother line, but it can hide meaningful features such as multiple peaks.

Seaborn exposes this through bw_adjust. Values below 1 make the estimate less smooth, and values above 1 make it smoother.

python
1import numpy as np
2import seaborn as sns
3import matplotlib.pyplot as plt
4
5rng = np.random.default_rng(7)
6values = np.concatenate([
7    rng.normal(60, 6, 250),
8    rng.normal(80, 4, 250)
9])
10
11fig, axes = plt.subplots(1, 2, figsize=(10, 4), sharey=True)
12
13sns.kdeplot(values, bw_adjust=0.5, fill=True, ax=axes[0])
14axes[0].set_title("Lower bandwidth")
15
16sns.kdeplot(values, bw_adjust=1.8, fill=True, ax=axes[1])
17axes[1].set_title("Higher bandwidth")
18
19for ax in axes:
20    ax.set_xlabel("Value")
21    ax.set_ylabel("Density")
22
23plt.tight_layout()
24plt.show()

If the two charts tell very different stories, that is a signal to look carefully at your data rather than blindly trusting the default.

Comparing Groups

Density plots are often most valuable when comparing distributions. For example, you may want to compare response times from two service versions or exam scores from two classes.

python
1import numpy as np
2import pandas as pd
3import seaborn as sns
4import matplotlib.pyplot as plt
5
6rng = np.random.default_rng(123)
7frame = pd.DataFrame({
8    "latency_ms": np.concatenate([
9        rng.normal(140, 18, 400),
10        rng.normal(120, 15, 400)
11    ]),
12    "version": ["old"] * 400 + ["new"] * 400
13})
14
15sns.kdeplot(data=frame, x="latency_ms", hue="version", fill=True, common_norm=False)
16plt.xlabel("Latency in milliseconds")
17plt.title("Latency distribution by version")
18plt.show()

Using common_norm=False makes each group easier to interpret on its own. That matters when group sizes differ.

When to Use a Density Plot

A density plot works best when the variable is continuous and you have enough observations to support smoothing. It is excellent for:

  • checking skew and spread
  • comparing distributions across groups
  • spotting multiple peaks that suggest different subpopulations
  • building intuition before formal statistical analysis

It is less useful when the data is strongly discrete, such as small integer counts, or when you have very few observations. In those cases, the smooth curve can look more certain than the data actually supports.

Common Pitfalls

Treating density as count is the most common mistake. A taller peak does not automatically mean more total records unless the groups are normalized in the way you expect.

Choosing bandwidth without inspection can distort the story. If the chart seems too jagged or too flat, change bw_adjust and compare the result.

Using density plots for tiny samples can be misleading. With only a handful of data points, a box plot, rug plot, or raw-value scatter may be more honest.

Overlaying too many groups on one chart quickly makes the plot unreadable. If you need to compare many categories, small multiples are often clearer.

Summary

  • a density plot is a smoothed estimate of a continuous distribution
  • the y-axis shows density, not raw frequency
  • 'seaborn.kdeplot is a practical way to build one in Python'
  • bandwidth controls smoothness and should be inspected, not ignored
  • density plots are best for continuous data with enough observations to support a stable estimate

Related reading
Course
Intermediate
27 lessons
15 hours
DSA Fundamentals

Master algorithmic patterns and data structures through hands-on LeetCode-style problems - from arrays and hashing to dynamic programming and advanced graphs.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

ML System Design practice on Codemia

Design recommenders, ranking systems and training pipelines the way ML interviews actually ask for them, with worked solutions.

Practice ML system design

All Rights Reserved.