8

When using {patchwork} to combine separate ggplots into the same graphic, I sometimes want to add one or more sections that are no plots at all, but basically a textbox.

As far as I can tell, the only time this is adressed in the patchwork documentation is here making use of wrap_elements(grid::textGrob('Text on left side')). This won't do, however, since I want to use {ggtext} mostly to (i) have automated word-wrapping and (ii) mix regular, italics, bold and colored text (see e.g. here).

So far, I have found two different approaches that made this work and they are demonstrated in the reprex below. Both approaches have in common that they basically hide every part of the ggplot that is not the desired text.

  • The 1st approach (inspiration from here: plot and relevant code) adds the text as ggtext::geom_textbox().
  • The 2nd approach (inspiration from here: plot and relevant code) adds the text as a subtitle with theme(plot.subtitle = ggtext::element_markdown()).

Question

My question is, however, if there are even better/simpler ways to implement this. Mostly because as you can see in the reprex below, there is some playing around with margin() and plot_layout() necessary until the text lines up correctly.

I can think of two different vertical alignments I would want for this text:

  1. So that it starts at the same height as the title of the left plot. p2 is close to this, but needs manual twisting. Furthermore, it currently adds unnecessary vertical space for title and subtitle of the left plot.
  2. So that it starts at the same height as the plot area of the left plot. p1 is close to this, but needs manual twisting

reprex

# setup -------------------------------------------------------------------
library(ggtext)
library(glue)
library(patchwork)
library(tidyverse)

cols <- c(ctrl = "red", trt1 = "blue", trt2 = "purple")

string <- glue("<i style='color:{cols}'>{names(cols)}</i>") %>%
  paste(collapse = ", ") %>%
  paste("There are three treatments", .,
        "and here are some interesting facts about them.") %>%
  rep(5) %>% paste(collapse = " ")

plot <- ggplot(PlantGrowth) +
  aes(x = group, y = weight, color = group) +
  geom_point() +
  scale_color_manual(values = cols, guide = "none") +
  labs(title = "Title",
       subtitle = "Subtitle",
       caption = "Caption") 


# 1st approach: geom_textbox() --------------------------------------------
text1 <- ggplot(data = tibble(x = 0, y = 1, label = string)) +
  aes(x = x, y = y, label = label) +
  geom_textbox(
    box.color = NA,
    fill = NA,
    width = unit(10, "cm"),
    hjust = 0,
    vjust = 1
  ) +
  scale_x_continuous(limits = c(0, 1), expand = c(0, 0)) +
  scale_y_continuous(limits = c(0, 1), expand = c(0, 0)) +
  theme_void() +
  theme(
    plot.margin = margin(0, 0, 0, 0)
  )


p1_left  <- plot
p1_right <- text1 / plot + plot_layout(heights = c(1, 1))
p1 <- p1_left | p1_right

ggsave("p1.png", p1, width = 9, height = 6)
cowplot::ggdraw() + cowplot::draw_image(png::readPNG("p1.png"))

# 2nd approach: subtitle --------------------------------------------------
text2 <- ggplot(data = data.frame(x = 1:2, y = 1:10)) +
  labs(subtitle = string) +
  theme_void() +
  theme(
    plot.subtitle = ggtext::element_textbox_simple(
      hjust = 0,
      halign = 0,
      margin = margin(20, 0, 0, 0)
    ),
    plot.margin = margin(0, 0, 0, 0)
  )

p2_left  <- plot
p2_right <- text2 / plot + plot_layout(heights = c(1, 6))
p2 <- p2_left | p2_right

ggsave("p2.png", p2, width = 9, height = 6)
cowplot::ggdraw() + cowplot::draw_image(png::readPNG("p2.png"))

Created on 2022-08-24 with reprex v2.0.2

edit 1

Based on Quinten's answer, I updated

  1. the reprex. It was correctly pointed out that the ggplots created via {reprex} were sort of arbitrary in their dimensions. This is similar with the plot preview window in RStudio. However, I do not wish this to be the focus of this question, since ggplots will always be exported with some given dimensions. Therefore I added two lines that export the ggplot with given dimensions via ggsave(...) and then display the actual png via cowplot::draw_image(png::readPNG(".png")).
  2. the question. It is now more precise in what I want.

edit 2

Related tweet: https://twitter.com/CedScherer/status/1579066626741178368

Paul Schmidt
  • 1,072
  • 10
  • 23
  • For your second approach, when you click on Zoom in Rstudio, do you like the way how the plot is shown? – Quinten Aug 23 '22 at 10:53
  • More or less: (i) the extra space between text and bottom plot definitely has to go and (ii) the title and subtitle of the left plot should not be stretched upwards just to align with the text-box-subtitle. (Also, I am always going to look at the exported file and not the RStudio preview - but yeah it's unfortunate how {reprex} displays it) – Paul Schmidt Aug 23 '22 at 11:07

1 Answers1

0

What you could do is first change the height layout between your right plot and text using plot_layout with argument height. After check with the "Zoom" option in Rstudio how the plot is shown and change the width and height until you like it. When you right-click on "Inspect Element", you can see what the height and width are. Here is an example:

enter image description here

In this case width = 1259 and height = 827. You can use these values with ggsave to specify the width and height with aspect ratio, which gives the following result:

p <- plot | (text2 / plot + plot_layout(heights = c(1, 7))) 

# aspect ration 1259:827
ratio = 827/1259
width = 10
height = ratio * width
ggsave(p, filename = "your_plot.png",  width = width, height = height)

Output:

enter image description here

As you can see it is better aligned know.

Quinten
  • 35,235
  • 5
  • 20
  • 53