2

I am trying to use grid.arrange to combine multiple types of graph/table, one of which is a correlation matrix using corrplot. Is there a way to convert a corrplot to a grob or export/import as an image compatible with grid.arrange? Since the other plots I'm combining are from ggplot and tableGrob, I can't seem to use par(mfrow = c(2, 2)) or layout(matrix(1:2)) as suggested in other posts.

P1 <- corrplot(PANAcor, order="hclust", addgrid.col = "gray",  
               type="full", col = col2(50), tl.cex=1.5, tl.col="black", 
               method="color", tl.pos="lt", tl.srt=45, hclust.method = "average",
               cl.ratio = 0.25, cl.align = "l", number.cex = 2)

summary <- grid.arrange(
    top=textGrob(sprintf("%s Summary",subject), gp=gpar(fontsize=16,font=8)),
    blank, P1, P2,
    blank, T1, T2,
    ncol=3, widths = c(0.1, 3, 3), 
    nrow=2, heights= c(1, 1),
    bottom = textGrob(sprintf("%s run %s",version,runtime), 
    gp=gpar(fontsize=6,font=8), hjust=-1)
  )

Error in gList(list(1, 0.45, 0.62, 0.55, 0.68, 0.64, -0.13, -0.37, -0.22, : only 'grobs' allowed in "gList" In addition: Warning message: In grob$wrapvp <- vp : Coercing LHS to a list

Data:

PANAcor <- structure(c(1, 0.56, 0.68, -0.49, -0.4, -0.39, 0.56, 1, 0.64, -0.55, 
                       -0.49, -0.54, 0.68, 0.64, 1, -0.69, -0.57, -0.65, -0.49,
                       -0.55, -0.69, 1, 0.82, 0.73, -0.4, -0.49, -0.57, 0.82, 1, 
                       0.71, -0.39, -0.54, -0.65, 0.73, 0.71, 1), 
                     .Dim = c(6L, 6L), 
                     .Dimnames = list(c("Anxious", "Irritable", "Upset", "Happy",
                                        "Enthusiastic", "Outgoing"), 
                                      c("Anxious", "Irritable", "Upset", "Happy", 
                                        "Enthusiastic", "Outgoing"))) 

col2 <- colorRampPalette(c("#7bffff","#7bbdff","#0000ff","black",
                           "#ff1a1a","#ff8000","#ffff4d"))
Z.Lin
  • 28,055
  • 6
  • 54
  • 94
Garth
  • 21
  • 1
  • 2
  • Have you seen the accepted answer to [this question](https://stackoverflow.com/questions/29583849/save-a-plot-in-an-object)? Sounds like `grid.echo` + `grid.grab` from the [gridGraphics package](https://cran.r-project.org/web/packages/gridGraphics/index.html) can convert P1 to a grob for you. – Z.Lin Dec 12 '18 at 01:36
  • I did try grid.echo + grid.grab, but it creates doubles/shifts everything (legend overlaps main plot, variable names overlap main plot and are no longer centered, columns overlap/are too narrow on the left half and have white space/are too wide on the right half) in a way I can't figure out how to undo. I've tried different settings within grid.echo + grid.grab, but no luck. – Garth Dec 12 '18 at 02:32
  • Can you include the output of `dput(PANAcor)` (or some sample data that reproduces this problem) in your question? This would help others troubleshoot better. – Z.Lin Dec 12 '18 at 02:41
  • An abbreviated version: `structure(c(1, 0.56, 0.68, -0.49, -0.4, -0.39, 0.56, 1, 0.64, -0.55, -0.49, -0.54, 0.68, 0.64, 1, -0.69, -0.57, -0.65, -0.49, -0.55, -0.69, 1, 0.82, 0.73, -0.4, -0.49, -0.57, 0.82, 1, 0.71, -0.39, -0.54, -0.65, 0.73, 0.71, 1), .Dim = c(6L, 6L), .Dimnames = list( c("Anxious", "Irritable", "Upset", "Happy", "Enthusiastic", "Outgoing"), c("Anxious", "Irritable", "Upset", "Happy", "Enthusiastic", "Outgoing")))` `col2 <- colorRampPalette(c("#7bffff","#7bbdff","#0000ff","black","#ff1a1a","#ff8000","#ffff4d"))` – Garth Dec 12 '18 at 02:51

1 Answers1

5

grid.echo + grid.grab from the gridGraphics package will convert a graphic drawn by corrplot into an identical-looking grob. Trouble is, the grob only looks identical at the exact same graphics device size.

Reproducing the problem:

library(gridGraphics)
library(grid)

corrplot(PANAcor, order="hclust", addgrid.col = "gray",  
         type="full", col = col2(50), tl.cex=1.5, tl.col="black", 
         method="color", tl.pos="lt", tl.srt=45, hclust.method = "average",
         cl.ratio = 0.25, cl.align = "l", number.cex = 2)

## grab the scene as a grid object & save it to P1
grid.echo()
P1 <- grid.grab()

grid.draw(P1) # looks fine, until you resize the graphics device

Original size (looks identical to the graphic generated by corrplot:

original size looks fine

Larger size (coloured regions remain squares, even though the matrix has extended to rectangular cells, & don't extend to the edge of each cell):

larger size with squares that don't cover the matrix

Smaller size (coloured regions have a minimum height / width, which cause them to spill out beyond the confines of each cell):

smaller size with squares that spill out

And if we arrange multiple grobs together, it's almost certainly going to look weird:

library(gridExtra)
grid.arrange(P1, P1, P1, layout_matrix = matrix(c(1, 1, 2, 3), nrow = 2, ncol = 2))

arranging 3 plots together

In short, due to the way corrplot draws the graphic, all other children grobs in P1 adjust in sync when the graphics device is re-sized, except for the grob responsible for colour.

Solution:

# save correlation matrix colors to a vector, then make coloured matrix grob transparent
matrix.colors <- getGrob(P1, gPath("square"), grep = TRUE)[["gp"]][["fill"]]
P1 <- editGrob(P1,
               gPath("square"), grep = TRUE,
               gp = gpar(col = NA,
                         fill = NA))

# apply the saved colours to the underlying matrix grob
P1 <- editGrob(P1,
               gPath("symbols-rect-1"), grep = TRUE,
               gp = gpar(fill = matrix.colors))

# convert the background fill from white to transparent, while we are at it
P1 <- editGrob(P1,
               gPath("background"), grep = TRUE,
               gp = gpar(fill = NA))

Replace gPath("square") with gPath("circle") if you use corrplot's default method. I haven't tested the other method options for the corresponding grob names, but the general principle should be similar.

Check that everything's aligned now:

grid.arrange(P1, P1, P1, layout_matrix = matrix(c(1, 1, 2, 3), nrow = 2, ncol = 2))

arranging 3 plots together after applying solution

By the way, you may want to adjust the text size arguments in corrplot. Based on your current code, the labels appear rather large, and are liable to be cut off when you arrange multiple plots together.

Z.Lin
  • 28,055
  • 6
  • 54
  • 94
  • Thanks so much for your help! I don't seem to have a few things defined. First it says: **Error in numnotnull("fontsize") : object 'grid.matrix.colors' not found** So I changed matrix.colors to grid.matrix.colors, and then got: **Warning message: In editGrob(P1, gPath("symbols-rect-1"), grep = TRUE, gp = gpar(fill = grid.matrix.colors)) : 'gPath' (symbols-rect-1) not found** – Garth Dec 12 '18 at 16:05
  • Sorry, 1st one was a typo on my part. I changed the vector name half way through answering & didn't clean up the code consistently. For the 2nd one, what do you get if you enter `P1$childrenOrder`? That should give you the names of all children grobs to `P1`. – Z.Lin Dec 13 '18 at 08:41
  • `P1$childrenOrder "graphics-background" "graphics-plot-1-symbols-square-1" "graphics-plot-1-symbols-square-2" "graphics-plot-1-symbols-square-3" "graphics-plot-1-rect-1" "graphics-plot-1-rect-2" "graphics-plot-1-segments-1" "graphics-plot-1-text-1" "graphics-plot-1-text-2" "graphics-plot-1-text-3" "graphics-plot-1-main-1" "graphics-plot-1-rect-3"` But then I either get "Subscript out of bounds" ([["x"]]) or "NA" (["x"]). Also, matrix.colors="white" (rather than multiple colors, not sure if it's supposed to say that). Thanks again, and sorry for all the difficulties! – Garth Dec 13 '18 at 17:24