Histograms in Python
A histogram (also called a histogram chart, histogram plot, or histogram
graph) bins continuous values into intervals and draws a bar for the count in
each bin, showing the shape of a distribution. With xy you build a
python histogram that stays interactive at scale: bin hundreds of thousands of
values, then pan, zoom, and hover without lag.
Jump to creating a histogram, density and cumulative modes, or options.
Create a Histogram
Pass a 1-D array of samples to histogram and choose the number of bins.
This is the minimal Python histogram:
Density and Cumulative Histograms
By default a histogram plots raw counts. Set density=True to normalize the
bars into a probability density that integrates to 1, which is useful when
comparing distributions with different sample sizes. Set cumulative=True to
accumulate counts from left to right and read off how much of the data falls
below any value — a close cousin of the ECDF.
Probability Density with Styled Bars
Set density=True to normalize the bars, clamp binning to a (min, max)
window with range, and soften the bars with corner_radius and opacity.
Overlay Two Distributions
Stack two histogram marks in one chart with shared bins and range, name
each with name=, lower opacity so both stay readable, and add xy.legend().
Large Histograms
xy computes bins over the raw f64 samples and draws only the bin bars, so the
cost scales with the bin count, not the sample count. Binning hundreds of
thousands of values stays fast, and the chart pans and zooms without
recomputing the whole distribution.
Histogram Options
Pass a column name with data= instead of an array when your data is a table.
Related Charts
- ECDF plots — the cumulative distribution without binning.
- Box plots — summarize a distribution with quartiles.
- Violin plots — show the full density shape.
API Reference
xy.histogram_chart
A histogram chart composing `histogram` marks and axis/legend children.
Props
| Prop | Type | Description |
|---|---|---|
*children | Component | Marks, axes, annotations, and chart chrome. |
title | Optional[str] | Title shown above the plot. |
width | int | str | Chart width in pixels or a CSS size such as ``"100%"``. |
height | int | str | Chart height in pixels or a CSS size such as ``"100%"``. |
padding | Union[float, Sequence[float], None] | Plot margins, as one value or a sequence of side values. Use zero for an edge-to-edge sparkline. |
data | TableLike | Chart-level data used by marks that omit their own ``data``. |
class_name | Optional[str] | CSS class applied to the chart container. |
class_names | Optional[dict[str, str]] | CSS classes keyed by stable chart DOM slot. |
style | Optional[dict[str, StyleValue]] | Inline style overrides for the chart container. |
styles | Optional[dict[str, dict[str, StyleValue]]] | Inline style mappings keyed by stable chart DOM slot. |
on_hover | Optional[Callable[[dict], None]] | Callback receiving hover event payloads. |
on_click | Optional[Callable[[dict], None]] | Callback receiving picked-mark click payloads. |
on_brush | Optional[Callable[[dict], None]] | Callback receiving brush event payloads. |
on_select | Optional[Callable[[Selection], None]] | Callback receiving data-space selections. |
on_view_change | Optional[Callable[[dict], None]] | Callback receiving viewport change payloads. |
hover | Optional[bool] | Whether pointer movement emits hover events. |
click | Optional[bool] | Whether picked marks emit click events. |
select | Optional[bool] | Whether shift-drag box selection is enabled. |
brush | Optional[bool] | Whether brush selection is enabled. |
crosshair | Optional[bool] | Whether plot-aligned hover guides are shown. |
navigation | Optional[bool] | Whether browser pan and zoom navigation is enabled. |
pan | Optional[bool] | Whether plain-drag panning is enabled. |
pan_axes | Optional[tuple[str, ...]] | Declared axis IDs translated by pan gestures. |
zoom | Optional[bool] | Whether viewport zoom is enabled. |
default_drag_action | Optional[DefaultDragAction] | Initial action performed by a plain plot drag. |
zoom_axes | Optional[tuple[str, ...]] | Declared axis IDs changed by zoom gestures and controls. |
zoom_limits | Optional[ZoomLimits] | Minimum and maximum magnification globally or by axis. |
wheel_zoom | Optional[bool] | Whether wheel and trackpad zoom is available. |
box_zoom | Optional[bool] | Whether box zoom is available as a drag action. |
zoom_buttons | Optional[bool] | Whether modebar Zoom In/Out commands are available. |
double_click_reset | Optional[bool] | Whether double-click restores ``reset_axes``. |
reset_axes | Optional[tuple[str, ...]] | Declared axis IDs restored by reset. |
link_group | Optional[str] | Identifier used to synchronize charts in the browser. |
link_axes | Optional[tuple[str, ...]] | Axes synchronized within the link group. |
FAQ
How do I make a histogram in Python?
Call xy.histogram(values, bins=...) inside xy.histogram_chart(...) and
render it. Binning, axes, pan, zoom, and hover are handled automatically.
How many bins should a histogram use?
It depends on your data, but more bins reveal finer structure at the cost of
noisier bars. Start around 30–120 and adjust bins until the shape reads
clearly.
What is the difference between a histogram and a density plot?
A raw histogram shows counts per bin. Set density=True to normalize the bars
into a probability density that integrates to 1, so distributions with
different sample sizes become comparable.
Can a histogram handle large datasets?
Yes. xy bins the raw samples and draws only the bin bars, so hundreds of
thousands of values stay interactive with smooth pan and zoom.