0

I'd like to order the bars in a grouped bar chart.

This is my tibble.

# A tibble: 13 x 3                
   number    name                   prop     
       <dbl> <chr>                  <int>    
           1 Dog                     0.0664  
           1 Cow                     0.0628  
           1 Pig                     0.0166  
           1 Garden                  0.0163  
           2 Moose                   0.0619  
           2 Cliff                   0.0517  
           2 Hike                    0.0214  
           2 Dog                     0.0147  
           2 Cow                     0.0141  
           3 Pig                     0.0615  
           3 Garden                  0.0245  
           3 Moose                   0.0135  
           3 Cow                     0.0132  

I'd like to make a grouped bar chart where the groups are sorted from greatest to least for values of prop. I want the groups to be sorted from greatest to least, so the x-axis should read from left to right: 1, 2, 3 and then, within each group, I want it to be greatest to least.

The bars for the number 1 should be Dog then Cow then Pig then Garden

This is what I tried.

library(tidyverse)
library(ggplot2)

tib.ready <- tib %>% 
  mutate(name = fct_reorder(name, prop, sum)) %>% 
  group_by(number) %>% 
  mutate(name = fct_reorder(name, prop, sum)) %>% 
  ungroup() %>% 
  mutate(number = fct_reorder(factor(number), prop, sum)) 

I tried the solution that Matias Andina linked to.

tib_ready <- with(tib_ready, 
       tib_ready[order(name, -as.numeric(prop)), ])

tib_ready %>%
ggplot(aes(number, prop, fill = name, label = name)) +
      geom_col(position = "dodge") +
      coord_flip()

Cauder
  • 2,157
  • 4
  • 30
  • 69

2 Answers2

1

Since you already ordered all the rows by the appropriate groups you could add another column that represents the order within the groups and use the group aesthetic argument. That should order them as desired within the number group.

tib.ready <- tib %>% 
  mutate(name = fct_reorder(name, prop, sum)) %>% 
  group_by(number) %>% 
  mutate(name = fct_reorder(name, prop, sum)) %>% 
  ungroup() %>% 
  mutate(number = fct_reorder(factor(number), prop, sum))

ggplot(tib.ready, aes(x = number, y = prop, fill = name, group = rank, label = name)) +
  geom_col(position = "dodge")

edit: Updated to just use the rank variable

EJJ
  • 1,474
  • 10
  • 17
  • This worked, can you please explain why this works? – Cauder May 15 '20 at 12:51
  • the `group` aes overrides existing default ordering in your input dataframe, using the new column you're manually setting the ordering – EJJ May 15 '20 at 13:00
  • actually i realized you already have a rank variable... you can use that instead of the new `grp_order` variable, see edit – EJJ May 15 '20 at 13:01
  • Great, now how do I get the legend to have the same order as the bars? – Cauder May 15 '20 at 13:23
  • the order of the legend also uses the default ordering in the data.frame, so you can modify the order there, or you can set them by using `scale_fill_discrete` or `guides` – EJJ May 15 '20 at 13:29
1

Tidyverse solution:

library(tidyverse)
df %>% 
  mutate(prop = as.double(prop), rank = as.numeric(rank)) %>%
  ggplot(., aes(x = number, y = reorder(prop, rank), fill = name)) +
  geom_col(position = "dodge") +
  ylab("Prop") +
  coord_flip()

Data:

df <- structure(list(number = c(1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 2L, 
    3L, 3L, 3L, 3L), name = c("Dog", "Cow", "Pig", "Garden", "Moose", 
    "Cliff", "Hike", "Dog", "Cow", "Pig", "Garden", "Moose", "Cow"
    ), prop = c(0.0664, 0.0628, 0.0166, 0.0163, 0.0619, 0.0517, 0.0214, 
    0.0147, 0.0141, 0.0615, 0.0245, 0.0135, 0.0132), rank = c(1L, 
    2L, 3L, 4L, 1L, 2L, 3L, 4L, 5L, 1L, 2L, 3L, 4L)), row.names = c(NA, 
    -13L), class = "data.frame")
hello_friend
  • 5,682
  • 1
  • 11
  • 15