GGplot Color Outline

Viewed 471
data(mtcars)
library(ggplot2)
ggplot(mtcars, aes(x = reorder(row.names(mtcars), mpg), y = mpg, fill = factor(cyl))) +
        geom_bar(stat = "identity")

This will ggplot the bars with solid fills but what if I wish to use the same fill colors as outlines for some measures but solid fills for others. For example if 'am' equals to 1 it is solid fill but if 'am' equals to 0 than it is just an outline fill like this sample:

enter image description here

3 Answers

One option to remove the fill based on a logical condition is to change those values to NA.

library(tidyverse)

d <- head(mtcars) %>%
  rownames_to_column() %>%
  # make a new variable for fill
  # note: don't use ifelse on a factor!
  mutate(cyl_fill = ifelse(am == 0, NA, cyl),
         # now make them factors 
         # (you can do this inside ggplot, but that is messy)
         cyl = factor(cyl),
         cyl_fill = factor(cyl_fill, levels = levels(cyl)))

# plot 
p <- ggplot(d) +
  aes(x = rowname, 
      y = mpg, 
      color = cyl,
      fill = cyl_fill
  ) +
  geom_bar(stat = "identity") +

  theme(axis.text.x = element_text(angle = 90))

# change the fill color of NA values
p +   scale_fill_discrete(drop=FALSE, na.value="white")  

enter image description here

If you want NA fill values to be empty and omitted from the legend:

# omit the fill color of NA values
# note: drop=FALSE is still needed to keep the fill and (outline) color values the same
p +   scale_fill_discrete(drop=FALSE, na.translate = F)  

enter image description here

You can change the color of the outline in the same way (e.g. cyl_color = ifelse(am != 0, NA, Cyl)), but if you want to specify a color like white or black, it will (should) appear in the legend. You can try to hack your way around these wise defaults by plotting non-aesthetic layers behind your main layers, but it usually gets ugly:

head(mtcars) %>%
  rownames_to_column() %>%
  mutate(cyl_fill = ifelse(am == 0, NA, cyl),
         cyl_color = ifelse(am != 0, NA, cyl),
         cyl = factor(cyl),
         cyl_fill = factor(cyl_fill, levels = levels(cyl)),
         cyl_color = factor(cyl_color, levels = levels(cyl))) %>% 
  ggplot() +
  aes(x = rowname, 
      y = mpg, 
      color = cyl_color,
      fill = cyl_fill
  ) +
  geom_bar(stat = "identity", color = "black") + # NON-AES LAYER FIRST
  geom_bar(stat = "identity") + # Covers up the black except where omitted
  theme(axis.text.x = element_text(angle = 90))+
  scale_fill_discrete(drop=FALSE, na.translate = F) + 
  scale_color_discrete(drop=FALSE, na.translate = F) 

enter image description here

You could assign the desired colors to each level of the fill and color variables. For example:

library(tidyverse)

mtcars %>% 
  rownames_to_column() %>% 
  arrange(mpg) %>% 
  mutate(rowname=factor(rowname, levels=rowname)) %>% 
  ggplot(aes(x = rowname, y = mpg, fill = factor(am), colour=factor(cyl))) +
    geom_col(size=1) +
    scale_fill_manual(values=c("0"="white", "1"="red")) +
    scale_color_manual(values=c("4"="blue", "6"="orange", "8"="white")) +
    theme_classic() +
    theme(axis.text.x=element_text(angle=-90, vjust=0.5, hjust=0)) 

enter image description here

May be, we can do

library(dplyr)
library(ggplot2)
mtcars %>% 
  mutate(new = case_when(am == 1 ~ factor(cyl)),
    new1 = case_when(am !=1 ~ factor(cyl))) %>% 
 ggplot(aes(x = reorder(row.names(mtcars), mpg), y = mpg, 
          fill = new, color = new1)) + 
    geom_bar(stat = 'identity') + 
    scale_fill_discrete(na.value= NA) + # similar to Devin Judge-Lord post
    theme_classic() +       
    theme(axis.text.x=element_text(angle=-90, vjust=0.5, hjust=0))
Related