Search code examples
rsortingggplot2geom-bar

ggplot dodged bar chart: arrange bars by y-value per group


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!


Solution

  • 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
      )