Add annotation and segments to groups of legend elements

前端 未结 2 761
予麋鹿
予麋鹿 2020-12-14 12:55

My ggplot has the following legend:

I want to group my individual legend variables, and add the group names and \"brackets\" like shown in legend below:

2条回答
  •  温柔的废话
    2020-12-14 13:20

    Because @erocoar provided the grob digging alternative, I had to pursue the create-a-plot-which-looks-like-a-legend way.

    I worked out my solution on a smaller data set and on a simpler plot than OP, but the core issue is the same: ten legend elements to be grouped and annotated. I believe the main idea of this approach could easily be adapted to other geom and aes.

    library(data.table)
    library(ggplot2)
    library(cowplot)
    
    # 'original' data    
    dt <- data.table(x = sample(1:10), y = sample(1:10), z = sample(factor(1:10)))
    
    # color vector
    cols <- c("1" = "olivedrab1", "2" = "olivedrab2",            # min
              "3" = "olivedrab3", "4" = "yellow", "5" = "gold2", # low
              "6" = "orange1", "7" = "orange3",                  # moderate
              "8" = "darkorange3", "9" = "red2", "10" = "red4")  # high 
    
    # original plot, without legend
    p1 <- ggplot(data = dt, aes(x = x, y = y, color = z)) +
      geom_point(size = 5) +
      scale_color_manual(values = cols, guide = FALSE)
    
    # create data to plot the legend
    # x and y to create a vertical row of points
    # all levels of the variable to be represented in the legend (here z)
    d <- data.table(x = 1, y = 1:10, z = factor(1:10))
    
    # cut z into groups which should be displayed as text in legend
    d[ , grp := cut(as.numeric(z), breaks = c(0, 2, 5, 7, 11),
                    labels = c("min", "low", "mod", "high"))]
    
    # calculate the start, end and mid points of each group
    # used for vertical segments
    d2 <- d[ , .(x = 1, y = min(y), yend = max(y), ymid = mean(y)), by = grp]
    
    # end points of segments in long format, used for horizontal 'ticks' on the segments  
    d3 <- data.table(x = 1, y = unlist(d2[ , .(y, yend)]))
    
    # offset (trial and error)
    v <- 0.3
    
    # plot the 'legend'
    p2 <- ggplot(mapping = aes(x = x, y = y)) +
      geom_point(data = d, aes(color = z), size = 5) +
      geom_segment(data = d2,
                   aes(x = x + v, xend = x + v, yend = yend)) +
      geom_segment(data = d3,
                   aes(x = x + v, xend = x + (v - 0.1), yend = y)) +
      geom_text(data = d2, aes(x = x + v + 0.4, y = ymid, label = grp)) +
      scale_color_manual(values = cols, guide = FALSE) +
      scale_x_continuous(limits = c(0, 2)) +
      theme_void()
    
    # combine original plot and custom legend
    plot_grid(p1,
              plot_grid(NULL, p2, NULL, nrow = 3, rel_heights = c(1, 1.5, 1)),
              rel_widths = c(3, 1))
    


    In ggplot the legend is a direct result of the mapping in aes. Some minor modifications can be done in theme or in guide_legend(override.aes . For further customization you have to resort to more or less manual 'drawing', either by speleological expeditions in the realm of grobs (e.g. Custom legend with imported images), or by creating a plot which is added as legend to the original plot (e.g. Create a unique legend based on a contingency (2x2) table in geom_map or ggplot2?).

    Another example of a custom legend, again grob hacking vs. 'plotting' a legend: Overlay base R graphics on top of ggplot2.

提交回复
热议问题