Written and maintained by CASRAI Editorial Board
Last updated
A histogram in Matplotlib (generated via matplotlib.pyplot.hist or the Axes method ax.hist) estimates the probability distribution of a continuous variable by dividing data into discrete intervals (bins) and displaying the count or density of observations in each bin. In scientific research, generating a matplotlib histogram provides immediate visual verification of data distribution, central tendency, variance, and multimodal clustering prior to inferential testing.
What it is used for in research
In empirical research, continuous experimental measurements require distributional inspection before researchers apply statistical tests. Creating matplotlib histograms serves several critical analytical functions across laboratory and computational workflows:
- Distribution Shape and Modality: Researchers inspect data for unimodal, bimodal, or multimodal patterns to determine whether underlying subpopulations exist within an experimental cohort.
- Skewness and Outlier Detection: Visualizing distribution tails reveals extreme values and skewness, such as a right-skewed histogram, indicating when mean-based metrics may distort results relative to an average vs median assessment.
- Statistical Assumption Verification: Parametric procedures such as Student’s t-test or analysis of variance (ANOVA) assume approximate normality in residuals. Plotting empirical histograms helps researchers decide between parametric vs non-parametric tests.
- Bivariate Density Mapping: When analyzing paired coordinate data, such as single-cell measurements or particle velocities, two-dimensional histograms (
matplotlib.pyplot.hist2d) display density concentrations without point overplotting.
Install or access (official source only)
Matplotlib is distributed as an open-source Python library. According to official Matplotlib documentation, the package can be installed in an active virtual environment using standard package managers.
To install or update Matplotlib via pip from PyPI, run the following terminal command:
python -m pip install -U matplotlib
For detailed pip flags, Python compatibility matrices, and build troubleshooting, see our guide to install Matplotlib with pip. If you manage dependencies using Anaconda or Miniconda, install the package from the conda-forge channel:
conda install -c conda-forge matplotlib
The standard research practice imports for numerical analysis and figure generation are:
import matplotlib.pyplot as plt
import numpy as np
Step-by-step task
The following step-by-step tutorial demonstrates how to construct reproducible 1D and 2D histograms using Matplotlib’s object-oriented interface (fig, ax = plt.subplots()), which is recommended over stateful pyplot scripting for research publications.
Step 1: Prepare the observational data
Input data for matplotlib hist must be a one-dimensional array-like sequence of real numbers, such as a NumPy array, a pandas Series, or a Python list. Ensure all non-finite entries (NaN or infinite values) are filtered prior to passing data to the plotting function.
# Generate reproducible synthetic measurement data
rng = np.random.default_rng(seed=42)
control_group = rng.normal(loc=50.0, scale=10.0, size=500)
treatment_group = rng.normal(loc=58.0, scale=12.0, size=500)
Step 2: Choose bin edges and calculation strategies
By default, Matplotlib assigns 10 equal-width bins (governed by rcParams["hist.bins"]). In research practice, 10 bins is often arbitrary and can obscure meaningful structure. You can supply an integer number of bins, an explicit sequence of bin edges, or an automated bin selection algorithm string supported by NumPy:
bins='fd'(Freedman-Diaconis rule): Sets bin width to 2 × IQR × n^(-1/3). Highly robust against outliers and heavily skewed distributions.bins='scott'(Scott’s rule): Sets bin width to 3.49 × σ × n^(-1/3). Optimal for normally distributed continuous datasets.bins='sturges': Default NumPy heuristic based on sample size (k = ⌈log2(n) + 1⌉). Tends to oversmooth large research datasets.bins=np.linspace(20, 90, 36): Explicitly sets 35 bins of uniform 2-unit width between 20 and 90.
Step 3: Construct a publication-ready 1D histogram
This histogram matplotlib example demonstrates setting up an explicit figure canvas, computing bins with the Freedman-Diaconis rule, adding density normalization, and styling the plot for manuscript submission:
# Initialize figure and axes using the Object-Oriented API
fig, ax = plt.subplots(figsize=(7, 4.5), dpi=150)
# Generate 1D histogram with density normalization and custom edges
n, bins, patches = ax.hist(
control_group,
bins='fd',
density=True,
histtype='bar',
color='#2b5c8f',
edgecolor='white',
linewidth=0.8,
alpha=0.85,
label='Control Cohort (n=500)'
)
# Apply descriptive scientific labels and gridlines
ax.set_title('Empirical Distribution of Control Cohort Measurements', fontsize=12, fontweight='bold', pad=10)
ax.set_xlabel('Measured Value (arbitrary units)', fontsize=10)
ax.set_ylabel('Probability Density', fontsize=10)
ax.legend(frameon=True, loc='upper right')
ax.grid(axis='y', linestyle='--', alpha=0.5)
fig.tight_layout()
# fig.savefig('histogram_control.pdf', dpi=300, bbox_inches='tight')
plt.show()
Step 4: Plot overlaid comparative histograms
To compare two or more experimental treatments on the same axis without visual collision, set histtype='stepfilled' or histtype='step' with transparency (alpha) and enable density=True so groups with different sample sizes can be compared objectively:
fig, ax = plt.subplots(figsize=(7, 4.5), dpi=150)
# Plot control cohort
ax.hist(control_group, bins='fd', density=True, histtype='stepfilled',
alpha=0.5, color='#1f77b4', edgecolor='#1f77b4', linewidth=1.2, label='Control')
# Plot treatment cohort
ax.hist(treatment_group, bins='fd', density=True, histtype='stepfilled',
alpha=0.5, color='#d62728', edgecolor='#d62728', linewidth=1.2, label='Treatment')
ax.set_title('Comparative Distribution: Control vs. Treatment', fontsize=12, fontweight='bold')
ax.set_xlabel('Measured Response')
ax.set_ylabel('Probability Density')
ax.legend(loc='upper right')
ax.grid(axis='y', linestyle=':', alpha=0.6)
fig.tight_layout()
plt.show()
Step 5: Create a 2D bivariate histogram (hist2d)
For paired continuous variables (x, y) where scatter plots suffer from severe overplotting, matplotlib.pyplot.hist2d (or ax.hist2d) bins the Cartesian plane into rectangular cells and encodes point counts via a colormap:
# Generate paired correlated observations
x_coord = rng.normal(0, 1, 5000)
y_coord = x_coord * 0.7 + rng.normal(0, 0.7, 5000)
fig, ax = plt.subplots(figsize=(6.5, 5), dpi=150)
# Compute 2D histogram
h, xedges, yedges, image = ax.hist2d(
x_coord, y_coord,
bins=40,
cmap='viridis',
cmin=1 # Leave zero-count cells uncolored (white background)
)
# Attach a colorbar to report count density
cbar = fig.colorbar(image, ax=ax)
cbar.set_label('Point Count per Bin', rotation=270, labelpad=15)
ax.set_title('2D Bivariate Histogram: Coordinate Density Mapping', fontsize=12, fontweight='bold')
ax.set_xlabel('Measurement X')
ax.set_ylabel('Measurement Y')
fig.tight_layout()
plt.show()
Output and how to read it
When you call ax.hist(x, ...), Matplotlib computes the underlying distribution and returns a 3-element tuple: (n, bins, patches).
- n (array or list of arrays): The computed values for each bin. When
density=False,n[i]is the integer count of observations falling into the i-th bin interval. Whendensity=True,n[i]represents probability density. - bins (1D array): The bin edges of length
len(n) + 1. The i-th bin spans the interval frombins[i]tobins[i+1]. By default in Matplotlib, all bins except the last are half-open intervals: [a, b), while the final bin includes the upper edge: [a, b]. - patches (BarContainer): A collection of Matplotlib
Rectangleartists corresponding to each drawn bar, allowing direct post-render property adjustments (such as color modifications).
In a 1D frequency histogram, the vertical axis represents observation counts. In a density histogram (density=True), the vertical axis represents probability density, meaning the total area of all bars integrates to 1.0 (sum of bin height × bin width = 1.0). For 2D histograms created with hist2d, the function returns (h, xedges, yedges, image), where h is a 2D matrix of shape (nx, ny) representing cell counts, and image is a QuadMesh artist linked to the figure colorbar.
Troubleshooting
When generating histograms in scientific workflows, researchers commonly encounter several recurring technical and visual issues:
- ValueError from NaN or Infinite Values: If your dataset contains
np.nan,np.inf, or-np.inf,ax.histraises aValueErrorstating that max must be larger than min. Resolve this by cleaning the array withdata = data[np.isfinite(data)]prior to plotting. - Singularities with Logarithmic Scaling: Setting
log=Trueorax.set_yscale('log')fails or displays empty bars when bins have zero counts, because log(0) is undefined. Use thebottomparameter (e.g.bottom=0.1) or passnonpositive='clip'to handle zero-count bins safely. - Distortion from Inadequate Bins: Relying on the default 10 bins can create misleading visual artifacts, masking true bimodal separations or exaggerating random noise. Test
bins='fd'orbins='scott', and avoid bad data visualization practices such as arbitrary bin truncation. - Overplotting and Occlusion: Plotting multiple opaque bar histograms on the same axes obscures background series. Use
histtype='step',histtype='stepfilled'withalpha=0.5, or consider a dedicated Seaborn boxplot or violin plot for high-dimensional group comparisons. - Density Heights Exceeding 1.0: Researchers sometimes confuse probability density with relative percentage. When bin widths are narrower than 1.0 unit (e.g. width = 0.05), the bar height will routinely exceed 1.0 so that the total area (height × width) integrates to 1.0.
Free alternatives (table)
The following table summarizes open-source alternatives for visualizing empirical data distributions in academic research, highlighting their primary capabilities and recommended use cases:
| Tool / Library | Environment | Key Histogram Capabilities | Best Research Use Case |
|---|---|---|---|
| Matplotlib (plt.hist / hist2d) | Python | Direct programmatic control over bin edges, patches, density normalization, and 2D bin meshes. | Publication-ready standalone figures, multi-panel axes, and highly customized figure layouts. |
| Seaborn (sns.histplot) | Python | Built-in Kernel Density Estimation (KDE) overlays, automated hue grouping, and semantic faceting. | Statistical exploration and multi-variable cohort comparisons (see Seaborn pairplot). |
| Pandas (df.hist / Series.plot.hist) | Python | Convenient one-line plotting wrapper built on top of Matplotlib directly from DataFrames. | Rapid preliminary distribution checks during exploratory data wrangling and cleaning. |
| SciPy (scipy.stats) | Python | Continuous parametric distribution fitting (norm, gamma, beta) against empirical histograms. | Formal goodness-of-fit testing and statistical modeling overlaid on histogram plots. |
| R (hist / ggplot2 geom_histogram) | R | Declarative grammar of graphics, automated bin width warnings, and native statistical summaries. | Biostatistical manuscripts and reproducible R Markdown / Quarto computational reports. |
FAQ
How do I set specific bin widths in a Matplotlib histogram?
To define an exact, fixed bin width (such as 5 units), generate an explicit array of bin edges using numpy.arange covering your data range, and pass it to the bins parameter: ax.hist(data, bins=np.arange(min_val, max_val + bin_width, bin_width)).
How do I plot two overlapping histograms in Matplotlib?
Call ax.hist twice on the same Axes object. To prevent the foreground series from completely obscuring the background series, specify alpha=0.5, use histtype='stepfilled' or histtype='step', and assign distinct colors and labels to each cohort.
What is the difference between density=True and raw counts in Matplotlib?
When density=False (the default), bar heights represent the absolute frequency count of samples within each bin interval. When density=True, the histogram is normalized so that the total area under the bars equals 1.0, enabling fair visual comparisons between datasets with unequal sample sizes.
How do I create a 2D histogram with a colorbar in Matplotlib?
Use matplotlib.pyplot.hist2d(x, y, bins=30) or ax.hist2d(x, y, bins=30). Capture the returned 4-tuple h, xedges, yedges, image = ax.hist2d(...), and pass the image object to fig.colorbar(image, ax=ax) to display the count scale.
Why does ax.hist() return three objects, and how do I use them?
The return values (n, bins, patches) provide programmatic access to the underlying computational results: n contains the bin counts or densities, bins contains the edge coordinates, and patches contains the individual graphical bar objects. Researchers use n and bins to conduct downstream quantitative verification or export numerical distribution tables.
Sources (official docs)
- Matplotlib Development Team.
matplotlib.pyplot.hist. Matplotlib 3.11.2 Documentation. URL: https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.hist.html. Accessed 2026-11-07. - Matplotlib Development Team.
matplotlib.pyplot.hist2d. Matplotlib 3.11.2 Documentation. URL: https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.hist2d.html. Accessed 2026-11-07. - NumPy Developers.
numpy.histogram: Bin selection algorithms and reference. NumPy v2.5 Manual. URL: https://numpy.org/doc/stable/reference/generated/numpy.histogram.html. Accessed 2026-11-07. - Matplotlib Development Team. Installing Matplotlib. Matplotlib 3.11 Documentation. URL: https://matplotlib.org/stable/users/installing/index.html. Accessed 2026-11-07.







