talat
talat

Reputation: 70266

Different fill orders in geom_bar per discrete x-value

I have this ggplot:

pic

What I want to do is change the order of cat2 in each distinct cat1-category in decreasing order of share:

I know I can control the order by defining factor levels, but since I need a different order in different cat1-categories, I'm not sure how to do this.

Can someone enlighten me?


df <- structure(list(cat2 = c("i", "k", "j", "h", "g", "j", "h", "k", 
"f", "g", "j", "h", "k", "f", "g", "i", "k", "j", "h", "f"), 
    cat1 = c("Z", "Z", "Z", "Z", "Z", "Y", "Y", "Y", "Y", "Y", 
    "X", "X", "X", "X", "X", "W", "W", "W", "W", "W"), share = c(0.254318086442458, 
    0.217254586570476, 0.13303361614456, 0.107317457057957, 0.0796390207719751, 
    0.255762968963295, 0.198921069216629, 0.177295815678624, 
    0.100770340584133, 0.0971042138291394, 0.222007778896866, 
    0.177174367182501, 0.156891912894117, 0.097677432116308, 
    0.0975337223454565, 0.503295513011454, 0.154491050393999, 
    0.114284166914891, 0.0802892036069214, 0.0549053589320047
    ), pos = c(0.477447245936265, 0.108627293285238, 0.283771394642756, 
    0.658265017686473, 0.751743256601439, 0.305177300160272, 
    0.532519319250233, 0.088647907839312, 0.779469237979754, 
    0.680531960773118, 0.26789580234255, 0.467486875382234, 0.0784459564470585, 
    0.702446497377094, 0.604840920146212, 0.520422973814617, 
    0.0772455251969995, 0.211633133851444, 0.812215332123805, 
    0.879812613393268)), .Names = c("cat2", "cat1", "share", 
"pos"), class = "data.frame", row.names = c(NA, -20L))

library(ggplot2)
ggplot(df, aes(cat1, share)) + 
  geom_bar(stat = "identity", aes(fill = cat2)) + 
  geom_text(aes(label = cat2, y = pos)) +
  coord_flip()

Note: my current thinking is that perhaps it could be done using a new category with the levels set as 1st, 2nd, 3rd, 4th, 5th but if I use that for filling, different cat2-values for example i for Z and j for Y would have the same fill-color because they are 1st in the respective cat1-category which I wouldn't want.

Upvotes: 4

Views: 919

Answers (1)

eipi10
eipi10

Reputation: 93811

You can do this by creating a new factor column, let's call it cat3, based on both cat1 and cat2. Set the factor ordering of this column based on cat1 and share. Then use this new cat3 factor column as a group aesthetic to set the stacking order for the bars.

In the code below, arrange sets the ordering. We sort the data frame by share separately within each level of cat1. Now the data frame is in the order we want. Then we create cat3 by pasting cat1 and cat2 together and set the order of the levels to be the current sorted order of the data frame. Finally, within ggplot we use cat3 as a group aesthetic to set the stacking order in geom_bar.

Another addition is the use of position_stack(vjust=0.5) to set the label locations, rather than a y aesthetic.

library(tidyverse)

df %>% 
  arrange(cat1, share) %>% 
  mutate(cat3 = factor(paste(cat1, cat2), levels=paste(cat1, cat2))) %>%
  ggplot(aes(cat1, share, group=cat3)) + 
    geom_bar(stat = "identity", aes(fill = cat2)) + 
    geom_text(aes(label = cat2), position=position_stack(vjust=0.5)) +
    coord_flip()

enter image description here

Upvotes: 5

Related Questions