invictus
invictus

Reputation: 1971

ordered bar chart with multiple groupings in ggplot2

I have this ungrouped data (dput()'ed below):

# A tibble: 12 x 3
   category1 category2 value
   <chr>     <chr>     <dbl>
 1 A         x         0.200
 2 A         y         0.32 
 3 A         z         0.46 
 4 B         x         0.52 
 5 B         y         0.420
 6 B         z         0.28 
 7 C         x         0.3  
 8 C         y         0.26 
 9 C         z         0.440
10 D         x         0.34 
11 D         y         0.440
12 D         z         0.58 

and I plot it:

data %>% 
  ggplot(aes(x = category2, y = value, fill = as.factor(category1))) + 
  geom_col(position = "dodge") + 
  coord_flip() 

enter image description here

and now I want to order the bars within category2, descending in category1.

From this previous post I understand you have to arrange the data and create/order the factor. But it does not change anything, and I can't figure out why:

data %>% 
  arrange(desc(category1), value) %>%
  mutate(category2 = factor(category2, levels = unique(category2), ordered = TRUE)) %>%
  ggplot(aes(x = category2, y = value, fill = as.factor(category1))) + 
  geom_col(position = "dodge") + 
  coord_flip() 

I also tried re-ordering the factor per this post, but it didn't do anything:

data %>% 
  mutate(category2 = factor(category2)) %>% 
  mutate(category2 = category2 %>% forcats::fct_reorder(value, sum)) %>% 
  ggplot(aes(x = category2, y = value, fill = as.factor(category1))) + 
  geom_col(position = "dodge") + 
  coord_flip()

enter image description here

edit: forgot to the add the data:

structure(list(category1 = c("A", "A", "A", "B", "B", "B", "C", 
"C", "C", "D", "D", "D"), category2 = c("x", "y", "z", "x", "y", 
"z", "x", "y", "z", "x", "y", "z"), value = c(0.2, 0.32, 0.46, 
0.52, 0.42, 0.28, 0.3, 0.26, 0.44, 0.34, 0.44, 0.58)), row.names = c(NA, 
-12L), class = c("tbl_df", "tbl", "data.frame"))

Upvotes: 2

Views: 703

Answers (2)

Jon Spring
Jon Spring

Reputation: 66490

tidytext::reorder_within is built for this use case.

library(tidytext)
data %>% 
  mutate(category1b = reorder_within(category1, value, within = category2)) %>%
  ggplot(aes(x = value, y = category2, group = category1b, fill = as.factor(category1))) + 
  geom_col(position = "dodge") 

enter image description here

Upvotes: 2

stefan
stefan

Reputation: 124148

If I got you right you could achieve your desired result by making use of an interaction variable and the group aesthetic like so:

  1. Arrange your data by category2 and value
  2. Add a new category variable as the interaction of category2 and category1
  3. Set the order of the new category variable via forecast::fct_inorder
  4. Map the the new category variable on the group aesthetic
library(ggplot2)
library(dplyr)

data %>%
  arrange(category2, value) %>%
  mutate(
    category3 = interaction(category2, category1),
    category3 = forcats::fct_inorder(category3)
  ) %>%
  ggplot(aes(x = category2, y = value, fill = category1, group = category3)) +
  geom_col(position = "dodge") +
  coord_flip()

DATA

data <- structure(list(category1 = c(
  "A", "A", "A", "B", "B", "B", "C",
  "C", "C", "D", "D", "D"
), category2 = c(
  "x", "y", "z", "x", "y",
  "z", "x", "y", "z", "x", "y", "z"
), value = c(
  0.2, 0.32, 0.46,
  0.52, 0.42, 0.28, 0.3, 0.26, 0.44, 0.34, 0.44, 0.58
)), class = "data.frame", row.names = c(
  "1",
  "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12"
))

Upvotes: 3

Related Questions