Irio Li
Irio Li

Reputation: 13

ggplot2 geom_bar color bars by one column and outline by another column

My dataframe has the following columns:

  1. Proportion, which is the y axis.
  2. Category, which is the x axis and have 7 categories
  3. Group1, which is binary
  4. Group2, which has 4 groups, multiple categories could belong to a same group.

I wish to color the bars by Group2 (so fill = Group2), but I couldn't figure out how to make bars outlined or not based on Group1 as well. My current code is

  ggplot(data=df, aes(x=Category,y=Proportion, fill = Group1, group = Group2)) + 
    geom_bar(stat = "identity", position = position_dodge()) + 
    xlab("") + 
    ylab("Proportion") + 
    theme_classic() + 
    theme(legend.position="top") + 
    theme(legend.title = element_blank())

I am new to R visualization, so it would be really nice if someone could tell me how to outline the bars based on the grouping.

Here's how the dataframe should look like:

> df
   Category Proportion Group1 Group2
1         1          45      a      A
2         2          40      a      A
3         3          49      a      A
4         4          47      a      A
5         5          43      a      A
6         1          32      a      B
7         2          37      a      B
8         3          45      a      B
9         4          47      a      B
10        5          50      a      B
11        6          49      b      A
12        7          34      b      A
13        8          31      b      A
14        6          36      b      B
15        7          46      b      B
16        8          39      b      B
17        9          42      c      A
18        9          31      c      B
19       10          31      d      A
20       10          44      d      B

Here's the current output:

output

I want the bars on left of each group to be outlined (white filling and the edge be the same color as the group)

Upvotes: 1

Views: 1441

Answers (1)

Ian Campbell
Ian Campbell

Reputation: 24810

Is this what you're looking for?

Set fill to Group2 then color to Group1. You can override the legend if you want a particular color or fill. I added factor around Category to get the x axis to make more sense, although I suspect your actual Category is already a factor.

Note your data column is Proportions, although your code is Proportion.

ggplot(data=df, aes(x=factor(Category),y=Proportions, fill = Group2, color = Group1)) + 
    geom_bar(stat = "identity", position = position_dodge()) + 
    scale_fill_manual(values =c(A = "white", B = "gray")) +
    xlab("") + ylab("Proportion") + 
    theme_classic() + 
    theme(legend.position="top") + 
    theme(legend.title = element_blank()) +
    guides(fill = guide_legend(override.aes = 
                               list(color = "gray")),
           color = guide_legend(override.aes = 
                               list(fill = "gray")))

enter image description here

If you're dead set on the fill matching the color, it gets more complicated, but you can use interaction(Group2,Group1) as the fill. Then you have to manually determine the colors for the interactions.

ggplot(df, aes(x=factor(Category),y=Proportions, fill = interaction(Group2,Group1), color = Group1)) + 
    geom_bar(stat = "identity", position = position_dodge()) + 
    scale_fill_manual(values = setNames(c(rep("white",4),"cornflowerblue","forestgreen","firebrick3","goldenrod"),
                                        sort(unique(as.character(interaction(df$Group2,df$Group1))))),
                      breaks = c("A.a","B.a"),
                      labels = c("A","B")) +
    scale_color_manual(values = setNames(c("cornflowerblue","forestgreen","firebrick3","goldenrod"),
                                         sort(unique(as.character(df$Group1))))) +
    labs(x = "", y = "Proportion") + 
    theme_classic() + 
    theme(legend.position="top") + 
    theme(legend.title = element_blank()) +
    guides(fill = guide_legend(override.aes = 
                               list(color = "gray",
                                    fill = c("white","darkgray"))),
           color = guide_legend(override.aes = 
                               list(fill = c("white"))))

enter image description here

To understand what's going on here, consider this:

interaction(df$Group2,df$Group1)
[1] A.a A.a A.a A.a A.a B.a B.a B.a B.a B.a A.b A.b A.b B.b B.b B.b A.c B.c A.d B.d
Levels: A.a B.a A.b B.b A.c B.c A.d B.d

If we sort and unique this, we get the following:

sort(unique(as.character(interaction(df$Group2,df$Group1))))
[1] "A.a" "A.b" "A.c" "A.d" "B.a" "B.b" "B.c" "B.d"

We want the first 4 to be white, so we can use rep, and then manually define the others.

c(rep("white",4),"cornflowerblue","forestgreen","firebrick3","goldenrod")
[1] "white"          "white"          "white"          "white"          "cornflowerblue" "forestgreen"    "firebrick3"     "goldenrod"    

From here we can use setNames to apply the fills to the values:

setNames(c(rep("white",4),"cornflowerblue","forestgreen","firebrick3","goldenrod"),
                                         sort(unique(as.character(interaction(df$Group2,df$Group1)))))
             A.a              A.b              A.c              A.d              B.a              B.b              B.c              B.d 
         "white"          "white"          "white"          "white" "cornflowerblue"    "forestgreen"     "firebrick3"      "goldenrod" 

Repeat the process for the colors.

We don't necessarily want to see all of the interaction()'s in the legend. So we can limit them with the breaks = argument. We can rename A.a and B.a to just A and B with labels =.

Upvotes: 1

Related Questions