0

I want to have a bar plot that colors the bars red if the numbers are positive and green if the numbers are negative. I want this to be in the form of a ggplot "template" where I can change the underlying data and it still does what I want. Currently my code only works if all the numbers are positive or if there are positive and negative numbers in my data.

Version 1 (does not work). All bars should be green, however, since there are no positive numbers the fill = change < 0 does not work and it takes just the first color which is red in this case.

data1 <- data.frame(group = as.factor(c("all", "men", "women")),
                   change = as.numeric(c(-10000, -6000, -4000)))

ggplot(data1, 
       aes(x = group, 
           y = change, 
           fill = change < 0)) + 
  geom_bar(stat = "identity") + 
  scale_fill_manual(guide = FALSE,
                    values = c("red", "green"))

Version 2 (works as expected).

data2 <- data.frame(group = as.factor(c("all", "men", "women")),
                    change = as.numeric(c(2000, 6000, -4000)))

ggplot(data2, 
       aes(x = group, 
           y = change, 
           fill = change < 0)) + 
  geom_bar(stat = "identity") + 
  scale_fill_manual(guide = FALSE,
                    values = c("red", "green"))

Version 3 (works - by chance?). All bars are red but I guess as in Version 1 this is only the case because red comes first in the colors specified in scale_fill_manual?

data3 <- data.frame(group = as.factor(c("all", "men", "women")),
                    change = as.numeric(c(10000, 6000, 4000)))

ggplot(data3, 
       aes(x = group, 
           y = change, 
           fill = change < 0)) + 
  geom_bar(stat = "identity") + 
  scale_fill_manual(guide = FALSE,
                    values = c("red", "green"))

How can I specify the bar color to make it work as I want, regardless if I use data1, data2 or data3 as an input?

r-newbie
  • 149
  • 7

2 Answers2

5

One possible way is to define a color column based on an ifelse statement and then use scale_fill_identity to apply corresponding color:

library(dplyr)
library(ggplot2)
data2 %>% mutate(Color = ifelse(change <0, "red","green")) %>%
  ggplot(aes(x = group, y = change, fill = Color))+
  geom_col()+
  scale_fill_identity(guide = FALSE)

enter image description here

dc37
  • 15,840
  • 4
  • 15
  • 32
  • nice. Would you mind considering to add / transfer this to the main questoin, to which this here is a duplicate?https://stackoverflow.com/questions/11838278/plot-with-conditional-colors-based-on-values-in-r/11838396#11838396 – tjebo Jan 30 '20 at 16:00
  • I would like to but on the example of the question you are referring, the color code does not make sense. There is no way to get the green color based on the example – dc37 Jan 30 '20 at 16:07
  • Although I don't understnad what you exactly mean - I've seen you have added your answer. +2 (here and there ;) – tjebo Jan 30 '20 at 16:14
  • 1
    ;) I did but I wa srefering to what the OP asked on the other question: "if the value of X is >0, then that value should be in green ... if the value of Y is >0, then that value should be in red". Based on the example, all values should be red if we apply the correct `ifelse` statement. I assumed it was a typo when writing the question. – dc37 Jan 30 '20 at 16:16
  • Funny I haven't even noticed that, as apparently has not @mnel in their great answer. Weird condition - I am not sure if this was really a typo. – tjebo Jan 30 '20 at 16:20
  • 1
    If not, the example is not adapted to this particular statement. As the question is from 2012, I believed the OP has been satisfy by @mnel's answer. – dc37 Jan 30 '20 at 16:22
1

Using the SO answer here and adapting it slightly for your case gives this way of using scale_fill_manual to get what you want:

library(ggplot2)

data1 <- data.frame(group = as.factor(c("all", "men", "women")),
                    change = as.numeric(c(-10000, -6000, -4000)))

data2 <- data.frame(group = as.factor(c("all", "men", "women")),
                    change = as.numeric(c(2000, 6000, -4000)))

data3 <- data.frame(group = as.factor(c("all", "men", "women")),
                    change = as.numeric(c(10000, 6000, 4000)))

myplot <- function(df){
    ggplot(df, 
           aes(x = group, 
               y = change, 
               fill = change < 0)) + 
        geom_bar(stat = "identity") + 
        scale_fill_manual(guide = FALSE,
                          name = 'change < 0', 
                          values = setNames(c('green', 'red'), c(T, F)))
}

myplot(data1)

myplot(data2)

myplot(data3)

Created on 2020-01-30 by the reprex package (v0.3.0)

meenaparam
  • 1,949
  • 2
  • 17
  • 29