12

I have a data.frame, something like the following:

set.seed(100)

df <- data.frame(year = rep(2011:2014, 3), 
                 class = rep(c("high", "middle", "low"), each = 4), 
                 age_group = rep(1:3, each = 4), 
                 value = sample(1:2, 12, rep = TRUE))

and I am looking to produce, by facet-ing (by the variable age_group) three plots which look similar to those produced by the following code:

library(ggplot2)
blue <- c("#bdc9e1", "#74a9cf", "#0570b0")
ggplot(df) + geom_bar(aes(x = year, y = value, 
                          fill = factor(class, levels = c("high", "middle", "low"))), 
                      stat = "identity") + 
  scale_fill_manual(values = c(blue)) +
  guides(fill = FALSE)

however, where each facet has a different colour scheme where all the colours are specified by myself.

I appear to want a more specific version of what is going on here: ggplot2: Change color for each facet in bar chart

So, using the data I have provided, I am looking to get three facet-ed plots, split by age_group where the fill is given in each plot by the level of class, and all colours (9 total) would be specified manually by myself.

Edit: For clarification, the facet that I would like to end up with is indeed provided by the following code:

ggplot(df) + geom_bar(aes(x = year, y = value, 
                          fill = factor(class, levels = c("high", "middle", "low"))), 
                      stat = "identity") + 
  scale_fill_manual(values = c(blue)) +
  guides(fill = FALSE) + 
  facet_wrap(~ age_group)

with the added level of control of colour subset by the class variable.

Community
  • 1
  • 1
Ross
  • 521
  • 1
  • 4
  • 16
  • This is for the most part a very nice and reproducible question (good work!), but I need a couple clarifications. (a) I'm surprised your example plot *doesn't* include the facet you want... is just adding `+ facet_wrap(~age_group)` what you had in mind? Maybe include that in your example. (b) What do you have in mind for a legend? A single legend with all of the age-group/class interactions? Or an individual legend for each subplot? – Gregor Thomas Jan 05 '17 at 19:13
  • I have added the final facet I would like into my question above. You are correct by your assumption that I would simply like to include `facet(~age_group)`. Your point about the legend is interesting, I had not thought of this. Some kind of legend where I could have three colours in blocks corresponding to the levels "high", "middle" and "low" would be nice however I imagine this is beyond ggplot's control. I'd welcome suggestions. – Ross Jan 05 '17 at 19:22
  • I have [this old question](http://stackoverflow.com/q/20129299/903061) about make "grid" legends in the `grid` system - you could probably adapt it. As for the colors, I would just add a column to your data that is a combination of class and age_group, then fill by that (using `paste` or `interaction` to create the new column). I don't have time to write up a nice answer now, but I'll try to find time later on if no one else does it first. – Gregor Thomas Jan 05 '17 at 22:18
  • Is it possible that your example is not quite ideal? For each age_group there is only one value of class (for instance, age_group 1 has only class "high"). This means that you will get different colours in each facet without doing anything special. If this is not what you had in mind, then it would be useful to create a more realistic reproducible example. (To compensate all the nagging: As Gregor already mentioned, you did create a working reproducible example, which is a very good thing!) – Stibu Jan 06 '17 at 08:16
  • I believe the data generated by Mark Peterson in his answer is more representative of the data I am working with, where as mentioned you get many classes across age groups. Thankyou for your kind commentary. – Ross Jan 06 '17 at 14:38

1 Answers1

17

I'm not entirely sure why you want to do this, so it is a little hard to know whether or not what I came up with addresses your actual use case.

First, I generated a different data set that actually has each class in each age_group:

set.seed(100)

df <- data.frame(year = rep(2011:2014, 3), 
                 class = rep(c("high", "middle", "low"), each = 12), 
                 age_group = rep(1:3, each = 4), 
                 value = sample(1:2, 36, rep = TRUE))

If you are looking for a similar dark-to-light gradient within each age_group you can accomplish this directly using alpha and not worry about adding extra data columns:

ggplot(df) +
  geom_bar(aes(x = year, y = value, 
               fill = factor(age_group)
               , alpha = class ), 
           stat = "identity") + 
  facet_wrap(~age_group) +
  scale_alpha_discrete(range = c(0.4,1)) +
  scale_fill_brewer(palette = "Set1"
                    , name = "age_group")

Here, I set the range of the alpha to give reasonably visible colors, and just chose a default palette from RColorBrewer to show the idea. This gives:

enter image description here

It also gives a relatively usable legend as a starting point, though you could modify it further (here is a similar legend answer I gave to a different question: https://stackoverflow.com/a/39046977/2966222 )

Alternatively, if you really, really want to specify the colors yourself, you can add a column to the data, and base the color off of that:

df$forColor <-
  factor(paste0(df$class, " (", df$age_group , ")")
         , levels = paste0(rep(c("high", "middle", "low"), times = 3)
                           , " ("
                           , rep(1:3, each = 3)
                           , ")") )

Then, use that as your fill. Note here that I am using the RColorBrewer brewer.pal to pick colors. I find that the first color is too light to show up for bars like this, so I excluded it.

ggplot(df) +
  geom_bar(aes(x = year, y = value, 
               fill = forColor), 
           stat = "identity") + 
  scale_fill_manual(values = c(brewer.pal(4, "Blues")[-1]
                               , brewer.pal(4, "Reds")[-1]
                               , brewer.pal(4, "Purples")[-1]
                               )
                    , name = "Class (age_group)") +
  facet_wrap(~age_group)

gives:

enter image description here

The legend is rather busy, but could be modified similar to the other answer I linked to. This would then allow you to set whatever 9 (or more, for different use cases) colors you wanted.

Community
  • 1
  • 1
Mark Peterson
  • 9,370
  • 2
  • 25
  • 48