Overview
The dumbbell package creates dumbbell plots in
ggplot2. A dumbbell plot compares two numeric values
for the same item and connects them with a line segment. This is useful
when you want to show before/after values, treatment/control values,
male/female values, or any paired comparison.
The main function is dumbbell(). It expects a data frame
with at least four columns:
- an ID column for the y-axis labels,
- a grouping or facet key,
- the first numeric value,
- the second numeric value.
The function returns a ggplot object, so you can add
standard ggplot2 layers such as facet_wrap(),
labs(), theme(), or
coord_cartesian().
Installation
Install the package from CRAN with:
install.packages("dumbbell")
Or install a development version from a local source directory:
devtools::install("path/to/dumbbell")
Load packages
suppressPackageStartupMessages({
library(dumbbell)
library(dplyr)
library(ggplot2)
})
Example data
The example below creates paired measurements for two groups. Each
subject has a value for group A and group B. The data are then reshaped
into the format expected by dumbbell().
set.seed(123)
raw_data <- data.frame(
Group = rep(c("A", "B"), each = 10),
Subject = rep(paste0("sub_", 1:10), times = 2),
result = sample(1:100000, 20, replace = TRUE),
analysis = rep(rep(c("a", "b"), each = 5), times = 2)
)
group_a <- raw_data %>% filter(Group == "A")
group_b <- raw_data %>% filter(Group == "B")
plot_data <- merge(
group_a,
group_b,
by = c("Subject", "analysis")
)
plot_data <- plot_data %>%
mutate(diff = result.x - result.y) %>%
arrange(diff)
plot_data$Subject <- factor(plot_data$Subject, levels = plot_data$Subject)
head(plot_data)
Basic dumbbell plot
Use id for the labels on the y-axis, key
for the grouping variable, and column1/column2
for the paired numeric values.
dumbbell(
xdf = plot_data,
id = "Subject",
key = "analysis",
column1 = "result.x",
column2 = "result.y",
lab1 = "Group A",
lab2 = "Group B"
)

Add a delta column
Set delt = 1 to add the difference between the two
values at the right side of the plot.
dumbbell(
xdf = plot_data,
id = "Subject",
key = "analysis",
column1 = "result.x",
column2 = "result.y",
lab1 = "Group A",
lab2 = "Group B",
delt = 1,
expandx = 0.1
)

Add value labels
Set pt_val = 1 to print the numeric values next to the
points. Use col_lab1 and col_lab2 to control
the label colors.
dumbbell(
xdf = plot_data,
id = "Subject",
key = "analysis",
column1 = "result.x",
column2 = "result.y",
lab1 = "Group A",
lab2 = "Group B",
pt_val = 1,
expandx = 0.05,
col_lab1 = "blue",
col_lab2 = "red"
)

Add arrows
Set arrow = 1 to draw arrows along the connecting
segments. Use arrow_size, segsize,
pointsize, pt_alpha, col_seg1,
and col_seg2 to customize the display.
dumbbell(
xdf = plot_data,
id = "Subject",
key = "analysis",
column1 = "result.x",
column2 = "result.y",
lab1 = "Group A",
lab2 = "Group B",
arrow = 1,
arrow_size = 0.2,
segsize = 0.7,
pointsize = 1.5,
pt_alpha = 0.6,
col_seg1 = "#A9A9A9",
col_seg2 = "#A9A9A9"
)

Facet by group
Because dumbbell() returns a ggplot object,
you can add facet_wrap() directly.
dumbbell(
xdf = plot_data,
id = "Subject",
key = "analysis",
column1 = "result.x",
column2 = "result.y",
lab1 = "Group A",
lab2 = "Group B"
) +
facet_wrap(~ analysis, ncol = 1, scales = "free_y")

Add paired p-values
The pval argument adds a paired test result to the facet
label:
pval = 1 uses a paired Wilcoxon test.
pval = 2 uses a paired t-test.
The current implementation uses base R functions from the
stats package, so the package does not need to depend on
rstatix for these tests.
dumbbell(
xdf = plot_data,
id = "Subject",
key = "analysis",
column1 = "result.x",
column2 = "result.y",
lab1 = "Group A",
lab2 = "Group B",
pval = 1
) +
facet_wrap(~ analysis, ncol = 1, scales = "free_y")

Complete customized example
This example combines facets, arrows, highlighted segment colors,
point transparency, and delta labels.
dumbbell(
xdf = plot_data,
id = "Subject",
key = "analysis",
column1 = "result.x",
column2 = "result.y",
lab1 = "Group A",
lab2 = "Group B",
delt = 1,
col_seg2 = "red",
col_seg1 = "blue",
arrow = 1,
pt_alpha = 0.6,
pointsize = 2,
expandx = 0.2,
segsize = 0.5,
textsize = 2,
pval = 1
) +
facet_wrap(~ analysis, ncol = 1, scales = "free_y")

Working with axis limits
Because dumbbell() already adds an x-axis scale,
xlim() will replace that scale and may remove data outside
the requested range. To zoom without dropping observations, use
coord_cartesian():
dumbbell(
xdf = plot_data,
id = "Subject",
key = "analysis",
column1 = "result.x",
column2 = "result.y",
lab1 = "Group A",
lab2 = "Group B"
) +
coord_cartesian(xlim = c(0, 100000))
Main arguments
xdf |
Input data frame. |
id |
Column used for the y-axis labels. |
key |
Grouping variable, commonly used with
facet_wrap(). |
column1, column2 |
Paired numeric columns to compare. |
lab1, lab2 |
Labels for the two compared values. |
delt |
Set to 1 to display the difference between the two
values. |
pt_val |
Set to 1 to display point value labels. |
pval |
Set to 1 for paired Wilcoxon test or 2 for
paired t-test. |
arrow |
Set to 1 to add arrows to the connecting segments. |
pointsize, textsize,
segsize |
Control point, label, and segment sizes. |
p_col1, p_col2 |
Colors for the two point groups. |
col_seg1, col_seg2 |
Segment colors by direction. |
expandx, expandy |
Expansion around the x- and y-axes. |
Notes for package maintainers
- The p-value functionality should rely on
stats::wilcox.test() and stats::t.test() to
avoid an unnecessary dependency on rstatix.
- If the roxygen comments are changed, run
devtools::document() to regenerate NAMESPACE
and help files.
- If a
docs/ website is used, regenerate it from the
source vignette/R Markdown rather than editing generated HTML by
hand.