11

I am aware that one is able to annotate a plot created by ggplot2 or even to combine large and small viewports, as is documented in the ggplot2-book. However, it seems that these only work in the actual plot-areas and not in the "final plot".

For example I have a plot like this: enter image description here

Here we see ten panels showing a linear regression smoother applied to a binomial dataset, but that´s not the point. Now I want a summary (stored in a dataframe) in form of a text in the lower right of the plot, such as this... enter image description here

I did not find any example that comes even close. Any hints, helps or comments are much appreciated!

Jens
  • 2,363
  • 3
  • 28
  • 44
  • 3
    Have you looked at the package gridExtra, and used ggplotGrob()? – lawyeR Feb 13 '15 at 10:07
  • !!! you are right, I just checked gridExtra according to your suggestion and see the last comment in this post: http://www.r-bloggers.com/extra-extra-get-your-gridextra/ – Jens Feb 13 '15 at 10:10
  • it turns out that gridExtra is pretty clumsy. I was able to plot table and figure into a two row or column layout using ncol=2 but it does not really fit the needs. I will continue trying and also check out the ggplotGrob function. – Jens Feb 13 '15 at 10:23
  • 3
    Related: http://stackoverflow.com/questions/22450765/how-to-use-empty-space-produced-by-facet-wrap, just replace `ggplotGrob` with `tableGrob`. – tonytonov Feb 13 '15 at 11:17

2 Answers2

12

Rather late to the game, but I haven't seen any solution that extends to multiple empty facet spaces, so here goes.

Step 0. Sample ggplot with 2 unfilled facets, using the inbuilt diamonds dataset:

library(ggplot2)

p <- ggplot(diamonds,
       aes(x = carat, y = price)) +
  geom_point() +
  geom_smooth() +
  facet_wrap(~color)
p

step 0

Step 1. Convert plot to gtable using ggplotGrob

gp <- ggplotGrob(p)

library(gtable)

# visual check of gp's layout (in this case, it has 21 rows, 15 columns)
gtable_show_layout(gp)

step 1

Step 2. (Optional) Get the cell coordinates of the unfilled cells to be used for textbox. You can skip this if you prefer to read off the layout above. In this case the top-left cell would be (16, 8) and the bottom-right cell would be (18, 12).

# get coordinates of empty panels to be blanked out
empty.area <- gtable_filter(gp, "panel", trim = F)
empty.area <- empty.area$layout[sapply(empty.area$grob,
                                       function(x){class(x)[[1]]=="zeroGrob"}),]

empty.area$t <- empty.area$t - 1 #extend up by 1 cell to cover facet header
empty.area$b <- empty.area$b + 1 #extend down by 1 cell to cover x-axis

> empty.area
   t  l  b  r z clip      name
6 16  8 18  8 1   on panel-3-2
9 16 12 18 12 1   on panel-3-3

Step 3. Overlay textbox as a tableGrob

library(gridExtra)

gp0 <- gtable_add_grob(x = gp,
                       grobs = tableGrob("some text",
                                         theme = ttheme_minimal()),
                       t = min(empty.area$t), #16 in this case
                       l = min(empty.area$l), #8
                       b = max(empty.area$b), #18
                       r = max(empty.area$r), #12
                       name = "textbox")
grid::grid.draw(gp0)

step 3

Demonstrating some variations:

gp1 <- gtable_add_grob(x = gp,
                       grobs = tableGrob("Simple line of comment that can go on & on for the sake of demonstration. Automatic line wrap not included.",
                                         theme = ttheme_minimal()),
                       t = min(empty.area$t),
                       l = min(empty.area$l),
                       b = max(empty.area$b),
                       r = max(empty.area$r),
                       name = "textbox")
grid::grid.draw(gp1)

demo 1

gp2 <- gtable_add_grob(x = gp,
                       grobs = tableGrob("Simple line of comment that can go on & on. 
Automatic line wrap not included. \nAt least it understands the concept of line breaks.",
                                         theme = ttheme_minimal()),
                       t = min(empty.area$t),
                       l = min(empty.area$l),
                       b = max(empty.area$b),
                       r = max(empty.area$r),
                       name = "textbox")
grid::grid.draw(gp2)

demo 2

gp3 <- gtable_add_grob(x = gp,
                       grobs = tableGrob(tibble::tribble(~col1, ~col2,
                                                         "a.", "This is a line in a table",
                                                         "b.", "This is another line in a table"),
                                         rows = NULL,
                                         theme = ttheme_minimal()),
                       t = min(empty.area$t),
                       l = min(empty.area$l),
                       b = max(empty.area$b),
                       r = max(empty.area$r),
                       name = "textbox")
grid::grid.draw(gp3)

demo 3

Z.Lin
  • 28,055
  • 6
  • 54
  • 94
  • Awesome! Late but more than welcome! This is a problem I still encounter and did not find a solution for. Many thanks Lin! – Jens Sep 21 '17 at 12:56
  • Great! Thank you! And if one wants to add a new figure into the blank space (instead of text or a table), in the last step, just alter the "grobs" argument in the "gtable_and_grob()" function to be ggplotGrob(my_other_plot). – K Bro Jan 06 '21 at 21:09
0

Since ggplot2 2.2, I've used the caption option instead. You might want to give that a try.

library(tidyverse)

ggplot(data = mtcars, 
            mapping = aes(y=mpg, x=wt)) +
  geom_point() +
  facet_wrap(c("gear", "cyl"), labeller = "label_both") +
  labs(title = "mtcars analysis",
       subtitle = "This is a subtitle \n",
       caption = "\n Note: This analysis compares the number of gears and cylinders", 
       x = "weight", y = "mpg")

And here's what you'd get:

ggtest

I hope that helps you.

p0bs
  • 1,004
  • 2
  • 15
  • 22
  • Interesting, but the caption ends up at the bottom of the figure, and is not filling the lower right corner of the figure. Nevertheless, thank you for your contribution. – Jens Sep 21 '17 at 12:57