knitr::opts_chunk$set(
  echo = TRUE, 
  collapse = TRUE, 
  comment = "#>", 
  fig.height = 3.5, 
  fig.width = 6)

Look, I just want to draw the plot’s legend-box and its caption on the same line.

Here’s what the basic plot looks like.

library(tidyverse)
#> Loading tidyverse: ggplot2
#> Loading tidyverse: tibble
#> Loading tidyverse: tidyr
#> Loading tidyverse: readr
#> Loading tidyverse: purrr
#> Loading tidyverse: dplyr
#> Warning: package 'dplyr' was built under R version 3.4.1
#> Conflicts with tidy packages ----------------------------------------------
#> filter(): dplyr, stats
#> lag():    dplyr, stats
df <- readr::read_csv("posterior.csv")
#> Parsed with column specification:
#> cols(
#>   rowid = col_integer(),
#>   rescor = col_double(),
#>   parameter = col_character(),
#>   test = col_character(),
#>   value = col_double()
#> )

# To draw horizontal quantile intervals...
library(ggstance)
#> Warning: package 'ggstance' was built under R version 3.4.1
#> 
#> Attaching package: 'ggstance'
#> The following objects are masked from 'package:ggplot2':
#> 
#>     geom_errorbarh, GeomErrorbarh
median_hilowh <- function(...) {
  median_hilow(...) %>% 
    rename(x = y, xmin = ymin, xmax = ymax)
}

df$test <- factor(df$test, c("PPVT", "EVT", "PPVT-EVT"))

base_plot <- ggplot(df) +
  aes(x = value, y = parameter, color = forcats::fct_rev(test)) +
  geom_vline(xintercept = 0, size = 2, color = "white") +
  stat_summaryh(fun.data = median_hilowh, geom = "linerangeh", 
                fun.args = list(.95), size = .5, 
                position = position_dodgev(height = .5)) +
  stat_summaryh(fun.data = median_hilowh, 
                geom = "linerangeh", fun.args = list(.80), size = 1.5, 
                position = position_dodgev(height = .5)) +
  stat_summaryh(fun.x = median, geom = "point", size = 3,
                position = position_dodgev(height = .5)) +
  labs(x = NULL, y = NULL) + 
  scale_color_discrete(
    name = NULL,
    breaks = c("PPVT", "EVT", "PPVT-EVT"),
    guide = guide_legend(direction = "horizontal"),
    labels = expression(PPVT, EVT, PPVT-EVT)) + 
  theme(
    legend.margin = margin(11/5 * .9),
    legend.justification = c(0, 0),
    legend.position = "bottom"
  )

base_plot

Now I add the caption

bad_plot <- base_plot + 
  labs(caption = "Intervals: thick 90%, thin 95%. Point: median.")
bad_plot

How can I get them on the same line?

I looked the printing method from the ggplot2 code and learned that the parts of the plot live in a table grob.

base_plot_data <- ggplot_build(base_plot)
base_plot_gtable <- ggplot_gtable(base_plot_data)
base_plot_gtable
#> TableGrob (12 x 7) "layout": 18 grobs
#>     z         cells       name                                  grob
#> 1   0 ( 1-12, 1- 7) background       rect[plot.background..rect.201]
#> 2   5 ( 5- 5, 3- 3)     spacer                        zeroGrob[NULL]
#> 3   7 ( 6- 6, 3- 3)     axis-l   absoluteGrob[GRID.absoluteGrob.172]
#> 4   3 ( 7- 7, 3- 3)     spacer                        zeroGrob[NULL]
#> 5   6 ( 5- 5, 4- 4)     axis-t                        zeroGrob[NULL]
#> 6   1 ( 6- 6, 4- 4)      panel              gTree[panel-1.gTree.158]
#> 7   9 ( 7- 7, 4- 4)     axis-b   absoluteGrob[GRID.absoluteGrob.165]
#> 8   4 ( 5- 5, 5- 5)     spacer                        zeroGrob[NULL]
#> 9   8 ( 6- 6, 5- 5)     axis-r                        zeroGrob[NULL]
#> 10  2 ( 7- 7, 5- 5)     spacer                        zeroGrob[NULL]
#> 11 10 ( 4- 4, 4- 4)     xlab-t                        zeroGrob[NULL]
#> 12 11 ( 8- 8, 4- 4)     xlab-b                        zeroGrob[NULL]
#> 13 12 ( 6- 6, 2- 2)     ylab-l                        zeroGrob[NULL]
#> 14 13 ( 6- 6, 6- 6)     ylab-r                        zeroGrob[NULL]
#> 15 14 (10-10, 4- 4)  guide-box                     gtable[guide-box]
#> 16 15 ( 3- 3, 4- 4)   subtitle zeroGrob[plot.subtitle..zeroGrob.198]
#> 17 16 ( 2- 2, 4- 4)      title    zeroGrob[plot.title..zeroGrob.197]
#> 18 17 (11-11, 4- 4)    caption  zeroGrob[plot.caption..zeroGrob.199]

bad_plot_data <- ggplot_build(bad_plot)
bad_plot_gtable <- ggplot_gtable(bad_plot_data)
bad_plot_gtable
#> TableGrob (12 x 7) "layout": 18 grobs
#>     z         cells       name                                   grob
#> 1   0 ( 1-12, 1- 7) background        rect[plot.background..rect.266]
#> 2   5 ( 5- 5, 3- 3)     spacer                         zeroGrob[NULL]
#> 3   7 ( 6- 6, 3- 3)     axis-l    absoluteGrob[GRID.absoluteGrob.235]
#> 4   3 ( 7- 7, 3- 3)     spacer                         zeroGrob[NULL]
#> 5   6 ( 5- 5, 4- 4)     axis-t                         zeroGrob[NULL]
#> 6   1 ( 6- 6, 4- 4)      panel               gTree[panel-1.gTree.221]
#> 7   9 ( 7- 7, 4- 4)     axis-b    absoluteGrob[GRID.absoluteGrob.228]
#> 8   4 ( 5- 5, 5- 5)     spacer                         zeroGrob[NULL]
#> 9   8 ( 6- 6, 5- 5)     axis-r                         zeroGrob[NULL]
#> 10  2 ( 7- 7, 5- 5)     spacer                         zeroGrob[NULL]
#> 11 10 ( 4- 4, 4- 4)     xlab-t                         zeroGrob[NULL]
#> 12 11 ( 8- 8, 4- 4)     xlab-b                         zeroGrob[NULL]
#> 13 12 ( 6- 6, 2- 2)     ylab-l                         zeroGrob[NULL]
#> 14 13 ( 6- 6, 6- 6)     ylab-r                         zeroGrob[NULL]
#> 15 14 (10-10, 4- 4)  guide-box                      gtable[guide-box]
#> 16 15 ( 3- 3, 4- 4)   subtitle  zeroGrob[plot.subtitle..zeroGrob.261]
#> 17 16 ( 2- 2, 4- 4)      title     zeroGrob[plot.title..zeroGrob.260]
#> 18 17 (11-11, 4- 4)    caption titleGrob[plot.caption..titleGrob.264]

So I figured I could drop the caption element from the second plot into the same row as the color guide from the first plot.

guide_row <- which(base_plot_gtable$layout$name == "guide-box")
caption_row <- which(bad_plot_gtable$layout$name == "caption")

merged_table <- gtable::gtable_add_grob(
  base_plot_gtable, 
  bad_plot_gtable$grobs[[caption_row]], 
  t = base_plot_gtable$layout[guide_row, c("t")],
  r = base_plot_gtable$layout[guide_row, c("r")],
  b = base_plot_gtable$layout[guide_row, c("b")],
  l = base_plot_gtable$layout[guide_row, c("l")])

merged_table
#> TableGrob (12 x 7) "layout": 19 grobs
#>     z         cells       name                                   grob
#> 1   0 ( 1-12, 1- 7) background        rect[plot.background..rect.201]
#> 2   5 ( 5- 5, 3- 3)     spacer                         zeroGrob[NULL]
#> 3   7 ( 6- 6, 3- 3)     axis-l    absoluteGrob[GRID.absoluteGrob.172]
#> 4   3 ( 7- 7, 3- 3)     spacer                         zeroGrob[NULL]
#> 5   6 ( 5- 5, 4- 4)     axis-t                         zeroGrob[NULL]
#> 6   1 ( 6- 6, 4- 4)      panel               gTree[panel-1.gTree.158]
#> 7   9 ( 7- 7, 4- 4)     axis-b    absoluteGrob[GRID.absoluteGrob.165]
#> 8   4 ( 5- 5, 5- 5)     spacer                         zeroGrob[NULL]
#> 9   8 ( 6- 6, 5- 5)     axis-r                         zeroGrob[NULL]
#> 10  2 ( 7- 7, 5- 5)     spacer                         zeroGrob[NULL]
#> 11 10 ( 4- 4, 4- 4)     xlab-t                         zeroGrob[NULL]
#> 12 11 ( 8- 8, 4- 4)     xlab-b                         zeroGrob[NULL]
#> 13 12 ( 6- 6, 2- 2)     ylab-l                         zeroGrob[NULL]
#> 14 13 ( 6- 6, 6- 6)     ylab-r                         zeroGrob[NULL]
#> 15 14 (10-10, 4- 4)  guide-box                      gtable[guide-box]
#> 16 15 ( 3- 3, 4- 4)   subtitle  zeroGrob[plot.subtitle..zeroGrob.198]
#> 17 16 ( 2- 2, 4- 4)      title     zeroGrob[plot.title..zeroGrob.197]
#> 18 17 (11-11, 4- 4)    caption   zeroGrob[plot.caption..zeroGrob.199]
#> 19 18 (10-10, 4- 4)     layout titleGrob[plot.caption..titleGrob.264]
grid::grid.draw(merged_table)

This is all incredibly hacky, and I don’t really know what I’m doing, and maybe the ggplot2 authors should scold me for showing users how to mess up their plots, and I don’t thing the text lines up perfectly, but it worked, or at least, it’s the rat hole I stumbled down today.