rggplot2refactoringfacet

Ordering bars in faceted ggplot2 bar chart, and omitting unused factor levels per group


I created this bar chart I am not happy with:

enter image description here

I do not want to have the relevant labels shown in the x-axis when an author (Me/Them/You) does not have any values.

For instance, the facet "Them" has no value with regards to the x-axis label "Raceway". This should be completely omitted.

How to achieve that?

This is my dataframe (df):

structure(list(author = c("Me", "Me", "Them", "Them", "Them", 
"Them", "Them", "You", "You"), display_name = c("Raceway", "Pulverized coal-fired boiler", 
"Hollow fiber membrane", "Permeation", "Residence time distribution", 
"Fluidization", "Rotary kiln", "Rotary kiln", "Raceway"), n = c(4, 
2, 4, 4, 3, 2, 2, 3, 3), sum_score = c(3.69998413, 1.4438111, 
2.5820569, 2.32903219, 1.90571574, 1.86841225, 1.42931327, 2.8, 
2.2)), row.names = c(NA, -9L), class = c("tbl_df", "tbl", "data.frame"
))

This is the code to factor the keyword (column display_name) by group (author), based on this:

df <- transform(df, category2 = factor(paste(author, display_name)))
df <- transform(df, category2 = reorder(category2, rank(sum_score)))

This is the ggplot code:

ggplot(df, aes(x = category2, y = sum_score, fill = author)) +
  geom_bar(stat = "identity") +
  facet_wrap(. ~ author, scales = "free_x", nrow = 3) +
  scale_x_discrete(labels = df$display_name, breaks = df$category2) +
  scale_y_continuous(limits = c(0, max(DF3$sum_score))) +
  scale_fill_manual("legend", values = c("You" = "#006699", "Me" = "orange", "Them" = "#720016")) +
  ylab("") +
  xlab("") +
  theme_minimal() + 
  theme(
    panel.grid.major = element_blank(),
    panel.grid.minor = element_blank(),
    legend.position = "none"
  ) +
  coord_flip()

Thanks for your help!


Solution

  • To get rid of the unused levels you could add scales="free_y" instead of "free_x". Additionally I would suggest to switch to facet_grid as it allows bars of equal width using space="free_y".

    Note: I switched x and y to get rid of coord_flip. Perhaps that was also the reason why you confused "free_x" and "free_y".

    library(ggplot2)
    
    ggplot(df, aes(y = category2, x = sum_score, fill = author)) +
      geom_bar(stat = "identity") +
      facet_grid(author ~ ., scales = "free_y", space = "free_y") +
      scale_y_discrete(labels = df$display_name, breaks = df$category2) +
      scale_x_continuous(limits = c(0, NA)) +
      scale_fill_manual("legend",
        values = c("You" = "#006699", "Me" = "orange", "Them" = "#720016")
      ) +
      ylab("") +
      xlab("") +
      theme_minimal() +
      theme(
        panel.grid.major = element_blank(),
        panel.grid.minor = element_blank(),
        legend.position = "none"
      )
    

    enter image description here

    And if you still want the facet labels on top you could use ggforce::facet_col in which case we have to fix the axis limits as you did in your original code:

    library(ggplot2)
    
    ggplot(df, aes(y = category2, x = sum_score, fill = author)) +
      geom_bar(stat = "identity") +
      ggforce::facet_col(author ~ .,
        scales = "free", space = "free"
      ) +
      scale_y_discrete(labels = df$display_name, breaks = df$category2) +
      scale_x_continuous(limits = c(0, max(df$sum_score))) +
      scale_fill_manual("legend",
        values = c("You" = "#006699", "Me" = "orange", "Them" = "#720016")
      ) +
      ylab("") +
      xlab("") +
      theme_minimal() +
      theme(
        panel.grid.major = element_blank(),
        panel.grid.minor = element_blank(),
        legend.position = "none"
      )
    

    enter image description here