Search code examples
rggplot2patchwork

How to arrange plots in specific positions on a grid in R?


I am trying to plot multiple plots (of different sizes) onto a grid, using specific coordinates. To do this, I am creating a treemap using the treemap package to get coordinates and then, using these coordinates, Im trying to place my individual plots. Hopefully my example below will explain better my problem.

To begin, I create some barplots and then, I create a treemap, like so:

library(ggplot2)
library(treemap)
#create data
df <- data.frame(var1 = c("A", "B", "C"),
             var2 = runif(3),
             var3 = runif(3),
             var4 = runif(3),
             size = c(10, 5, 3),
             labels = c(1:3))
# Basic barplot
p1 <- ggplot(data=df, aes(x=var1, y=var2)) +
   geom_bar(stat="identity")
p2 <- ggplot(data=df, aes(x=var1, y=var3)) +
   geom_bar(stat="identity")
p3 <- ggplot(data=df, aes(x=var1, y=var4)) +
  geom_bar(stat="identity")

#create treemap
tm <- treemap(df,
       index = "labels",
       vSize = "size",
       palette = "Set2")

If we take a look at the treemap object tm, we can see that it provides the coordinates of the treemap shown below, where x0, y0, w, and h are the coordinates.

> tm$tm
  labels vSize vColor stdErr vColorValue level        x0    y0         w     h   color
1      1    10      1     10          NA     1 0.0000000 0.000 0.5555556 1.000 #66C2A5
2      2     5      1      5          NA     1 0.5555556 0.375 0.4444444 0.625 #FC8D62
3      3     3      1      3          NA     1 0.5555556 0.000 0.4444444 0.375 #8DA0CB

treemap

So, what Im trying to do is to take my three barplots p1,p2,and p3 and place them into the treemap so that p1 will be in position 1 of the treemap.... p2 will be in position 2 etc...

For clarity, My desired result would look something like this: treemap of plots

Any suggestions as to how I could do this? I tried using the patchwork package but ran into issues of the plots overlapping... but im open to suggestions using any package (eg gridExtra or just ggplot)

EDIT to clarify things a little, as per the answer given by Peter below in relation to the patchwork package, I cant manually input the coordinates as the plot layout (I was originally using the area function in patchwork and inputting the coordinates of the treemap into the area function). But, as mentioned in the comments below, if I have numerous barplots, and the size and shape of the treemap changes, then I cant manually input the layout values. I am trying to find a way to automate the process


Solution

  • This requires some arithmetic, and isn't perfect, but should be a start toward using patchwork's more complex layout options. There are a couple tricks to address:

    • treemap scales its dimensions based on the viewport, which means the positions it calculates depend on the viewer in e.g. RStudio's plot pane. Instead, it's not strictly necessary, but you should probably decide on an aspect ratio to make sure the positions of boxes are something you'd want.
    • You need to convert the decimal positions & sizes to whole numbers, not simply by multiplying by some number and rounding, but by finding the denominator of their fractional equivalent and multiplying by that. For example, the first box has a width 0.55555, which is 5/9; to get integers, we need to know that this box should be 5 units wide out of a total of 9 units. Taking the maximum width & height are arbitrary; the point is just to get one that isn't equal to 1.0.
    • Adding and subtracting 1 from the different dimensions isn't the best but was an easy-enough way to get e.g. one box that goes from 1 to 5, and the next from 6 to 9.
    • The order of plots isn't right but the sizing is...Haven't figured out why, but to try diagnosing this I added labels to each plot.
    library(ggplot2)
    library(treemap)
    library(dplyr)
    
    asp <- 1.25
    tm <- treemap(df, index = "labels", vSize = "size", palette = "Set2", sortID = "labels",
                  aspRatio = asp)
    denoms <- tm$tm %>%
      tidyr::pivot_longer(w:h, names_to = "direction") %>%
      filter(value < 1) %>%
      group_by(direction) %>%
      slice_max(value, n = 1) %>%
      mutate(denom = MASS::fractions(value) %>%
               stringr::str_extract("\\d+$") %>%
               as.numeric()) %>%
      select(direction, denom) %>%
      tibble::deframe()
    denoms
    #> h w 
    #> 8 9
    areas <- tm$tm %>%
      mutate(across(c(x0, w), ~. * denoms[["w"]]),
             across(c(y0, h), ~. * denoms[["h"]]),
             top = y0 + 1,
             left = x0 + 1,
             bottom = top + h - 1,
             right = left + w - 1) %>%
      select(top:right) %>%
      purrr::pmap(function(top, left, bottom, right) patchwork::area(top, left, bottom, right)) %>%
      purrr::reduce(c)
    
    patchwork::wrap_plots(p1, p2, p3, design = areas)