ggplot2: Coloring axis text on a faceted plot

后端 未结 2 658
无人及你
无人及你 2020-12-16 17:33

I seem unable to correctly color axis text on a faceted plot when the scales parameter is set to \"free\". Consider the following dataset:

相关标签:
2条回答
  • 2020-12-16 18:00

    After digging through the graphical objects (grobs) associated with the plot, I came across a potential hack to get around the issue. While not as elegant as Z.Lin's solution, I wanted to share it for educational purposes.

    We begin by retrieving grobs with

    gt <- ggplotGrob( g + facet_wrap( ~V3, scales = "free" ) )
    ## TableGrob (11 x 11) "layout": 20 grobs
    ##     z         cells        name                                  grob
    ## 1   0 ( 1-11, 1-11)  background       rect[plot.background..rect.105]
    ## 2   1 ( 7- 7, 4- 4)   panel-1-1               gTree[panel-1.gTree.17]
    ## 3   1 ( 7- 7, 8- 8)   panel-2-1               gTree[panel-2.gTree.30]
    ## 4   3 ( 5- 5, 4- 4)  axis-t-1-1                        zeroGrob[NULL]
    ## 5   3 ( 5- 5, 8- 8)  axis-t-2-1                        zeroGrob[NULL]
    ## 6   3 ( 8- 8, 4- 4)  axis-b-1-1    absoluteGrob[GRID.absoluteGrob.43]
    ## 7   3 ( 8- 8, 8- 8)  axis-b-2-1    absoluteGrob[GRID.absoluteGrob.50]
    ## 8   3 ( 7- 7, 7- 7)  axis-l-1-2    absoluteGrob[GRID.absoluteGrob.64]
    ## 9   3 ( 7- 7, 3- 3)  axis-l-1-1    absoluteGrob[GRID.absoluteGrob.57]
    ## 10  3 ( 7- 7, 9- 9)  axis-r-1-2                        zeroGrob[NULL]
    ## 11  3 ( 7- 7, 5- 5)  axis-r-1-1                        zeroGrob[NULL]
    ## 12  2 ( 6- 6, 4- 4) strip-t-1-1                         gtable[strip]
    ## 13  2 ( 6- 6, 8- 8) strip-t-2-1                         gtable[strip]
    ## 14  4 ( 4- 4, 4- 8)      xlab-t                        zeroGrob[NULL]
    ## 15  5 ( 9- 9, 4- 8)      xlab-b titleGrob[axis.title.x..titleGrob.33]
    ## 16  6 ( 7- 7, 2- 2)      ylab-l titleGrob[axis.title.y..titleGrob.36]
    ## 17  7 ( 7- 7,10-10)      ylab-r                        zeroGrob[NULL]
    ## 18  8 ( 3- 3, 4- 8)    subtitle zeroGrob[plot.subtitle..zeroGrob.102]
    ## 19  9 ( 2- 2, 4- 8)       title    zeroGrob[plot.title..zeroGrob.101]
    ## 20 10 (10-10, 4- 8)     caption  zeroGrob[plot.caption..zeroGrob.103]
    

    Grobs are hierarchical objects and the general rules for traversing these structures fall into two categories:

    1. If a grob is of type gtable (as gt above), accessing individual grobs that go into the table can be done through $grobs.
    2. If a grob is NOT of type gtable, its children grobs can be accessed through $children.

    Looking at the gtable above, we observe that grobs 6 and 7 correspond to the bottom axes of facets 1 and 2, respectively. Each of these axis grobs is of type absoluteGrob, so using the two rules above, we can examine what they are made up of like this:

    gt$grobs[[6]]$children
    ## (zeroGrob[axis.line.x..zeroGrob.40], gtable[axis]) 
    ##  and likewise for gt$grobs[[7]]$children
    

    Noting that the second child is a gtable, we can continue descending the hierarchy of grobs until we arrive at gt$grobs[[6]]$children[[2]]$grobs[[2]]$children[[1]], which is a leaf of the grob hierarchy (its $children is NULL) and corresponds to the axis text. Let's examine its graphical parameters, which can be accessed through $gp:

    ## Double-check that we have the correct text object
    gt$grobs[[6]]$children[[2]]$grobs[[2]]$children[[1]]$label
    ## [1] "A" "B" "C" "D" "E" "F" "G" "H" "I" "J" "K" "L" "M"
    
    ## Display the summary of graphical parameters
    str( gt$grobs[[6]]$children[[2]]$grobs[[2]]$children[[1]]$gp )
    ## List of 5
    ##  $ fontsize  : num 8.8
    ##  $ col       : chr [1:26] "black" "black" "black" "red" ...
    ##  $ fontfamily: chr ""
    ##  $ lineheight: num 0.9
    ##  $ font      : Named int 1
    ##   ..- attr(*, "names")= chr "plain"
    ##  - attr(*, "class")= chr "gpar"
    

    Note that the col attribute is of length 26 and corresponds exactly to the v variable from the question. If we look at the bottom axis of the second facet (gt$grobs[[7]]$...), we see that the same col value is used there as well, leading to identical axis text coloring in both facets (as suggested in Z.Lin's solution).

    Consequently, setting these color setting to only be the corresponding portions of v "by hand" allows us to modify the original plot and achieve the desired result.

    gt$grobs[[6]]$children[[2]]$grobs[[2]]$children[[1]]$gp$col <- v[1:13]
    gt$grobs[[7]]$children[[2]]$grobs[[2]]$children[[1]]$gp$col <- v[14:26]
    grid::grid.draw( gt )
    

    0 讨论(0)
  • 2020-12-16 18:17

    I don't think it's a bug. The problem is that v here is basically a string of characters, length 26, which defines colours for the first 26 breaks on the x-axis. When the x-axis has 26 breaks exactly, well & good; when it has less than that (which is the case when you set scales="free"), it simply restarts at the beginning for each axis. Q is red here because it's in the fourth position in the second plot, although the v[4]'s red was meant for D, in the first plot.

    Based on what I've tried & read here on SO, one can't map aesthetics into theme(), which controls the appearance of axis text in ggplot.

    It's possible to hack a solution by hiding the axis & using geom_text() instead to simulate an axis, since the latter does accept aesthetics mapped from the data. It may not be very elegant, though:

    g2 <- ggplot(cbind(X, v), #add v to X
                 aes(x = V1, y = V2)) + 
      geom_point() +
      # make space to accommodate the fake axis
      expand_limits(y = -0.05) +
      # create a strip of white background under the fake axis
      geom_rect(ymin = -5, ymax = 0, xmin = 0, xmax = nrow(X) + 1, fill = "white") +
      # fake axis layer, aligned below y = 0
      geom_text(aes(colour = v, label = V1), y = 0, vjust = 1.1) +
      # specify the font colours for fake axis
      scale_colour_manual(values = c("black", "red"), guide = F) +
      # hide the actual x-axis text / ticks
      theme(axis.text.x = element_blank(), axis.ticks.x = element_blank())
    
    g2 + facet_wrap( ~V3, scales = "free" )
    

    0 讨论(0)
提交回复
热议问题