3

Is it possible to manually indicate the number of factors shown per column in a ggplot legend? This is a reproducible example demonstrating what I am trying to do:

#loading libraries
library(ggplot2)

#Creating hypothetical dataframe
services<-data.frame(study=c(0),category=c(0),subcategory=c(0))
services<-services[-1,]
services[1:15,1]<-c(1:15)

services[1:4,2]<-c("Provisioning", "Regulating", "Suporting", "Cultural")
services[5:8,2]<-c("Provisioning", "Regulating", "Suporting", "Cultural")
services[9:10,2]<-c("Provisioning", "Regulating") 
services[11:15,2]<-c("Provisioning", "Regulating", "Suporting", "Cultural", "Provisioning")

services[1:4,3]<-c("Water supply", "Climate regulation", "Soil formation",      "Recreation")
services[5:8,3]<-c("Fisheries", "Water purification", "Habitat", "Recreation")
services[9:10,3]<-c("Water supply", "Flood regulation") 
services[11:15,3]<-c("Agriculture", "Water purification", "Soil formation", "Aesthetics", "Fisheries")

services

 #Manually re-ordering subcategory factors by larger categories and by number of occurences in the df (I am also looking for a better way to do this, as it must exist!!)
table(services$subcategory)
services$subcategory  <- factor(services$subcategory, 
                            levels=c(#Cultural services
                              "Recreation", "Aesthetics",
                              #Provisioning services
                              "Fisheries", "Water supply", "Agriculture",
                              #Regulating services
                              "Water purification", "Climate regulation","Flood regulation",
                              #Supporting services
                              "Soil formation", "Habitat"))


#Creating figure
ggplot(services, aes(category, fill=subcategory)) + geom_bar() + 
theme(legend.position="right") + 
guides(fill=guide_legend(ncol=4, title="Ecosystem Service Sub Categories"))+
 xlab("Ecosystem Service Type") +
 ylab("Number of times each ecosystem service was evaluated") 

The Figure produced by this code

What would like the figure legend to look like instead!

An Even more advanced way I'd love the figure to come out as

Although this suggestion, and this one get at this idea, they do not produce the result I am looking for. I've been pondering this problem for weeks and have spent hours looking around for a solution as I'm against having to manually make these adjustments in power point, illustrator etc. Unable to find a good answer, I'm now turning towards you folks! Thanks for any help!

Community
  • 1
  • 1
  • Have read [the documentation](http://docs.ggplot2.org/current/guide_legend.html) for `guide_legend()`? – Eric Fail Oct 28 '15 at 15:32
  • Yes! I've definitely looked through this. As much as guide_legend() allows you to adjust many aspects of a legend, such as the number of rows and columns, it doesn't seem as though in here you can adjust the number of factors that appear in each row or column. If I am mistaken, do explain! I'd be so happy to sort this out! – Dalal_EL_Hanna Oct 28 '15 at 22:32
  • Fair enough, then you might be able to tease out an answer to your problem form [this SO answer](http://stackoverflow.com/a/27804153/1305688) – Eric Fail Oct 29 '15 at 16:09

2 Answers2

1

Ha! While Hadley confirmed that it is not possible natively with ggplot2 (see link in comment) there is a very hack-y way to do it that is limited by maximum number of aesthetics that have associated legends in ggplot2.

What I have done is created four dummy data frames and plotted four different aesthetics features (size, color, alpha, linetype) outside of the print window. You then can override the legend aesthetics to make them look like the fill colors. Turn off the original fill legend and voila!

I know the ordering of the groups and within the groups are not exactly what you wanted, but it should be easy enough to change with factor levels etc.

Original Plot

https://i.stack.imgur.com/LXmeo.png

New Plot

https://i.stack.imgur.com/VAqov.png

Code

#####################
# Original code 
#####################

#loading libraries
library(ggplot2)

#Creating hypothetical dataframe
services<-data.frame(study=c(0),category=c(0),subcategory=c(0))
services<-services[-1,]
services[1:15,1]<-c(1:15)

services[1:4,2]<-c("Provisioning", "Regulating", "Suporting", "Cultural")
services[5:8,2]<-c("Provisioning", "Regulating", "Suporting", "Cultural")
services[9:10,2]<-c("Provisioning", "Regulating") 
services[11:15,2]<-c("Provisioning", "Regulating", "Suporting", "Cultural", "Provisioning")

services[1:4,3]<-c("Water supply", "Climate regulation", "Soil formation",      "Recreation")
services[5:8,3]<-c("Fisheries", "Water purification", "Habitat", "Recreation")
services[9:10,3]<-c("Water supply", "Flood regulation") 
services[11:15,3]<-c("Agriculture", "Water purification", "Soil formation", "Aesthetics", "Fisheries")
services$subcategory  <- factor(services$subcategory, levels=c("Recreation", "Aesthetics","Fisheries", "Water supply", "Agriculture","Water purification", "Climate regulation","Flood regulation","Soil formation", "Habitat"))

#####################
# New code 
#####################

# Create dummy variables 
cultural <- data.frame(Cultural_Services = c("Recreation","Aesthetics"),category = c("Cultural","Cultural"))
provisional <- data.frame(Provisioning_Services = c("Fisheries","Water Supply","Agricultre"), category = c("Provisioning","Provisioning","Provisioning"))
regulating <- data.frame(Regulating_Services = c("Water Purification","Climate Regulation","Flood Regulation"), category = c("Regulating","Regulating","Regulating"))
supporting <- data.frame(Supporting_Services = c("Soil Formation","Soil Formation", "Habitat", "Habitat"), x = c("Regulating","Suporting","Regulating","Suporting"),y=c(-1,-1,-2,-2))

## Create plot
ggplot() + 
## Plot the four dummy layer outside of the intended plotting area  
  geom_point(data = provisional, aes(x = category, y = -1, size = Provisioning_Services)) + 
  geom_point(data = cultural, aes(x = category, y = -1, color = Cultural_Services)) +
  geom_point(data = regulating, aes(x = category, y = -1, alpha = Regulating_Services)) +
  geom_line(data = supporting, aes(x = x, y = y, linetype = Supporting_Services)) +
## Add in your real plot goal  
  geom_bar(data = services, aes(category, fill=subcategory)) +
## Remove the Fill legend  
  scale_fill_hue(guide="none") +
## Override the guide aesthetics to make them look like fill colors  
  guides(size = guide_legend(override.aes = list(colour = c("#A3A500","#39B600","#00BF7D"),fill = c(NA,NA,NA), shape = c(15,15,15), size = c(8,8,8)),title="Provisioning Services"),
         color = guide_legend(override.aes = list(colour = c("#F8766D","#D89000"), shape = c(15,15), size = c(8,8)),title = "Cultural Services"),
         alpha = guide_legend(override.aes = list(colour = c("#00BFC4","#00B0F6","#9590FF"), shape = c(15,15,15),size = c(8,8,8)), title = "Regulating Services"),
         linetype = guide_legend(override.aes = list(colour = c("#E76BF3","#FF62BC"), shape = c(15,15), size = c(8,8)),title = "Supporting Services")) +
## Adjust the plot range to hide all the extra layers  
  ylim(0,5) +
  xlab("Ecosystem Service Type") +
  ylab("Number of times each ecosystem service was evaluated") 
#> Warning: Removed 3 rows containing missing values (geom_point).
#> Warning: Removed 2 rows containing missing values (geom_point).
#> Warning: Removed 3 rows containing missing values (geom_point).
#> geom_path: Each group consist of only one observation. Do you need to adjust the group aesthetic?
Laura Wiley
  • 233
  • 2
  • 6
  • Hadley's Tweets: https://twitter.com/hadleywickham/status/660101332058042370 and https://twitter.com/hadleywickham/status/660103377792045056 – Laura Wiley Oct 30 '15 at 22:50
  • For more help/examples of override.aes() check out this post: http://zevross.com/blog/2014/08/04/beautiful-plotting-in-r-a-ggplot2-cheatsheet-3/#manually-adding-legend-items-guides-override.aes – Laura Wiley Oct 30 '15 at 22:51
  • Huh, also just realized that `show_col(hue_pal())` gave me the wrong hex codes for the blue colors of Regulating Services. Resetting all colors manually (which you should do anyway as hue is a difficult palette to differentiate) will fix the issue. – Laura Wiley Oct 30 '15 at 23:03
  • Laura, Thanks so much for taking the time to find a fix to this problem! I really like your approach! I guess one remaining issue is that the idea of writing out all the colors and services manually for my real dataset, which is much bigger, isn't particularly appealing. I really do think it's time to build a simple extension to ggplot2 that would simply, quickly and easily solve this type of problem! ... anyhow, thanks again! – Dalal_EL_Hanna Nov 05 '15 at 02:30
1

I had a similar problem this morning and struck upon this partial solution.

ggplot(data = services,
       mapping = aes(x = category, fill = interaction(category, subcategory, sep = ', '))) +
  geom_bar() +
  guides(fill = guide_legend(title = 'Category and subcategory'))
rcorty
  • 1,140
  • 1
  • 10
  • 28