Ben Bolker
Ben Bolker

Reputation: 226182

geom_boxplot with mapped, variable widths per bar?

I would like to be able to map the width of each of the boxplots in a plot to a variable, or otherwise specify it. Let's say I want the relative widths of the boxes in the figure below to be 1, 2, 3. Setting varwidth won't help me since the actual numbers of observations are the same for each bar.

I have the beginnings of a horrible hacky solution I can post, but would welcome something actually good!

library(ggplot2)
set.seed(101) 
dd <- data.frame(f = factor(rep(LETTERS[1:3], each = 10)),
                 y = rnorm(30))
g1 <- ggplot(dd, aes(f,y)) + geom_boxplot()
print(g1)

enter image description here

Upvotes: 3

Views: 86

Answers (2)

Allan Cameron
Allan Cameron

Reputation: 173803

A bit more concise and does it all in one gulp, without having to build the plot first:

library(ggplot2)

set.seed(101) 
dd <- data.frame(f = factor(rep(LETTERS[1:3], each = 10)), y = rnorm(30))

ggplot(dd, aes(f,y)) + 
  Map(\(a, b) geom_boxplot(data = a, width = b), split(dd, dd$f), 1:3 * 0.35)

Created on 2023-02-07 with reprex v2.0.2

Upvotes: 1

Ben Bolker
Ben Bolker

Reputation: 226182

My basic idea (which would take more work to make it nice) is to ggplot_build(); hack the relevant elements in the data for the layer; and redraw the plot: example below. Obviously not as nice as having a real mapping/scaling system but maybe OK for simple cases ...

rel_wid <- c(1, 2, 3)
g1B <- ggplot_build(g1)
newdat <- g1B$data[[1]]
wids <- mean(newdat$new_width)*rel_wid/mean(rel_wid)
newdat <- within(newdat,
{
    xmin <- newx - wids/2
    xmax <- newx + wids/2
})
g2 <- g1B
g2$data[[1]] <- newdat
library(grid)
grid.draw(ggplot_gtable(g2))

enter image description here

Upvotes: 4

Related Questions