rggplot2geom-bar

How to order fill variable in bar plot ascending for each x factor level?


I make a bar plot with two factors, one lays on the x axis and the other determines the fill color of the bars. I want to be the bars ascending from left to right within each x factor level. Further, I want the x levels to be ordered ascending, too, based on the smallest y group of each x factor level. I found a similiar question and tried this:

library(ggplot2)
library(tidyverse)

# Finding the right x levels order.
manufacturer_order <- mpg %>%
  dplyr::group_by(manufacturer, class) %>%
  dplyr::summarise(num= dplyr::n()) %>%
  dplyr::group_by(manufacturer) %>%
  dplyr::arrange(num, .by_group = TRUE) %>%
  dplyr::mutate(dupl= duplicated(manufacturer)) %>%
  dplyr::filter(!dupl) %>%
  dplyr::arrange(num) %>%
  .$manufacturer
# Finding the right y order in each x level.
mpg_ordered <- mpg %>%
  dplyr::mutate(manufacturer= factor(manufacturer, levels= manufacturer_order)) %>%
  dplyr::arrange(manufacturer) %>%
  dplyr::group_by(manufacturer, class) %>%
  dplyr::summarise(num= dplyr::n()) %>%
  dplyr::group_by(manufacturer) %>%
  dplyr::arrange(num, .by_group = TRUE)
# Plot.
mpg_ordered %>%
  ggplot(., aes(x= manufacturer, y= num, fill= class)) +
  geom_bar(stat= "identity", position = "dodge", color='black')

Plot

This is not what I want. The manufacturer levels are sorted as expected but not the class levels within each manufacturer level. For example, within first manufacturer level nissan the order should be compact, suv and midsize. Of course I can change the order of the fill color variable class, but the problem is that the order is not the same in all the manufacturer factor levels. Basically, all bars should have the same order as the rows in my data mpg_ordered:

> mpg_ordered
# A tibble: 32 x 3
# Groups:   manufacturer [15]
   manufacturer class        num
   <fct>        <chr>      <int>
 1 nissan       compact        2
 2 nissan       suv            4
 3 nissan       midsize        7
 4 audi         midsize        3
 5 audi         compact       15
 6 lincoln      suv            3
 7 land rover   suv            4
 8 mercury      suv            4
 9 subaru       compact        4
10 subaru       subcompact     4
# i 22 more rows
# i Use `print(n = ...)` to see more rows

Thus the plot I expect has an ordering similiar to the question linked above (this question):

Like this

(but the answer provided there seems not to work)


Solution

  • One option to achieve your desired result would be to create a helper column as the interaction of the x and fill variable which could then be mapped on the group aes:

    library(tidyverse)
    
    mpg |>
      count(manufacturer, class, name = "num") |>
      mutate(manufacturer = reorder(manufacturer, num, FUN = min)) |>
      # Create a helper column to set the order
      arrange(manufacturer, num) |>
      mutate(
        class_ordered = interaction(manufacturer, class),
        class_ordered = fct_inorder(class_ordered)
      ) |>
      ggplot(aes(
        x = manufacturer, y = num, fill = class,
        group = class_ordered
      )) +
      geom_col(position = "dodge", color = "black")
    
    

    enter image description here