Home > Mobile >  How to order stack bar based on cumulative value in each group
How to order stack bar based on cumulative value in each group

Time:06-25

I'm trying to plot a geom_bar with the stack order depending on the cumulative value of each group in each bar. therefore, each bar should have a different order. Here is a reproducible example

set.seed(8)

#create dataframe
name <- c("a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "v", "w", "x", "y", "z", "aa")
group <- c("1","1","1","2","2", "2", "1","1","1","1","1","1","1","1","1","1","1","1","2","2","2","2","2","2","2","2")
subgroup <- c("C", "C", "C", "C", "C", "C", "A", "A", "A","A","A","A","B","B","B","B","B","B","B","B", "C", "C", "C", "C", "C", "C")
value <- runif(26, min = 0, max = 10)

df <- data.frame(name, group, subgroup,value)

#plot
ggplot(data=df, aes(x=group, y=value))   
  geom_bar(stat="identity",aes(fill=reorder(subgroup, value)))

The latter results in the following plot: enter image description here

Note that it is clear that the B subgroup represents a higher value than C in group 1, but it is plotted above C. I want that, for each group, the subgroups are ordered based on their cumulative value in the group. Does anybody know how to achieve this? Thanks in advance!

CodePudding user response:

You could do this by precalculating the counts and using geom_tile instead of geom_bar. This way will work automatically with any number of groups and subgroups without manual intervention.

library(dplyr); library(ggplot2)
df %>%
  count(group, subgroup, wt = value) %>%
  group_by(group) %>%
  arrange(group, -n) %>%
  mutate(middle = cumsum(n) - n/2) %>% 
  ungroup() %>%

  ggplot(aes(group, fill = subgroup))  
  geom_tile(aes(y = middle, height = n), width = 0.9)

enter image description here

CodePudding user response:

Try using a new column with the cumulative values and then use fct_reorder to order the stacks.

see example below:

df %>%
   group_by(group,subgroup) %>% 
   mutate(csum = cumsum(value)) %>% 
   ggplot() 
   aes(x = group, y = csum) 
   geom_bar(stat = "identity", aes(fill = fct_reorder(subgroup, csum)))
  

enter image description here

CodePudding user response:

And another option would be to use the group aes. To this end first aggregate your data and arrange your data by group and (cumulated) value as in the approach by @JonSpring. Afterwards you could add a helper column as the interaction of group and subgroup and set the order via forcats::fct_inorder. This helper column could then be mapped on the group aes to set the order of the stack:

library(dplyr)
library(ggplot2)

df1 <- df |>
  count(group, subgroup, wt = value) |> 
  arrange(group, n) |> 
  mutate(group_aes = forcats::fct_inorder(paste(group, subgroup, sep = ".")))

# plot
ggplot(data = df1, aes(x = group, y = n, group = group_aes))  
  geom_col(aes(fill = subgroup))

  • Related