J Louro
J Louro

Reputation: 33

customize two legends inside one graph in ggplot2

I wanted to comment on the following doubt.

Using this code:

Plot<-data.frame(Age=c(0,0,0,0,0),Density=c(0,0,0,0,0),Sensitivity=c(0,0,0,0,0),inf=c(0,0,0,0,0),sup=c(0,0,0,0,0),tde=c(0,0,0,0,0))    
Plot[1,]<-c(1,1,0.857,0.793,0.904,0.00209834)
Plot[2,]<-c(1,2,0.771   ,0.74,0.799,0.00348286)
Plot[3,]<-c(1,3,0.763   ,0.717,0.804,0.00577784) 
Plot[4,]<-c(1,4,0.724   ,0.653,0.785,0.00504161)
Plot[5,]<-c(2,1,0.906,0.866,0.934,0.00365742)
Plot[6,]<-c(2,2,0.785   ,0.754,0.813,0.00440399)
Plot[7,]<-c(2,3,0.660,0.593,0.722,0.00542849)
Plot[8,]<-c(2,4,0.544,0.425,0.658,0.00433052)

     names(Plot)<-c("Age","Mammographyc density","Sensitivity","inf","sup","tde")
    Plot$Age<-c("50-59","50-59","50-59","50-59","60-69","60-69","60-69","60-69")
    Plot$Density<-c("Almost entirely fat","Scattered fibroglandular density","Heterogeneously dense","Extremely dense","Almost entirely fat","Scattered fibroglandular density","Heterogeneously dense","Extremely dense")    
levels(Plot$Age)<-c("50-59","60-69")
levels(Plot$Density)<-c("Almost entirely fat","Scattered fibroglandular density","Heterogeneously dense","Extremely dense")
pd <- position_dodge(0.2) # 
    Plot$Density <- reorder(Plot$Density, 1-Plot$Sensitivity)

ggplot(Plot, aes(x = Density, y = 100*Sensitivity, colour=Age)) + 
  geom_errorbar(aes(ymin = 100*inf, ymax = 100*sup), width = .1, position = pd) +
  geom_line(position = pd, aes(group = Age), linetype = c("dashed")) +
  geom_point(position = pd, size = 4)+
  scale_y_continuous(expand = c(0, 0),name = 'Sensitivity (%)',sec.axis = sec_axis(~./5,   name  = 'Breast cancer detection rate (per 1000 mammograms)', breaks = c(0,5,10,15,20),
                                                              labels = c('0‰',"5‰", '10‰', '15‰', '20‰')), limits = c(0,100)) +
   geom_line(position = pd, aes(x = Density, y = tde * 5000, colour = Age, group = Age), linetype = c("dashed"), data = Plot) +
  geom_point(shape=18,aes(x = Density, y = tde * 5000, colour = Age, group = Age), position = pd, size = 4) +
  theme_light() + 
 scale_color_manual(name="Age (years)",values = c("50-59"= "grey55", "60-69" = "grey15")) +
  theme(legend.position="bottom") + guides(colour = guide_legend(), size = guide_legend(),
                                         shape = guide_legend())

I have made the following graph,

Graph

in which the axis on the left is the scale of the circles and the axis on the right is the scale of the diamonds. The fact is that I would like to have a legend approximately like this:

Legend

But it is impossible for me, I have tried suggestions of other threads like scale_shape and different commands in guides but I have not got success. I just want to make clear the difference in what shape and color represent.

Would someone know how to help me?

Best regards,

Upvotes: 1

Views: 338

Answers (1)

W. Murphy
W. Murphy

Reputation: 1141

What you should do is a panel plot to avoid the confusion of double axes:

library(dplyr)
library(tidyr)
Plot %>% 
  gather(measure, Result, Sensitivity, tde) %>%
  ggplot(aes(x = Density, y = Result, colour=Age)) + 
  geom_errorbar(aes(ymin = inf, ymax = sup), width = .1, position = pd,
                data = . %>% filter(measure == "Sensitivity")) +
  geom_line(aes(group = Age), position = pd, linetype = "dashed") +
  geom_point(position = pd, size = 4)+
  # scale_y_continuous(expand = c(0, 0), limits = c(0, 1)) +
  scale_y_continuous(labels = scales::percent) +
  facet_wrap(~measure, ncol = 1, scales = "free_y") +
  theme_light() + 
  scale_color_manual(name="Age (years)",values = c("50-59"= "grey55", "60-69" = "grey15")) +
  theme(legend.position="bottom")

enter image description here

But to do what you asked, you problem is that you have only 1 non-positional aesthetic mapped so you cannot get more than one legend. To force a second legend, you need to add a second mapping. It can be a dummy mapping that has no effect, as below we map alpha but then manually scale both levels to 100%. This solution is not advisable because, as you have done in your example of a desired legend, it is easy to mix up the mappings and have your viz tell a lie by mislabeling which points are sensitivity and which are detection rate.

ggplot(Plot, aes(x = Density, y = 100*Sensitivity, colour=Age, alpha = Age)) + 
  geom_errorbar(aes(ymin = 100*inf, ymax = 100*sup), width = .1, position = pd) +
  geom_line(position = pd, aes(group = Age), linetype = c("dashed")) +
  geom_point(position = pd, size = 4)+
  scale_y_continuous(expand = c(0, 0),name = 'Sensitivity (%)',sec.axis = sec_axis(~./5,   name  = 'Breast cancer detection rate (per 1000 mammograms)', breaks = c(0,5,10,15,20),
                                                                                   labels = c('0‰',"5‰", '10‰', '15‰', '20‰')), limits = c(0,100)) +
  geom_line(position = pd, aes(x = Density, y = tde * 5000, colour = Age, group = Age), linetype = c("dashed"), data = Plot) +
  geom_point(shape=18,aes(x = Density, y = tde * 5000, colour = Age, group = Age), position = pd, size = 4) +
  theme_light() + 
  scale_color_manual(name="Age (years)",values = c("50-59"= "grey55", "60-69" = "grey15")) +
  scale_alpha_manual(values = c(1, 1)) +
  guides(alpha = guide_legend("Sensitivity"),
         color = guide_legend("Detection Rate", override.aes = list(shape = 18))) +
  theme(legend.position="bottom")

enter image description here

Upvotes: 2

Related Questions