# dumbbell R Package

#### Foo Cheung

#### 2026-07-06

## 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:

1. an ID column for the y-axis labels,
2. a grouping or facet key,
3. the first numeric value,
4. 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

Argument | Description
--- | ---
`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.