aiorr
aiorr

Reputation: 599

A stacked range plot using ggplot

Suppose I wish to make a range plot with the design below using ggplot with the following dummy data:

enter image description here

enter image description here with following legend.

set.seed(1)
test.dat <- data.frame(
  yval = sample(1:100, 40),
  xcat = rep(LETTERS[1:4], 10),
  base = sample(c(1, 0),40, replace=T),
  col = rep(c("red", "blue"), 40)
)

> head(test.dat)
  yval xcat base  col
1   68    A    0  red
2   39    B    0 blue
3    1    C    0  red
4   34    D    1 blue
5   87    A    0  red
6   43    B    0 blue

The gray portion shows the range of the data where base == 1 and the whisker-like line (that resembles errorbar) shows the range of the data where base == 0 using the respective color designed for each xcat.

So using this dummy data, I would expect:

minmax <- function(x){
      return(
        c(min(x),max(x))
      )
    }

> minmax(test.dat[test.dat$xcat == "D" & test.dat$base == 1,]$yval)
[1] 24 99
> minmax(test.dat[test.dat$xcat == "D" & test.dat$base == 0,]$yval)
[1] 21 82
> unique(test.dat[test.dat$xcat == "D",]$col)
[1] "blue"

for xcat == "D", a gray bar to range from 24 to 99, and a blue whisker line to range from 21 to 82.

How can I achieve this? It looks like there is no straightforward ggplot function to create a range plot.

My approach idea was to adjust geom_boxplot's hinges and whisper definition for gray part, and use geom_line or geom_linerange to create the whisker-line part, but I am unsure how to do that.

Thank you.

Upvotes: 0

Views: 179

Answers (2)

Jon Spring
Jon Spring

Reputation: 66755

I would suggest doing some reshaping first using dplyr/tidyr, and then geom_tile:

library(tidyverse)
test.dat %>%
  group_by(xcat, base, col) %>%
  summarize(mid = mean(range(yval)),
            range = diff(range(yval)), .groups = "drop") %>%
  pivot_wider(names_from = base, values_from = mid:range) %>%

ggplot(aes(x = xcat)) +
  geom_tile(aes(y = mid_0, height = range_0), fill = "gray70", color = "black") +
  geom_tile(aes(y = mid_1, height = range_1, fill = col), color = "black") +
  scale_fill_identity()

enter image description here

Upvotes: 0

david.hp24
david.hp24

Reputation: 51

You first create a dataframe where you have min and max for each combination of (xcat, base and col)

data2 <- test.dat %>% group_by(xcat, base, col) %>% summarise(min = min(yval), max=max(yval))

Then you use geom_linerange for the gray "bars" and geom_errorbar for the whisker line:

ggplot()+
   geom_linerange(data= data2 %>% filter(base==1), aes(x= xcat, ymin=min, ymax=max), size=12, alpha=0.5)+
   geom_errorbar(data= data2 %>% filter(base==0), aes(x= xcat, ymin=min, ymax=max),  colour=data2[data2$base==1,]$col, width=.2)

And this is the Plot

Upvotes: 1

Related Questions