Quick Reference · stacked density ridgelines

joypy cheat sheet

One function, one job. joypy.joyplot(df) takes a DataFrame, estimates a density (KDE) for each numeric column — or each group via by= — and stacks them into overlapping ridgelines. It returns a plain Matplotlib (fig, axes), so everything you know about saving and styling still applies. Named after Joy Division's Unknown Pleasures cover.

import / setup data & grouping density (KDE / hist) overlap & range colour & fill labels & output gotcha most common

Distilled & cross-checked against the joypy source (no formal docs exist): github.com/leotac/joypy (joyplot.py + README + Joyplot.ipynb) · python-charts.com · python-graph-gallery.com · blanchardjulien.com · Wikipedia: ridgeline plot

One call — DataFrame in, stacked ridgelines out
THE ENTIRE PIPELINE 1 · DataFrame numeric columns or groups via by= 2 · Density each KDE (scipy) by default or hist=True 3 · Stack one row per column/group offset by overlap 4 · (fig, axes) a Matplotlib figure + n+1 axes back WHAT COMES BACK — n ROWS + 1 GLOBAL AXIS axes[0]→ the top ridge's own axis axes[1] … axes[n-1]→ each subsequent row axes[-1]→ the global / background axis (style the whole figure here) len(axes) == number_of_rows + 1
The minimal call, then the usual dressing-up
import joypyimport pandas as pdfrom matplotlib import cmiris = pd.read_csv("iris.csv")fig, axes = joypy.joyplot(iris) # one ridge per numeric columnfig, axes = joypy.joyplot(iris, by="Name", column="SepalLength", # one ridge per group colormap=cm.viridis, overlap=2, fade=True)fig.savefig("joy.png", dpi=300, bbox_inches="tight") # it's just Matplotlib
01Setup & Importonce per project
02The One Functionthe entire API
03Data Input Modeswhat it accepts
04Grouping: by & columnrows vs values
05Density: KDE vs Histogramhow the curve is made
06Density Kind & Bandwidthfiner control
07Overlap & Stackingthe signature knob
08Range & x-axiswhere curves live
09Colour & Colormapthe palette
10Fill, Lines & Backgroundcurve styling
11Labels & Titleannotate
12Legend & Gridextras
13Output & the Axessave & tweak
14Recipescopy & adapt

Four ideas that explain the plot

What a joyplot actually is, the three ways to feed data in, how the one signature knob reshapes the stack, and the KDE-vs-histogram choice behind every curve. Based on the joypy source & example notebook.

anatomy of a joyplot

Each row is one distribution's density, drawn on its own axis and stacked so neighbours overlap — the ridgeline.

201920202021 shared x-axis · each row = one density · rows overlap

three ways to feed it

Ridges can come from columns, from groups, or from both at once — same function, different arguments.

joyplot(df) per column ABC by="g" per group g1g2g3 by + column=[..] groups × vars one function → three shapes of comparison

overlap = the signature knob

One number sets how far each row rides up into the one above — from tidy separation to the classic dense stack.

overlap=0 overlap=1 overlap=2

KDE vs histogram

The same data, two estimators: a smooth kernel density (default) or discrete bars with hist=True.

default · KDE smooth curve hist=True binned bars

Worth memorizing

returns (fig, axes)it's a Matplotlib wrapper — save/style as usual
len(axes) == n+1n rows plus a global axis at axes[-1]
KDE by defaultscipy gaussian_kde; pass hist=True for bars
numeric onlynon-numeric columns are silently skipped
by vs columnby= groups rows · column= selects values
overlap is the knob0 separated · 1 default · >1 denser stack
ylim="max" vs "own"shared scale across rows, or each self-scales
colormap needs an objectcm.viridis, not the string "viridis"
colormap vs colorcolormap= gradients rows · color= is flat / a list
fill forces linecolorfill=True sets linecolor="k" unless you override
range_style"all" · "own" · "group" control each row's x-range
no formal docsthe README notebook & joyplot.py source are the reference