Stem Plots in Python
A stem plot (also called a stem chart or stem graph) draws each value as a
vertical line, or impulse, rising from a
common baseline to a marker at the data point. With xy you build a stem plot
in Python for discrete sequences — sample indices, event counts, or signal taps
— where a connecting line would imply continuity that isn't there. Every stem
plot is interactive by default: pan, zoom, and hover work with no configuration.
Jump to creating a stem plot, when to use one, or the options.
Create a Stem Plot
Pass equal-length x and value arrays to stem. Each point becomes an impulse
anchored to the baseline with a marker at its tip:
Style Stems and Markers
Values may swing above and below the baseline, and width, opacity,
marker_size, and symbol control how each impulse and its tip are drawn:
Compare Stem Series with an Overlay
Layer multiple stem marks (offset the x values slightly so stems don't
overlap), disable markers with marker=False, and add a dashed line envelope
for context:
When to Use a Stem Plot
Reach for a stem plot when values are discrete or sampled and each observation should stay anchored to a baseline — impulse responses, counts, or spectral taps. To mark thresholds or reference levels alongside the stems, use the annotations component. When observations form a continuous trend that should be connected instead, use the line chart.
Stem Plot Options
Pass column names with data= instead of arrays when your values live in a
table.
Related Charts
- Line charts — connect ordered observations to show a continuous trend.
- Line segments — draw independent start-to-end segments with their own endpoints.
API Reference
xy.stem_chart
A stem chart composing `stem` marks.
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 stem plot in Python?
Call xy.stem(x, values) inside xy.chart(...) and render it. Each value is
drawn as an impulse from the baseline to a marker, and pan, zoom, and hover are
enabled automatically.
When should I use a stem plot instead of a line chart?
Use a stem plot for discrete or sampled values where a connecting line would falsely imply continuity between points. Use a line chart when the data is a continuous trend that should be connected.
How do I add a legend to a stem plot?
Give the stem mark a name and add xy.legend() inside the same
xy.chart(...).
Can I change the baseline the stems anchor to?
Yes. Offset the values you pass to stem so they rise from the level you want,
as the demo does by subtracting a constant from the raw values.