Search code examples
rggplot2categories

Fix the middle category in ggplot


I have a bar graph with 3 groups, each with 3 fill categories. I want one category to always be the middle category on the bar graph.

dat <- data.frame(fill=c("Category A","Category B","Middle",
                      "Category A","Category C","Middle",
                      "Category B","Category C","Middle"),
                  x=c("A","A","A",
                      "B","B","B",
                      "C","C","C"),
                  y=c(.333,.333,.333,
                      .4,.4,.2,
                      .2,.4,.4))

ggplot(dat,aes(x=x,y=y,fill=fill))+
  geom_col()

I don't think this is possible with traditional factor ordering because I can't set the Middle category to be the middle of each combination:

dat$fill <- factor(dat$fill,levels=c("Category A","Middle","Category B","Category C"))

If I do this, Middle will fall between Category A and Category B, but not between Category B and Category C.

enter image description here

Is it possible to set one category to always be the middle category?


Solution

  • Here is a way to achieve this using dplyr:

    library(dplyr)
    
    dat %>% group_by(x) %>% 
      mutate(n = ifelse(fill=="Middle", 2, c(1,3)))%>%
      ggplot(.,aes(x=x,y=y,fill=fill, group=n))+
      geom_col()
    

    enter image description here

    The mutate will add the column you can use for your group ranked by order. The ifesle will give the row with "Middle" 2, and when false 1 and 3. In this case it is convenient as each group have 3 categories. This part will need change if you have more categories per group.

    dat %>% group_by(x) %>% 
      mutate(n = ifelse(fill=="Middle", 2, c(1,3)))
    
    # A tibble: 9 x 4
    # Groups:   x [3]
      fill       x         y     n
      <chr>      <chr> <dbl> <dbl>
    1 Category A A     0.333     1
    2 Category B A     0.333     3
    3 Middle     A     0.333     2
    4 Category A B     0.4       1
    5 Category C B     0.4       3
    6 Middle     B     0.2       2
    7 Category B C     0.2       1
    8 Category C C     0.4       3
    9 Middle     C     0.4       2
    

    Update: Middle with different categories per group:

    Here is an option to have a specific category always in the middle of the stacked histograms:

    dat %>% group_by(x) %>% 
      mutate(n = ifelse(fill=="Middle", round(median(1:n())), c(1:(round(median(1:n()))-1),(round(median(1:n()))+1):n()))) %>%
      ggplot(.,aes(x=x,y=y,fill=fill, group=n))+
      geom_col(position="fill")+
      scale_fill_manual(values = c(colorRampPalette(c("grey10", "grey80"))(14), "red"))
    

    enter image description here

    Data


    library(dplyr)
    library(ggplot2)
    library(RColorBrewer)
    
    set.seed(14)
    
    dat = data.frame(
      fill = c(
        c(sample(paste0("Cat ", letters[1:20]), 20, replace = TRUE), "Middle"),
        c(sample(paste0("Cat ", letters[1:20]), 10, replace = TRUE), "Middle"),
        c(sample(paste0("Cat ", letters[1:20]), 2, replace = TRUE), "Middle")),
      x= c(rep("A", 21), rep("B", 11), rep("C", 3)),
      y= runif(35))