3

Some journals require that figures with several graphs have each of the plots lettered. I'm trying to do the same with facet_wrap in ggplot2 because I've organized the data in such a way that covariates for all my explanatory variables are in one column and the grouping variables for the covariates are in another column.

I followed the instructions here to give the effect that the x-axis label is different for each plot (when it's really a panel).

But I still need labels for each plot (column figure.letters) in the figure, preferably in the top-left hand corner. I've attempted to add it in the facet_wrap formula, so now each plot has two strip panels.

require(ggplot2)
my.df <- data.frame(
  param = rep(c('Distance (km)', 'Energy (j)', 'Girth (m)', 'Height (cm)', 'Incline (degrees)'), each = 20),
  explanatory = rnorm(100, 0, 1),
  response = rnorm(100, 0, 1),
  figure.letter = rep(LETTERS[1:5], each = 20)
)

ggplot(my.df, aes(x = explanatory, y = response)) +
  geom_point() +
  facet_wrap(. ~ param + figure.letter, strip.position = 'bottom') +
  theme_bw() +
  theme(
    strip.placement = 'bottom',
    strip.background = element_blank()
  )

enter image description here

There are a lot of really good answers here, but they're more geared towards moving all the strip panels to the top and then left-justifying the panel text. I'm looking to separate the two strip panels so that the figure letter panel sits on top and is left-justified while the param strip panel remains at the bottom.

I'm also hoping to avoid writing code for several plots and then use patchwork to get the figure labels.

Is there a way to take the strip panel for the figure.letter and move it to the top while keeping the strip panel for param at the bottom?

Claus Wilke
  • 16,992
  • 7
  • 53
  • 104
Lalochezia
  • 497
  • 4
  • 15
  • 1
    I can't think of any way that would be possible. To work with patchwork (or cowplot, which has figure labeling built in), you wouldn't necessarily have to write code separately for each plot. Splitting the data into a list and iterating over that to make plots, then assembling with labels, should be pretty painless – camille Jan 06 '20 at 00:49
  • @camille It's possible by drawing text outside the plot canvas. https://stackoverflow.com/a/59606727/4975218 – Claus Wilke Jan 06 '20 at 22:56

2 Answers2

2

You can use geom_text() to place labels outside the plot area if you turn off clipping in coord_cartesian() and use the same fixed axis limits for all facets.

If you have different axis limits for different facets, then the patchwork route will likely be your only option.

require(ggplot2)
#> Loading required package: ggplot2

set.seed(1234)
my.df <- data.frame(
  param = rep(c('Distance (km)', 'Energy (j)', 'Girth (m)', 'Height (cm)', 'Incline (degrees)'), each = 20),
  explanatory = rnorm(100, 0, 1),
  response = rnorm(100, 0, 1)
)

df.label <- data.frame(
  param = unique(my.df$param),
  x = -2.8,
  y = 3.6,
  figure.letter = LETTERS[1:5]
)

ggplot(my.df, aes(x = explanatory, y = response)) +
  geom_point() +
  geom_text(
    data = df.label, aes(x = x, y = y, label = figure.letter),
    hjust = 0, vjust = 0
  ) +
  facet_wrap(. ~ param, strip.position = 'bottom') +
  coord_cartesian(
    xlim = c(-2.8, 2.8),
    ylim = c(-3.3, 3.3),
    expand = FALSE,
    clip = "off"
  ) +
  theme_bw() +
  theme(
    strip.placement = 'bottom',
    strip.background = element_blank(),
    plot.margin = margin(16.5, 5.5, 5.5, 5.5))
#> Warning: Suppressing axis rendering when strip.position = 'bottom' and
#> strip.placement == 'outside'

Created on 2020-01-05 by the reprex package (v0.3.0)

Claus Wilke
  • 16,992
  • 7
  • 53
  • 104
1

Throwing my hat into the ring for my own question, combining the scenario @Claus brought up where the x-axis could possibly have different limits (thus using patchwork) and @camille suggestion of splitting data and then using cowplot.

UPDATE Now includes an additional suggestion by @Claus to use the argument plotlist in plot_grid to plot a list of graphs.

#== Required Packages
require(ggplot2)
require(cowplot)

#== Make a Dataset
my.df <- data.frame(
  param = rep(c('Distance (km)', 'Energy (j)', 'Girth (m)', 'Height (cm)', 'Incline (degrees)'), each = 20),
  explanatory = c(rnorm(20, 0, 1), rnorm(20, 40, 30), 
                  rnorm(20, -5, 0.5), rnorm(20, 100, 300),
                  rnorm(20, 0.1, 0.02)),
  response = rnorm(100, 0, 1)
)

#== Split, Plot, and Save
my.list <- lapply(split(my.df, my.df$param), function(x){
  ggplot(x, aes(x = explanatory, y = response)) +
    xlab(unique(x$param)) +
    geom_point() +
    theme_bw() +
    theme(
      strip.placement = 'bottom',
      strip.background = element_blank()
    )
})

#== Plot Grid
plot_grid(plotlist = my.list, labels = 'AUTO')

enter image description here Not nearly as graceful as @Claus's though.

Lalochezia
  • 497
  • 4
  • 15
  • 1
    `plot_grid()` has a `plotlist` argument that takes a list of plots, so you can write `plot_grid(plotlist = my.list, labels = 'AUTO')`. – Claus Wilke Jan 07 '20 at 17:40