Written and maintained by CASRAI Editorial Board
Last updated
A seaborn histogram is drawn with seaborn.histplot(): pass a DataFrame and a column name, and it bins the values and draws bars whose heights follow the stat option you choose (count by default). This guide is for researchers who need a defensible distribution figure in Python: choose bins, pick the right normalisation, overlay groups without misleading the reader, and report the choices.
What it is used for in research
A histogram shows how the values of one numeric variable are distributed: where they cluster, how spread out they are, and whether there is more than one mode. In seaborn, histograms are the default plot type for both histplot() and the figure-level displot(). The seaborn tutorial on visualizing distributions advises: “It is always advisable to check that your impressions of the distribution are consistent across different bin sizes.” For the general concept, see CASRAI’s histogram guide; for the matplotlib route (ax.hist), see histogram in matplotlib.
Install or access
The seaborn installation page (read 2026-11-07) gives these commands:
pip install seabornpip install seaborn[stats]for the optional statistical dependenciesconda install seaborn, orconda install seaborn -c conda-forge
The mandatory dependencies are numpy, pandas and matplotlib. The page stated Python 3.8 or later when read; the install page carries the current requirement.
Step-by-step: draw and tune a histogram
The signature, from the official seaborn.histplot reference, begins seaborn.histplot(data=None, *, x=None, y=None, hue=None, weights=None, stat='count', bins='auto', binwidth=None, binrange=None, discrete=None, cumulative=False, common_bins=True, common_norm=True, multiple='layer', element='bars', fill=True, shrink=1, kde=False, ...).
- Import and prepare data. Put the variable in a pandas DataFrame column. The data below are generated only to make the example runnable.
import numpy as np import pandas as pd import seaborn as sns import matplotlib.pyplot as plt rng = np.random.default_rng(1) df = pd.DataFrame({ "score": rng.normal(50, 10, 300), "group": rng.choice(["A", "B"], 300), }) - Draw the basic histogram.
sns.histplot(data=df, x="score"), thenplt.show(). The default isbins='auto', which is passed tonumpy.histogram_bin_edges(). - Set the bins.
bins=30sets a number of bins,binwidth=3sets a fixed width and overridesbins, andbinrange=(20, 80)limits the range (the default is the data extremes).binscan also be a rule name or explicit edges. - Add a density curve.
sns.histplot(data=df, x="score", kde=True)overlays a kernel density estimate (univariate plots only). Smoothness is controlled throughkde_kws, for examplebw_adjust. - Split by group.
sns.histplot(data=df, x="score", hue="group"). See the comparing-groups section below for the layering options. - Change the vertical scale. Set
stat="density","percent"and so on (table below). - Bivariate version. Passing both
xandydraws a two-dimensional histogram;cbar=Trueadds a colour bar, andthresh,pthreshandpmaxcontrol which cells are shaded.
Output and how to read it
The stat options
The default stat='count' shows the number of observations per bin. The documented alternatives:
| stat | What the bars show |
|---|---|
count (default) |
Number of observations in each bin |
frequency |
Observations divided by bin width |
probability or proportion |
Heights sum to 1 |
percent |
Heights sum to 100 |
density |
Total area of the bars equals 1 |
The seaborn tutorial notes that stat="probability" suits discrete data, while stat="density" normalises so areas sum to 1. Because both are normalised, they let you compare samples of different sizes; raw counts make a larger sample look taller.
Comparing groups: layer, step, stack, dodge
With hue, the default multiple="layer" overlays translucent bars, which the tutorial says can be hard to read. Documented alternatives:
element="step"draws outlines instead of filled bars, easing comparison.multiple="stack"shows part-to-whole composition but can hide modes within groups.multiple="dodge"places bars side by side and suits only a few levels.multiple="fill"is the remaining documented option for univariate plots.
The normalisation trap. By default common_norm=True, so normalisation is applied over the whole dataset: with stat="density", each group keeps its share of the total, so a smaller group’s histogram is lower. To compare the shape of each group on its own, set common_norm=False, which normalises each subset separately. The official example is sns.histplot(data=penguins, x="bill_length_mm", hue="island", element="step", stat="density", common_norm=False). Bins are shared across groups by default (common_bins=True).
Integer and Likert data
Integer-valued data can leave gaps between bars when bins do not align with the values. Use explicit bin edges or discrete=True, which centres bars on the data points and defaults binwidth to 1. Categorical variables get discrete bins automatically. Example: sns.histplot(data=df, x="rating", stat="percent", discrete=True).
Reporting for a paper
The documentation notes that bins that are too large erase features and bins that are too small show noise. State your bin rule, width or count, and the stat in the figure caption, and check that the conclusion survives a different bin choice. A log-scaled axis is available through log_scale=True. Colour choices are covered in seaborn color palettes.
Troubleshooting
| Symptom | Documented cause and fix |
|---|---|
| Gaps between bars in integer data | Bins do not align with integer values. Use discrete=True or explicit bin edges. |
| Features vanish, or the plot looks jagged | Bins too large or too small. Try several bins or binwidth values. |
| Overlaid groups are unreadable | Use element="step", multiple="stack", or multiple="dodge" for few levels. |
A smaller group looks low under stat="density" |
common_norm=True is the default. Set common_norm=False. |
Free alternatives
| Option | Notes |
|---|---|
seaborn.displot() |
Figure-level interface in seaborn; histograms are its default plot type (seaborn tutorial). |
matplotlib Axes.hist |
The lower-level route; see histogram in matplotlib. |
numpy.histogram_bin_edges() |
The function histplot uses to compute bins when you pass a rule name. |
For a grid of pairwise distributions, see pairplot in seaborn.
FAQ
How do I make a histogram in seaborn?
Call sns.histplot(data=df, x="column"), then plt.show(). Bins default to 'auto'.
How do I change the number or width of bins?
Use bins= for a count, rule or edges, or binwidth= for a fixed width, which overrides bins.
How do I show percentages instead of counts?
Set stat="percent" so heights sum to 100, or stat="probability" so they sum to 1.
How do I add a density curve?
Set kde=True; adjust smoothness with kde_kws.
What is the difference between histplot and displot?
histplot() draws a histogram on a single axes; displot() is the figure-level distribution function. Histograms are the default for both.
Sources
- seaborn.histplot reference, seaborn.pydata.org, read 2026-11-07.
- Visualizing distributions of data, seaborn.pydata.org, read 2026-11-07.
- Installing seaborn, seaborn.pydata.org, read 2026-11-07.







