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.
These binaries (installable software) and packages are in development.
They may not be fully stable and should be used with caution. We make no claims about them.