nathkpa
nathkpa

Reputation: 121

R ggplot2 customize legend with multiple geom_bar in one dataframe

My question is close to this Legends for multiple fills in ggplot and yet not the same. Let me explain.

I have a dataframe that looks like this:

df <- data.frame(
  'sf'=c('a', 'a', 'a', 'b', 'b', 'b', 'c', 'c', 'c'), 
  'totalsf'=c(28, NA, NA, 32, NA, NA, 40, NA, NA),
  'sl' = c('A', 'A', 'A', 'B', 'B', 'B', 'C', 'C', 'C'),
  'totalsl' = c(35, NA, NA, 40, NA, NA, 25, NA, NA),
  'min' = c('aa', 'bb', 'cc', 'aa', 'bb','cc', 'aa', 'bb', 'cc'),
  'totalmin' = c(30, 4, 0, 8, 0, 25, 8, 9, 16),
  'id' = c(1,2,3,4,5,6,7,8,9))

I basically want to plot totalsf with sf as a fill, totalsl with sl as a fill and totalmin with min as a fill, all with geom_bar, as follows:

g <- ggplot(df  %>% mutate(id = id), aes(group=id))
g <- g + geom_bar(aes(x=0, y=totalsf, fill=sf), width=.05, stat="identity")
g <- g + geom_bar(aes(x= 0.05, y=totalsl, fill=sl), width=.05, stat="identity")
g <- g + geom_bar(aes(x=0.1, y=totalmin, fill=min), width=.05, stat="identity")
g <- g + theme_light()
g <- g + theme(axis.title.x=element_blank(),
               axis.text.x=element_blank(),
               axis.ticks.x=element_blank(),
               legend.position="bottom")
g <- g + labs(x = "Variable", y = "y", fill = "x")
g <- g + scale_y_continuous(breaks=seq(0, 100, by=10))
print(g)

This yields the following graph:

enter image description here

Mind you, this is what I want (and I also know how to change the colors but let's not overload the code). Here is the core of my problem: the legend is hideous, I would like ideally to have the three variables having their own legend and not have it in alphabetical order like it is now. Would someone know how to customize the legend so that each variable has it own legend? (basically "sf" as a title with a, b, c colors, "sl" as a title with A, B, C colors and "min" as a title with aa, bb, cc colors).

Thank you in advance for your answers!

Best,

Nathalie

Upvotes: 2

Views: 855

Answers (1)

teunbrand
teunbrand

Reputation: 37933

I know of no easy trick, but he ggnewscale package allows you to map multiple variables to several scales at once, which will result in the legends having multiple titles. You would have to specify a scale and then specify that you want to override the scale. An example based on your code:

library(ggnewscale)

g <- ggplot(df %>% mutate(id = id), aes(group = id)) +
  # Note geom_col is shorthand for geom_bar(stat = "identity")
  geom_col(aes(x = 0, y = totalsf, fill = sf), width = 0.05) +
  # Scale has to be specified, not giving colour values will
  # result in all scales being red blue and green.
  # guide_legend(order = 1) ensures that the legends appear in your
  # order instead of alphanumerically
  scale_fill_manual(values = c("red", "green", "blue"), 
                    guide = guide_legend(order = 1)) +
  new_scale_fill() +
  geom_col(aes(x = 0.05, y = totalsl, fill = sl), width = 0.05) +
  scale_fill_manual(values = c("yellow", "magenta", "cyan"), 
                    guide = guide_legend(order = 2)) +
  new_scale_fill() +
  geom_col(aes(x = 0.1, y = totalmin, fill = min), width = 0.05) +
  scale_fill_manual(values = c("black", "grey50", "white"), name = "min") +
  theme_light() +
  theme(axis.title.x=element_blank(),
        axis.text.x=element_blank(),
        axis.ticks.x=element_blank(),
        legend.position="bottom") +
  labs(x = "Variable", y = "y", fill = "x") +
  scale_y_continuous(breaks=seq(0, 100, by=10))

enter image description here

Upvotes: 3

Related Questions