A ridgeline plot (also known as a "joy plot") shows the distribution of a
numeric variable for several groups as a stack of partially overlapping
density curves that share a common x-axis. The nickname comes from the
cover of Joy Division's 1979 album Unknown Pleasures, itself a
reproduction of a 1970 plot of successive radio pulses from the first
discovered pulsar. The format was popularised by Claus Wilke's
ggridges package and by FiveThirtyEight and The Washington Post
graphics desks.
Ridgeline plots are ideal for comparing the shape of many distributions at once (e.g. across months, categories, or experimental conditions) while using far less vertical space than a set of separate histograms.
The kernel density estimates are computed in the browser. Ridges are drawn from the back (top) to the front (bottom), so lower ridges overlap the ones above them. Hovering a ridge (or its label) brings it to the front, dims the others, and shows a small label with the group's number of observations and mean.
Usage
ridgeline_plot(
data,
x,
group,
bandwidth = NULL,
kernel = c("gaussian", "epanechnikov"),
overlap = 1.5,
fill = "steelblue",
fillOpacity = 0.8,
lineCol = "#333333",
lineWidth = 1,
sort = c("none", "mean", "median"),
stat = c("none", "mean", "median"),
statColor = "#d62728",
hover = TRUE,
xtitle = NULL,
xtitleFontSize = 13,
title = NULL,
titleFontSize = 16,
xFontSize = 11,
yFontSize = 11,
font = "Verdana, Geneva, Tahoma, sans-serif",
bgcol = "white",
axisCol = "black",
width = NULL,
height = NULL
)Arguments
- data
The data frame containing the variables to consider.
- x
The name of the numeric column whose distribution is displayed.
- group
The name of the categorical column used to split the data into one ridge per group. When the column is a factor, its level order is used (unless
sortis set).- bandwidth
Optional. The kernel bandwidth, expressed as the standard deviation of the kernel (as in
stats::density()). WhenNULL(default), a single joint bandwidth is computed from the pooled data with Silverman's rule of thumb (equivalent tostats::bw.nrd0()), so that all ridges are smoothed identically and remain comparable.- kernel
The smoothing kernel, either
"gaussian"(default) or"epanechnikov".- overlap
How much each ridge may overlap the one above it, in units of the vertical distance between two consecutive baselines. The tallest peak of the chart reaches exactly
overlapbaselines up. Defaults to1.5; use1for ridges that just touch and larger values for a more dramatic, Joy Division-like look.- fill
Fill color of the ridges. Either a single color (default
"steelblue") applied to every ridge, an unnamed vector of colors recycled over the groups (in their original order), or a named vector whose names match the group values.- fillOpacity
Fill opacity of the ridges (0 to 1). Defaults to
0.8.- lineCol
Color of the density outline. Defaults to
"#333333".- lineWidth
Width of the density outline in pixels. Defaults to
1.- sort
How to order the ridges from top to bottom. One of
"none"(default, factor level order or order of appearance),"mean", or"median". When sorting, the group with the smallest mean/median is placed at the top.- stat
Character. Draws a vertical line inside each ridge at the
"mean"or"median"of its values. Defaults to"none"(no line).- statColor
Color of the mean/median line. Defaults to
"#d62728".- hover
Logical. When
TRUE(default), hovering a ridge or its label raises and highlights it and shows its number of observations and mean.- xtitle
Optional. Title of the x-axis.
- xtitleFontSize
Font size of the x-axis title. Defaults to
13.- title
Optional. Title of the chart.
- titleFontSize
Font size of the chart title. Defaults to
16.- xFontSize
Font size of the x-axis tick labels. Defaults to
11.- yFontSize
Font size of the group labels. Defaults to
11.- font
The font family used for all text. Defaults to
"Verdana, Geneva, Tahoma, sans-serif".- bgcol
Background color of the SVG. Defaults to
"white".- axisCol
Color of axis lines, ticks, and labels. Defaults to
"black".- width
Optional. The width of the SVG output.
- height
Optional. The height of the SVG output.
Examples
# Simulated reaction times for five experimental conditions
set.seed(2026)
reaction <- data.frame(
time = c(rnorm(200, 300, 40), rnorm(200, 340, 50),
rnorm(200, 380, 35), c(rnorm(100, 360, 25), rnorm(100, 460, 30)),
rnorm(200, 420, 60)),
condition = rep(c("Baseline", "Noise", "Fatigue", "Dual task", "Night"),
each = 200)
)
ridgeline_plot(
data = reaction,
x = "time",
group = "condition",
sort = "mean",
stat = "median",
title = "Reaction time by condition",
xtitle = "Reaction time (ms)"
)
# Highway fuel economy by vehicle class
library(ggplot2) # for the mpg dataset
ridgeline_plot(
data = mpg,
x = "hwy",
group = "class",
sort = "median",
fill = "#2c7fb8",
lineCol = "white",
fillOpacity = 0.9,
overlap = 2,
title = "Highway fuel economy by vehicle class",
xtitle = "Highway miles per gallon"
)
# One color per group with a named vector, Epanechnikov kernel
ridgeline_plot(
data = iris,
x = "Petal.Length",
group = "Species",
kernel = "epanechnikov",
fill = c(setosa = "#66c2a5", versicolor = "#fc8d62",
virginica = "#8da0cb"),
overlap = 1,
stat = "mean",
xtitle = "Petal length (cm)"
)