Histogram & Density Plot (Python)

Authors

[Editor] Shixiang Wang;

[Contributors] Claude Code โ€” AI-generated, independently AI-reviewed;

Modified

2026-10-19

๐Ÿค– AI Skill โ€” Download the Bizard unified skill for your AI assistant โฌ‡๏ธ Download Skill ZIP
๐Ÿ”Œ MCP Server โ€” Let AI agents (Claude Code, Cursor, โ€ฆ) query Bizard live: claude mcp add bizard -- npx @openbiox/bizard-mcp โ€” or the free hosted instance (no install, faster from mainland China): claude mcp add --transport http bizard http://biotree.top:38123/bizard-mcp/mcp ๐Ÿ“– MCP Docs
๐Ÿ“– Cite Bizard โ€” if Bizard helps your research, please cite: Li, K., Zheng, H., Huang, K., et al. (2026). Bizard: A Community-Driven Platform for Accelerating and Enhancing Biomedical Data Visualization. iMetaMed, e70038. ๐Ÿ”— DOI / BibTeX

Histograms and kernel density estimates (KDE) reveal the shape of a distribution โ€” modality, skewness, and spread that summary statistics conceal. In biomedical research they are routinely used to inspect gene expression distributions, fluorescence intensity profiles, and quality-control metrics before statistical modeling. Pythonโ€™s seaborn provides histogram, density, and empirical CDF displays in a single consistent interface.

Example

Setup

  • System Requirements: Cross-platform (Linux/MacOS/Windows)
  • Programming Language: Python
  • Dependencies: matplotlib, seaborn, pandas, numpy
import matplotlib.pyplot as plt
import seaborn as sns
import pandas as pd
import numpy as np

Data Preparation

We simulate the expression of one gene measured in control and stimulated conditions. The stimulated condition is a mixture of two cell subpopulations, producing the bimodality that histograms and KDE are designed to expose.

np.random.seed(11)

n = 800
expr = np.concatenate([
    np.random.normal(4.0, 0.8, n),        # control
    np.random.normal(4.2, 0.8, n // 2),   # stimulated, subpopulation A
    np.random.normal(7.5, 0.9, n // 2),   # stimulated, subpopulation B
])
df = pd.DataFrame({
    'Expression': expr,
    'Condition': np.repeat(['Control', 'Stimulated'], [n, n])
})

Visualization

Histogram

Bin width controls the biasโ€“variance trade-off: too few bins hide structure, too many amplify noise. Compare bins=15 with finer settings when in doubt.

plt.figure(figsize=(6, 4.2))
ax = sns.histplot(data=df, x='Expression', bins=30,
                  color='#8da0cb', edgecolor='white', linewidth=0.6)
ax.set_xlabel('Gene expression (log2 counts)')
ax.set_ylabel('Count')
ax.spines[['top', 'right']].set_visible(False)
plt.tight_layout()
plt.show()
Figureย 1: Histogram of simulated expression values

Histogram with KDE Overlay

A kernel density estimate drawn on the count axis outlines the distribution while the bars keep the raw counts visible.

plt.figure(figsize=(6, 4.2))
ax = sns.histplot(data=df, x='Expression', bins=30, kde=True,
                  color='#66c2a5', edgecolor='white', linewidth=0.6,
                  line_kws=dict(linewidth=1.8))
ax.set_xlabel('Gene expression (log2 counts)')
ax.set_ylabel('Count')
ax.spines[['top', 'right']].set_visible(False)
plt.tight_layout()
plt.show()
Figureย 2: Histogram with a kernel density overlay

Comparing Groups with Density Curves

For two or more groups, overlaid density curves (one per condition) make the bimodality of the stimulated sample directly visible.

plt.figure(figsize=(6, 4.2))
ax = sns.kdeplot(data=df, x='Expression', hue='Condition',
                 fill=True, common_norm=False, alpha=0.35,
                 palette={'Control': '#457b9d', 'Stimulated': '#e63946'},
                 linewidth=1.8)
ax.set_xlabel('Gene expression (log2 counts)')
ax.set_ylabel('Density')
ax.spines[['top', 'right']].set_visible(False)
plt.tight_layout()
plt.show()
Figureย 3: Overlaid density curves for control vs stimulated conditions

Empirical Cumulative Distribution Function

The ECDF shows every observation without binning or smoothing, making it ideal for auditing distributional shift between cohorts.

plt.figure(figsize=(6, 4.2))
ax = sns.ecdfplot(data=df, x='Expression', hue='Condition',
                  palette={'Control': '#457b9d', 'Stimulated': '#e63946'},
                  linewidth=1.8)
ax.set_xlabel('Gene expression (log2 counts)')
ax.set_ylabel('Proportion')
ax.spines[['top', 'right']].set_visible(False)
plt.tight_layout()
plt.show()
Figureย 4: Empirical cumulative distribution functions by condition

References

  1. Hunter, J. D. (2007). Matplotlib: A 2D graphics environment. Computing in Science & Engineering, 9(3), 90-95.
  2. Waskom, M. L. (2021). seaborn: statistical data visualization. Journal of Open Source Software, 6(60), 3021.