This is not easy! As usual with plot annotation, there are basically three main options.
- annotate outside the plot area with clipping turned off.
- create plots and paste them together
- mess with the grobs.
In my plots, I've decided to replace your scale_fill call with something simpler.
Here option 1:
library(tidyverse)
df1$facet <- fct_collapse(df1$x, cd = c("c", "d")) # slightly changed
# Create data frames for segments and labels
yseg <- c(.5, 1.5, 3.5, 4.5)
df_txt <- data.frame(x = -0.75, y = c(1, 2.5, 4),
label = paste0("element", 1:3), facet = "a")
df_seg <- data.frame(x = 0.5, xend = -1.5, y = yseg, facet = "a")
ggplot(df1, aes(x = x, y = y)) +
geom_tile(aes(fill = z), colour = "grey20") +
scale_fill_distiller(palette = "RdBu") +
scale_x_discrete(expand = c(0, 0)) + # kind of necessary
facet_grid(.~facet, scales = "free_x", space = "free") +
geom_segment(data = df_seg,
aes(x = x, xend = xend, y = yseg,yend = yseg), lty = "dashed",
color = "black") +
geom_text(data = df_txt, aes(x = x, y = y, label = label), hjust = 0.5) +
coord_cartesian(xlim = c(0.5, NA), clip = "off") + # this is how you turn clipping off
theme(strip.text.x = element_blank(),
plot.margin = margin(l = 2, unit = "inch"))
Another option is to make 3 different plots - arguably less hacky. Well. I still think it's hacky.
library(patchwork)
# create three plots. You could obviously also create four plots.
p_right <-
ggplot(filter(df1, x != "a"), aes(x = x, y = y)) +
geom_tile(aes(fill = z), colour = "grey20") +
scale_x_discrete(expand = c(0, 0)) +
scale_fill_distiller(palette = "RdBu", limits = c(-1, 1.5)) + # You need to set the same scale limits
facet_grid(.~facet, scales = "free_x", space = "free") +
theme(axis.title.y = element_blank(),
axis.text.y = element_blank(),
axis.ticks.y = element_blank(),
plot.margin = margin(),
panel.spacing = unit(0.5, "lines")
)
p_left <-
ggplot(filter(df1, x == "a"), aes(x = x, y = y)) +
geom_tile(aes(fill = z), colour = "grey20") +
scale_x_discrete(expand = c(0, 0)) +
scale_fill_distiller(palette = "RdBu", limits = c(-1, 1.5)) + # You need to set the same scale limits
facet_grid(.~facet, scales = "free_x", space = "free") +
theme(plot.margin = margin(r = 0.4, unit = "lines"),
axis.title.y = element_text(margin = margin()))
p_seg <-
ggplot() +
geom_segment(data = df_seg,
aes(x = x, xend = xend, y = yseg,yend = yseg), lty = "dashed",
color = "black") +
scale_x_discrete(expand = c(0, 0)) +
geom_text(data = df_txt, aes(x = x, y = y, label = label), hjust = 0.5) +
theme_void() # important short cut to get rid of "everything but... "
p_seg + p_left + p_right +
plot_layout(nrow = 1, guides = "collect",
widths = c(2, 1, 3)) &
# the theme call in patchwork sets theme options globally, for all plots.
theme(strip.text.x = element_blank(),
axis.title.x = element_blank())
There is now a gap between segments and plots, so not quite the expected result, but visually fairly close. Also it requires (too much?) trial and error with the right margins / relative plot widths to get everything to look nice... On the other hand, this option probably gives you the biggest flexibility and you don't need to worry too much of the effects of scale expansion. So I personally would prefer the option. Would probably create four separate plots for full control of the gaps.
The last option would be to mess with the grobs, which I admit I am not very good at, so I will leave it with those two options. Hope this helped.