Home > Back-end >  ggplot dodged bar chart: arrange bars by y-value per group
ggplot dodged bar chart: arrange bars by y-value per group

Time:07-04

I need to plot a bar chart in R where the order of the bars is exactly like in the data_frame below, i.e. I need group 1 on the left, 2 in the middle and 3 on the right. But: I also need the bars to be arranged descending by score within the three groups and that I don't know how to do.

I already sorted the data_frame by 'group' and 'score' but ggplot only adopted the order of the 'group' variable:

data_scores <- data.frame(group = c(1, 1, 1, 2, 2, 2, 3, 3, 3), 
               country = c("U", "D", "M", "D", "U", "M", "D", "M", "U"), 
               score = c(10, 7, 3, 15, 12, 4, 9, 7, 5))
ggplot(data_scores, mapping = aes(x = group, y = score, fill = country))  
  geom_bar(stat = "identity", position = position_dodge())  
  geom_text(aes(label = country),
            position=position_dodge(width=0.9), angle = 90, hjust = 1)

resulting plot

This code orders the bars within each group by coutry but I need them arranged by score as it is the case in group 3.

Thank you very much in advance!

CodePudding user response:

One option would be to use a helper column made of the interaction of group and country. To this end I first order by group and score and use forcats::fct_inorder to set the order of the levels of the helper column and map it on the group aes:

library(ggplot2)
library(dplyr)

data_scores <- data_scores |>
  arrange(group, desc(score)) |>
  mutate(group_order = forcats::fct_inorder(interaction(group, country)))

ggplot(data_scores, mapping = aes(x = group, y = score, fill = country, group = group_order))  
  geom_col(position = position_dodge())  
  geom_text(aes(label = country),
    position = position_dodge(width = 0.9), angle = 90, hjust = 1
  )

CodePudding user response:

Well, you could generate bar, text geoms separately and sum them later. Not very pretty, but works:

reorder_group <- function(df, fn) {
    df %>% 
        group_by(group) %>% 
        group_map(function(x, ...) {
            x %>% 
                mutate(country = reorder(country, score, decreasing = TRUE)) %>% 
                fn()
        }, .keep = TRUE)
}
g_col <- 
    data_scores %>% 
    reorder_group(function(df) geom_col(data = df, position = position_dodge()))
g_text <- 
    data_scores %>% 
    reorder_group(function(df) {
        geom_text(
            aes(label = country), 
            data = df, 
            position = position_dodge(width = 0.9), angle = 90, hjust = 1
        )
    })
g_main <- ggplot(data_scores, aes(x = group, y = score, fill = country))
reduce(c(g_col, g_text), ` `, .init = g_main)

plot result

  • Related