rggplot2r-grid

Is there any way to convert a gTree back to a workable ggplot in r?


Here is some example data:

exampledata <- structure(list(x = c(2.93131952459005, 3.21275054434318, 1.36466997175509, 
2.13626543532502, 1.45889556823722, 1.94598707699052, 0.719062322132357, 
2.38139571953234, 2.37813367615963, 3.98126576880209), y = c(7.51581380181603, 
9.77495763943671, 8.9666894018554, 8.62675858853528, 7.89238665417542, 
9.84865061237773, 7.24526820962333, 7.64727218939944, 7.28026738945878, 
8.6913070524479), z = structure(c(1L, 1L, 1L, 2L, 2L, 2L, 2L, 
3L, 3L, 3L), .Label = c("a", "b", "c"), class = "factor"), z2 = structure(c(1L, 
1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 2L), .Label = c("cat", "dog"), class = "factor")), class = "data.frame", row.names = c(NA, 
-10L))

The plot of that data:

asdf <- ggplot(exampledata, aes(x = x, y = y, color = z, shape = z)) +
  geom_point() + 
  geom_line(aes(color = z, linetype = z2))+
  scale_linetype_manual(values = c(1,2,3)) +
  theme(legend.position = 'top', 
  legend.spacing = unit(2, 'cm'))

This code below just increases the size of points within the z component of the legend (independently of the lines in z or actual points in the figure). The output of this is a gTree object. Although this section might seem arbitrary, it is an important step for the final plots

grid::grid.ls(grid::grid.force())

# Set the size of the point in the legend to 2 mm
grid::grid.gedit("key-1-[-0-9]+-1.2-[-0-9]+-2-[-0-9]+", size = unit(4, "mm"))

# save the modified plot to an object
g2 <- grid::grid.grab()
ggsave(g2, filename = 'g2.tiff')

Now imagine a scatter plot of the following dataframe:

datasetb <- structure(list(x = c(2.55279478309192, 0.929375129220925, 1.56509894419863, 
2.48026699500513, 1.18018131012236, 1.79675395182943, 0.817046700547386, 
1.99710482619256, 2.18780091987683, 3.41661353718804), y = c(8.88460717718884, 
9.11053089978428, 7.68492406933585, 8.23110925234432, 7.48154953916593, 
9.0253526297593, 9.41899905471655, 8.54779428609509, 9.17050925351926, 
5.83078691211861)), class = "data.frame", row.names = c(NA, -10L
))

b <- ggplot(data = datasetb, aes(x = x, y = y) +
  geom_point()

I want to be able to use plot_grid to combine these plots into one. However I don't think a gTree can be used as an argument to get_legend. Is there any way to convert a gTree back into a ggplot object, or any workaround

Below is my end goal.

prow <- plot_grid( asdf + theme(legend.position="none"),
                   b + theme(legend.position="none"),
                   align = 'vh',
                   labels = c("A", "B"),
                   hjust = -1,
                   nrow = 1
)

legend_a <- get_legend(asdf + theme(legend.position="top"))


p <- plot_grid( legend_a, prow, ncol = 1, rel_heights = c(.2, 1))

tiff("BothPlots.tiff", units = 'in', width = 12, height = 7, res = 400)

p
dev.off()

Solution

  • The short answer is no. A ggplot is like a recipe. The gTree is like the cake that the recipe produces. You can't unbake a cake to get the recipe back.

    However, the answer here is that, instead of modifying the legend then extracting it and stitching the plot together, you can stitch the plot together then modify the legend. So if you do things in this order:

    asdf <- ggplot(exampledata, aes(x = x, y = y, color = z, shape = z)) +
      geom_point() +
      geom_line(aes(color = z, linetype = z2)) +
      scale_linetype_manual(values = c(1, 2, 3)) +
      theme(legend.position = 'top', legend.spacing = unit(2, 'cm'))
    
    
    b <- ggplot(data = datasetb, aes(x = x, y = y)) + geom_point()
    
    prow <- plot_grid(asdf + theme(legend.position="none"),
                      b + theme(legend.position="none"),
                      align = 'vh',
                      labels = c("A", "B"),
                      hjust = -1,
                      nrow = 1)
    
    legend_a <- get_legend(asdf + theme(legend.position = "top"))
    
    plot_grid(legend_a, prow, ncol = 1, rel_heights = c(.2, 1))
    
    grid.ls(grid::grid.force())
    grid.gedit("key-1-[-0-9]+-1.2-[-0-9]+-2-[-0-9]+", size = unit(4, "mm"))
    
    # save the modified plot to an object
    g2 <- grid::grid.grab()
    

    Now we can save (I've had to save as a small png to allow upload here):

    png("BothPlots.png", units = 'in', width = 6, height = 3.5, res = 200)
    
    grid::grid.draw(g2)
    dev.off()
    

    You get:

    BothPlots.png enter image description here