Ordering a ggplot2 barplot by multiple variables

Sometimes you would like to order your barplots by multiple variables. For example, you would like to sort by a categorical variable, and then within each categorical variable sort by a continuous variable. Let’s take a look at an example:

library(dplyr)

Attaching package: 'dplyr'
The following objects are masked from 'package:stats':

    filter, lag
The following objects are masked from 'package:base':

    intersect, setdiff, setequal, union
library(tidyr)
library(ggplot2)
library(forcats)
starwars %>% 
  group_by(homeworld) %>% 
  count() %>% 
  arrange(-n)
# A tibble: 49 × 2
# Groups:   homeworld [49]
   homeworld     n
   <chr>     <int>
 1 Naboo        11
 2 Tatooine     10
 3 <NA>         10
 4 Alderaan      3
 5 Coruscant     3
 6 Kamino        3
 7 Corellia      2
 8 Kashyyyk      2
 9 Mirial        2
10 Ryloth        2
# ℹ 39 more rows
starwars %>% 
  filter(is.na(homeworld))
# A tibble: 10 × 14
   name     height  mass hair_color skin_color eye_color birth_year sex   gender
   <chr>     <int> <dbl> <chr>      <chr>      <chr>          <dbl> <chr> <chr> 
 1 Yoda         66    17 white      green      brown            896 male  mascu…
 2 IG-88       200   140 none       metal      red               15 none  mascu…
 3 Arvel C…     NA    NA brown      fair       brown             NA male  mascu…
 4 Qui-Gon…    193    89 brown      fair       blue              92 male  mascu…
 5 R4-P17       96    NA none       silver, r… red, blue         NA none  femin…
 6 Finn         NA    NA black      dark       dark              NA male  mascu…
 7 Rey          NA    NA brown      light      hazel             NA fema… femin…
 8 Poe Dam…     NA    NA brown      light      brown             NA male  mascu…
 9 BB8          NA    NA none       none       black             NA none  mascu…
10 Captain…     NA    NA unknown    unknown    unknown           NA <NA>  <NA>  
# ℹ 5 more variables: homeworld <chr>, species <chr>, films <list>,
#   vehicles <list>, starships <list>
starwars <- starwars %>% 
  mutate(homeworld = replace_na(homeworld, "Unknown")) %>%
  filter(homeworld %in% c("Naboo", "Tatooine", "Unknown")) %>%
  drop_na(height) %>% 
  mutate(name = factor(name)) %>% 
  mutate(name = fct_reorder(name, height)) %>% 
  mutate(name = fct_reorder(name, homeworld))

ggplot(starwars, mapping = aes(x = height, y = name, fill = homeworld)) +
  geom_bar(stat = "identity") +
  xlab("Character Height (in inches)") +
  ylab("Character") +
  scale_fill_manual(values = c("darkorange","purple","cyan4"),
                    guide = guide_legend(reverse = TRUE),
                    name = "Homeworld") +
  theme_minimal()