4

How to add multiple titles in legend ggplot?

library(RColorBrewer)
library(ggplot2)
data1<-data.frame(x= rnorm(20, 0,1), 
                  y=rnorm(20, 1,1), 
                  section=c("a1", "a2", "a3", "a4", "a5", "a6", "a7", 
                        "a8",  "a9","a10","b1", "b2", "c1",   "c2", "c3", 
                         "d1", "d2", "e1", "e2","f1"),
                  region=c(rep("a",10), rep("b",2),rep("c",3),rep("d",2), 
                             rep("e",2), rep("f",1))) #data

newpalette<-c(colorRampPalette(brewer.pal(9,"Blues"))(15)[15:6], 
            brewer.pal(9,"Greens")[c(4,8)], 
          brewer.pal(9,"Oranges")[c(4,7,9)], 
          brewer.pal(9,"Purples")[c(4,8)],      
          brewer.pal(9,"BrBG")[c(1,3)], 
          brewer.pal(9,"Greys")[5]) #20 color values
labels<-c("a1", "a2", "a3", "a4", "a5", "a6", "a7", "a8", "a9","a10",
          "b1", "b2", 
           "c1",   "c2", "c3",
           "d1", "d2", 
           "e1", "e2",
           "f1")
 data1$section<-factor(data1$section,levels= labels)     
 p.legend<-ggplot( ) + 
           geom_point(data=data1, aes(x, y, colour = section, 
           shape=section), size=2)+
           scale_color_manual(values =newpalette ,labels= labels)+
           scale_shape_manual(values = 
           c(rep(1,10),0,0,2,2,2,3,3,5,5,6),labels= labels)

I want the region values as the subtitles in legend like following figure.

enter image description here

Mayebe we could modify spacing of some specific legend keys (e.g. legend key a10, c3). And then use grid.text adding the text (a, b, c...) into the spacing like How to add multiple legend titles (columns) in ggplot. But my subtitiles are more complex and I don't know how to modify spacing of some specific legend keys.

Or we could create some another values (e.g. a11) and make it lucency in legend. Then adding the text b into the a11 spacing.But I don't know how to remove the specific legend keys and texts.

The third ways, I had ever add \n\n into some labels codes to create the spacing.

labels2<- c("a1", "a2", "a3", "a4", "a5", "a6", "a7", "a8", "a9","a10\n\n",
          "b1", "b2", 
           "c1",   "c2", "c3\n\n",
           "d1", "d2\n\n", 
           "e1", "e2\n\n",
           "f1")
p.legend<-ggplot( ) + 
           geom_point(data=data1, aes(x, y, colour = section, 
           shape=section), size=2)+
           scale_color_manual(values =newpalette ,labels= labels2)+
           scale_shape_manual(values = 
           c(rep(1,10),0,0,2,2,2,3,3,5,5,6),labels= labels2)

But the labels and key would not align like following figure

enter image description here

These are three ways that I had tried and failed.

Revo
  • 111
  • 8

1 Answers1

1

Using @Mark Peterson's suggestion (ggplot2: Divide Legend into Two Columns, Each with Its Own Title) to use cowplot, here is one way:

library(cowplot)

# create chart without legend
p <- ggplot( ) + 
  geom_point(data=data1, aes(x, y, colour = section, shape = section), size = 2) +
  scale_color_manual(values = newpalette) + scale_shape_manual(values = c(rep(1,10),0,0,2,2,2,3,3,5,5,6)) +
  theme(legend.position = "none")

# To create individaul ggplot for each region
region_i <- unique(data1$region)

for (i in 1:length(region_i)) {

  region_d <- data1[data1$region == region_i[i], ]

  # this is to keep same shapes as above
  shape_ind <- switch(i, 
                      "1" = "1", 
                      "2" = "0",
                      "3" = "2", 
                      "4" = "3",
                      "5" = "5", 
                      "6" = "6")

  # ggplots to be saved in p1, p2 etc...
  nam <- paste0("p", i)

  # ggplots for each region with different colours and shapes for these regions
  my_plot <- ggplot() + 
    geom_point(data = region_d, aes(x, y, colour = section, shape = section), size=2) +
    scale_color_manual(values = newpalette[which(data1$region == region_i[i])]) +
    scale_shape_manual(values = c(rep(as.numeric(shape_ind), nrow(region_d)))) + 
    labs(color = unique(region_d$region), shape = unique(region_d$region))

  # call p1, p2 etc.. to see the plots
  assign(nam, my_plot)

}

# cowplot to combine the plots
plot_grid(p, 
          plot_grid(get_legend(p1), get_legend(p2), ncol = 1), # a and b to be kept in one column
          plot_grid(get_legend(p3), get_legend(p4), get_legend(p5), get_legend(p6), ncol = 1),  # c, d, e and f in one column
          ncol = 3, rel_widths = c(4, 1, 1))

enter image description here

MKa
  • 2,248
  • 16
  • 22
  • Thanks, it does work. However when I use "for" , it will get error. The plot p1-p6 can not be drawn. The error for p1 is following. Error: Insufficient values in manual scale. 10 needed but only 1 provided. If I draw the p1-p6 one by one without "for" code, It will work. I don't know why. Thank you very much. – Revo Apr 04 '19 at 02:50
  • 1
    That's strange. Can you check `scale_color_manual` and `scale_shape_manual`? For `i = 1`, you need to make sure there are 10 values in manual scale. Hence why there is the line `newpalette[which(data1$region == region_i[i])]` and `c(rep(as.numeric(shape_ind), nrow(region_d))` – MKa Apr 04 '19 at 03:10
  • I had checked scale_color_manual and scale_shape_manual. There is no problem with the manual scale when i=1 (or 2-6), and the plot p1 can also be drawn. The error would come out only when I used the "for" code. Anyway, thanks a lot. – Revo Apr 04 '19 at 07:32