Match legend text color in geom_text to symbol

前端 未结 4 606
暗喜
暗喜 2020-12-01 18:34

I am trying to color match the text of the legend to the color of text produced by a factored variable using geom_text. Here is a minimal working example:

4条回答
  •  鱼传尺愫
    2020-12-01 19:21

    Sometimes it is easier to edit a grob using grid's editing functions - if the names of the relevant grobs can be found. In this case, they can be found, and the edit is straightforward - change colour of label from black to red or blue.

    library(ggplot2)
    library(grid)
    
    df <- data.frame(a=rnorm(10),b=1:10,c=letters[1:10],d=c("one","two"))
    p1 <-ggplot(data=df,aes(x=b,y=a))
    p1 <- p1 + geom_text(aes(label = c, color=d, fontface="bold"))
    p1 <- p1 + scale_color_hue(name="colors should match",breaks=c("one", "two"),
                     labels=c("should be salmon", "should be sky blue"))
    p1
    
    # Get the ggplot grob
    g <- ggplotGrob(p1)
    
    # Check out the grobs
    grid.ls(grid.force(g))
    

    Look through the list of grobs. The grobs we want to edit are towards the bottom of the list, in the 'guide-box' set of grobs - with names that begin with "label". There are two grobs:

    label-3-3.4-4-4-4
    label-4-3.5-4-5-4

    # Get names of 'label' grobs.
    names.grobs <- grid.ls(grid.force(g))$name 
    labels <- names.grobs[which(grepl("label", names.grobs))]
    
    # Get the colours
    # The colours are the same as the colours of the plotted points.
    # These are available in the ggplot build data.
    gt <- ggplot_build(p1)
    colours <- unique(gt$data[[1]][, "colour"])
    
    # Edit the 'label' grobs - change their colours
    # Use the `editGrob` function
    for(i in seq_along(labels)) {
        g <- editGrob(grid.force(g), gPath(labels[i]), grep = TRUE,  
             gp = gpar(col = colours[i]))
    }
    
    # Draw it
    grid.newpage()
    grid.draw(g)
    

    What if it was required that the keys be points rather than letters? It could be useful because the 'a' is a symbol in the plot, and it is a symbol in the legend key. This is not a simple edit, like above. I need a point grob to take the place of the text grob. I draw grobs in viewports, but if I can find the names of the relevant viewports, it should be straightforward to make the change.

    # Find the names of the relevant viewports
    current.vpTree()  # Scroll out to the right to find he relevant 'key' viewports.
    

    viewport[key-4-1-1.5-2-5-2], viewport[key-3-1-1.4-2-4-2],

    # Well, this is convenient. The names of the viewports are the same 
    # as the names of the grobs (see above). 
    # Easy enough to get the names from the 'names.grobs' list (see above). 
    # Get the names of 'key' viewports(/grobs)
    keys <- names.grobs[which(grepl("key-[0-9]-1-1", names.grobs))]
    
    # Insert points grobs into the viewports:
    #    Push to the viewport;
    #    Insert the point grob;
    #    Pop the viewport.
    for(i in seq_along(keys)) {
       downViewport(keys[i])
       grid.points(x = .5, y = .5, pch = 16, gp = gpar(col = colours[i]))
       popViewport()
    }
    popViewport(0)
    
    # I'm not going to worry about removing the text grobs. 
    # The point grobs are large enough to hide them. 
    
    plot = grid.grab()
    grid.newpage()
    grid.draw(plot)
    

    Update

    Taking account of @user20650 's advice to change the legend key (see the comment below):

    p1 <-ggplot(data=df,aes(x=b,y=a))
    p1 <- p1 + geom_text(aes(label = c, color=d, fontface="bold"))
    p1 <- p1 + scale_color_hue(name="colors should match",breaks=c("one", "two"),
                     labels=c("should be salmon", "should be sky blue"))
    
    GeomText$draw_key <- function (data, params, size) { 
       pointsGrob(0.5, 0.5, pch = 16, 
       gp = gpar(col = alpha(data$colour, data$alpha), 
       fontsize = data$size * .pt)) }
    
    p1
    

    Then proceed as before to change the colour of the legend text.

提交回复
热议问题