Back to List

KDE and Histograms β€” Visualizing Distributions

Understand the difference between histograms and KDE (kernel density estimation), and learn how to visualize data distributions with seaborn.

Intermediate
|
10min
|
Verified (2026-07)
KDEhistogramdistribution visualizationkernel density estimationdata distribution
Progress0/17 (0%)

KDE and Histograms: Visualizing Distributions

After completing this topic, you will be able to:

  • Explain the difference between histograms and KDE plots.
  • Visualize data distributions using Seaborn to understand the shape of your data.

Why Look at Distributions?

When analyzing data, the mean and standard deviation alone are not enough:

python
import numpy as np
data_a = np.array([50, 50, 50, 50, 50])
data_b = np.array([10, 30, 50, 70, 90])
print(f"Mean of A: {data_a.mean()}, Mean of B: {data_b.mean()}") # Both are 50

The means are the same, but the shape of the data is completely different. A is clustered in one place, while B is spread out. Visualizing the distribution reveals these differences.


Histograms: Viewing Frequency by Bar

A histogram divides data into bins and represents the number of data points in each bin with the height of a bar.

python
import matplotlib.pyplot as plt
import numpy as np
np.random.seed(42)
data = np.random.normal(170, 10, 1000) # Mean 170, standard deviation 10, 1000 heights
plt.figure(figsize=(8, 4))
plt.hist(data, bins=30, color='steelblue', edgecolor='white', alpha=0.7)
plt.xlabel('Height (cm)')
plt.ylabel('Frequency')
plt.title('Height Distribution (Histogram)')
plt.show()

bins=30 means the entire range is divided into 30 bins. The shape changes depending on the number of bins:

python
fig, axes = plt.subplots(1, 3, figsize=(12, 3))
for ax, b in zip(axes, [5, 30, 100]):
ax.hist(data, bins=b, color='steelblue', edgecolor='white')
ax.set_title(f'bins={b}')
plt.tight_layout()
plt.show()
  • Too few bins: Loses detail.
  • Too many bins: Shows noise.
  • Finding the right number of bins is a challenge in histograms.

KDE: Viewing Density with Smooth Curves

KDE (Kernel Density Estimation) is a smoothed version of the histogram. It places a small bell-shaped curve (kernel) on each data point and sums them up.

python
import seaborn as sns
plt.figure(figsize=(8, 4))
sns.kdeplot(data, fill=True, color='steelblue', alpha=0.5)
plt.xlabel('Height (cm)')
plt.title('Height Distribution (KDE)')
plt.show()

Advantages of KDE:

  • No need to choose the number of bins (because it's a continuous curve).
  • Visually smooth β€” intuitively shows the shape of the distribution.
  • Easy to compare two distributions β€” clear when overlaid.

Histograms + KDE Together

python
plt.figure(figsize=(8, 4))
sns.histplot(data, bins=30, kde=True, color='steelblue',
edgecolor='white', alpha=0.5, stat='density')
plt.xlabel('Height (cm)')
plt.title('Histogram + KDE')
plt.show()

Setting stat='density' changes the y-axis of the histogram from frequency to density, so that the scale matches the KDE curve.


Comparing Two Groups

python
np.random.seed(42)
male = np.random.normal(175, 8, 500)
female = np.random.normal(162, 7, 500)
plt.figure(figsize=(8, 4))
sns.kdeplot(male, fill=True, label='Male', alpha=0.4)
sns.kdeplot(female, fill=True, label='Female', alpha=0.4)
plt.xlabel('Height (cm)')
plt.legend()
plt.title('Height Distribution Comparison by Gender')
plt.show()

KDE makes it intuitive to see the differences and overlap between the two groups by showing the overlapping parts transparently.


Various Distribution Shapes

python
np.random.seed(42)
fig, axes = plt.subplots(1, 4, figsize=(16, 3))
# Normal distribution β€” bell-shaped, symmetric
normal = np.random.normal(0, 1, 1000)
sns.kdeplot(normal, ax=axes[0], fill=True)
axes[0].set_title('Normal Distribution')
# Right-skewed distribution β€” mostly small, with a few large values (income distribution)
skewed = np.random.exponential(2, 1000)
sns.kdeplot(skewed, ax=axes[1], fill=True)
axes[1].set_title('Right-Skewed')
# Bimodal distribution β€” a mixture of two groups
bimodal = np.concatenate([np.random.normal(-2, 0.5, 500),
np.random.normal(2, 0.5, 500)])
sns.kdeplot(bimodal, ax=axes[2], fill=True)
axes[2].set_title('Bimodal Distribution')
# Uniform distribution β€” all values have similar frequency
uniform = np.random.uniform(0, 10, 1000)
sns.kdeplot(uniform, ax=axes[3], fill=True)
axes[3].set_title('Uniform Distribution')
plt.tight_layout()
plt.show()

By looking at the shape of the distribution, you can immediately understand the characteristics of the data. If a bimodal distribution appears, you can gain the insight that "there are two groups mixed in the data."


Histogram vs. KDE Selection Criteria

HistogramKDE
Accurate frequencyβœ… Can confirm the number in each bin❌ Density estimate
Distribution shapeChanges depending on the number of binsβœ… Consistent, smooth curve
Group comparisonDifficult to read when overlappingβœ… Transparent overlay makes it clear
Discrete dataβœ… Suitable❌ Suitable for continuous data
Small dataβœ… Reflects the actual data⚠️ Risk of excessive smoothing

The safest approach is to use both together: check the actual distribution with a histogram and grasp the overall trend with a KDE.



The bandwidth Parameter in KDE

The most important setting in KDE is the bandwidth. It determines the width of the kernel placed on each data point:

python
fig, axes = plt.subplots(1, 3, figsize=(12, 3))
bandwidths = [0.1, 0.5, 2.0]
for ax, bw in zip(axes, bandwidths):
sns.kdeplot(data, bw_adjust=bw, ax=ax, fill=True)
ax.set_title(f'bw_adjust={bw}')
plt.tight_layout()
plt.show()
  • Small bandwidth (0.1): Reflects noise, jagged.
  • Appropriate bandwidth (0.5~1.0): Captures the trend of the data well.
  • Large bandwidth (2.0): Overly smoothed, loses fine details.

bw_adjust in Seaborn is the coefficient multiplied by the automatically calculated bandwidth. 1.0 is the default value and works well in most cases.


In Practice: Finding Outliers

Visualizing distributions is effective for detecting outliers:

python
np.random.seed(42)
normal_data = np.random.normal(100, 15, 1000)
outliers = np.array([200, 210, 220, -50])
data_with_outliers = np.concatenate([normal_data, outliers])
fig, axes = plt.subplots(1, 2, figsize=(12, 4))
# Check for outliers with a histogram
axes[0].hist(data_with_outliers, bins=50, color='steelblue', edgecolor='white')
axes[0].set_title('Histogram β€” Outliers Visible in the Tail')
# Clearer with a box plot
axes[1].boxplot(data_with_outliers, vert=False)
axes[1].set_title('Box Plot β€” Outliers Marked as Points')
plt.tight_layout()
plt.show()

If the tail of the KDE or histogram looks longer than expected, suspect outliers. Using it with a box plot allows for more accurate diagnosis.



Seaborn's displot: Unified Distribution Visualization

python
# displot provides histograms, KDEs, and ECDF in a single API
sns.displot(data, kind="hist", kde=True, bins=30, height=4, aspect=2)
plt.show()
# ECDF β€” Cumulative Distribution Function
sns.displot(data, kind="ecdf", height=4, aspect=2)
plt.show()

displot is a unified interface introduced in Seaborn 0.11+. Select from "hist", "kde", or "ecdf" with the kind parameter, and you can easily compare by group with the hue parameter.


Visualizing distributions is the first step in data analysis. Patterns that are missed by just looking at the mean and standard deviation become apparent in a single graph.

πŸ’¬ Questions & Comments

0 comments

You can post without signing in. Guest comments cannot be edited or deleted by their author.

0/2000

Loading...