Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

like image 364
Ben Bolker Avatar asked Jul 03 '26 09:07

Ben Bolker


2 Answers

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

like image 143
Ben Bolker Avatar answered Jul 06 '26 00:07

Ben Bolker


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

like image 22
Allan Cameron Avatar answered Jul 05 '26 22:07

Allan Cameron



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!