I am trying to create a global/spanning Y-axis title and a global/spanning X-axis title for a plot containing several ggplot
objects arranged in a grid with clockwork
. A global axis title is one that replaces the multiple axis titles along some margin of the plot with a single title, a shown in Step 2 to Step 4 below. Although this post shows how to create a global Y axis title, it does not show how to create do so for both the X and Y axes.
My issue is that my attempt at creating a global X-axis title places it far away from the other plots and shifts the Y-axis title so it is not centered in the plot. In the following reproducible examples, I show my starting point, successful creation of one global axis, and then (starting at Step 3) my failed attempts to add more alongside what I expected to produce. Does anyone know how I can add the global x-axes close to the rest of the plot without shifting the y-axis substantially toward the top of the figure?
1. X and Y axis titles for each cell
This is my "starting" plot: it has a 2x2 layout with axis titles for each cell. I'll want to replace these with fewer axis titles.
library(ggplot2)
library(patchwork)
# Create a list of 4 plots, then render
plot_list <- rep(list(ggplot(mtcars, aes(mpg, disp)) geom_point()), 4)
wrap_plots(plot_list, nrow = 2)
2. Add a global Y-axis title
The cell-specific Y-axis titles are successfully replaced with a single, global Y-axis title. However, each cell still has its own X-axis title.
# Same list of 4 plots
plot_list <- rep(list(ggplot(mtcars, aes(mpg, disp)) geom_point()), 4)
# Loop through list to remove each plot's Y-axis
for(i in 1:length(plot_list)){
plot_list[[i]] <- plot_list[[i]] theme(axis.title.y = element_blank())
}
# Create geom_text figure with Y-axis title that will fill the first column
y_lab_big <- ggplot()
annotate(geom = "text", x = 1, y = 1,
label = "Here is some example long Y-axis text.", angle = 90)
coord_cartesian(clip = "off")
theme_void()
# Specify the text plot is NOT stacked and is much less wide
(y_lab_big | wrap_plots(plot_list, nrow = 2))
plot_layout(widths = c(.1, 1))
3. Try to add a global X-axis title
I successfully replace each cell's x-axis title with a single, global x-axis title. However, it is far from the 2x2 grid and the y-axis title is now not centered on the 2x2 grid.
plot_list <- rep(list(ggplot(mtcars, aes(mpg, disp)) geom_point()), 4)
# Loop through list to remove BOTH X-axis and Y-axis from every plot
for(i in 1:length(plot_list)){
plot_list[[i]] <- plot_list[[i]] theme(axis.title.y = element_blank(),
axis.title.x = element_blank())
}
# Create geom_text figures for *Y-axis* title (just like Example 2)
y_lab_big <-
ggplot()
annotate(geom = "text", x = 1, y = 1, label = "Here is some example long Y-axis text.", angle = 90)
coord_cartesian(clip = "off")
theme_void()
# Create geom_text figures for *X-axis* title (different angle)
x_lab_big <-
ggplot()
annotate(geom = "text", x = 1, y = 1, label = "Big X Text", angle = 0)
coord_cartesian(clip = "off")
theme_void()
# Same as Step 2 but add x-axis title using / to indicate that it should
# be stacked below the plot_list plots and specify heights to the thirst row
# (which should be the x-axis title) is relatively small.
(y_lab_big | (wrap_plots(plot_list, nrow = 2)) / x_lab_big)
plot_layout(widths = c(.1, 1),
heights = c(1,1,.1))
Ideally looks like below, with y_title centered between [1.1] and [2,1] and x_title centered between [3,2] [3,3].
matrix(c("y_title", "y_title", NA,
"scatter1","scatter2", "x_title",
"scatter3","scatter4","x_title"), nrow = 3, ncol = 3)
#> [,1] [,2] [,3]
#> [1,] "y_title" "scatter1" "scatter3"
#> [2,] "y_title" "scatter2" "scatter4"
#> [3,] NA "x_title" "x_title"
4. Adding more than one global X-axis title
Here I attempt to add a second global X-axis title so that each column receives its own x-axis title but all rows share the same axis title. The issue remains the same.
plot_list <- rep(list(ggplot(mtcars, aes(mpg, disp)) geom_point()), 4)
# Loop through list to remove BOTH X-axis and Y-axis from every plot
for(i in 1:length(plot_list)){
plot_list[[i]] <- plot_list[[i]] theme(axis.title.y = element_blank(),
axis.title.x = element_blank())
}
# Create geom_text figures for *Y-axis* title (just like Example 2)
y_lab_big <- ggplot()
annotate(geom = "text", x = 1, y = 1, label = "Here is some example long Y-axis text.", angle = 90)
coord_cartesian(clip = "off")
theme_void()
# Create geom_text figures for *X-axis* title (different angle)
x_lab_big <- ggplot()
annotate(geom = "text", x = 1, y = 1, label = "X Text 1", angle = 0)
coord_cartesian(clip = "off")
theme_void()
x_lab_big2 <- ggplot()
annotate(geom = "text", x = 1, y = 1, label = "X Text 2", angle = 0)
coord_cartesian(clip = "off")
theme_void()
# Same as previous but add x-axis title using / to indicate that it should
# be stacked below the plot_list plots and specify heights to the thirst row
# (which should be the x-axis title) is relatively small.
(y_lab_big | (wrap_plots(plot_list, nrow = 2)) / (x_lab_big | x_lab_big2))
plot_layout(widths = c(.1, 1),
heights = c(1, .1))
Ideally looks like below, with y_title centered between [1,1] and [2,1], x_title1 centered in [3,2], and x_title3 centered in [3,3].
matrix(c("y_title", "y_title", NA,
"scatter1","scatter2", "x_title1",
"scatter3","scatter4","x_title2"), nrow = 3, ncol = 3)
#> [,1] [,2] [,3]
#> [1,] "y_title" "scatter1" "scatter3"
#> [2,] "y_title" "scatter2" "scatter4"
#> [3,] NA "x_title1" "x_title2"
Although there are alternative solutions (ggplot: how to add common x and y labels to a grid of plots) I am trying to stay with using patchwork
.
CodePudding user response:
Hopefully patchwork
will gain this feature in one of the next releases. In the meanwhile one option would be to stick with patchwork
and get some support from cowplot
. Adapting my answer on R ggplot2 patchwork common axis labels to your case(s):
Note: That works nice if there is no legend. With a legend it gets probably more fiddling.
library(ggplot2)
library(patchwork)
library(cowplot)
p <- ggplot(mtcars, aes(mpg, disp))
geom_point()
plot_list <- rep(list(p labs(x = NULL, y = NULL)), 4)
p_axis <- p labs(x = "Big X Text", y = "Here is some example long Y-axis text.")
x_axis <- cowplot::get_plot_component(p_axis, "xlab-b")
y_axis <- cowplot::get_plot_component(p_axis, "ylab-l")
design = "
FAB
FCD
#EE
"
c(plot_list, list(x_axis, y_axis)) |>
wrap_plots()
plot_layout(heights = c(20, 20, 1), widths = c(1, 25, 25), design = design)
And for the e.g. two separate x titles you could do:
Note: For that case another option would be to remove the axis titles for only the top two plots.
p_axis1 <- p labs(x = "X Text 1", y = "Here is some example long Y-axis text.")
p_axis2 <- p labs(x = "X Text 2", y = "Here is some example long Y-axis text.")
x_axis1 <- cowplot::get_plot_component(p_axis1, "xlab-b")
x_axis2 <- cowplot::get_plot_component(p_axis2, "xlab-b")
design = "
GAB
GCD
#EF
"
c(plot_list, list(x_axis1, x_axis2, y_axis)) |>
wrap_plots()
plot_layout(heights = c(20, 20, 1), widths = c(1, 25, 25), design = design)