Manually position legend in Patchwork - r

I want to position a legend (common to all plots) in a blank space in a patchwork layout. From what I can find online I cannot manually position a legend using legend.position if I also use guides="collect" (but can use left, right etc.).
I have tried to use l <- get.legend and then + inset_element(l, 0.6, 0.6, 1, 1) however it doesn't understand l. I also tried mixing in + inset_element(gridExtra::tableGrob(l)) without luck.
My goal is to place the legend in the blank space. My actual patchwork plot is more complicated but has two blank spaces I want the legend to sit in.
MWE
library(patchwork)
library(ggplot2)
p1 <- ggplot(mtcars) +
geom_point(aes(mpg, disp, color = mpg)) +
ggtitle('Plot 1')
p2 <- ggplot(mtcars) +
geom_boxplot(aes(gear, disp, group = gear)) +
ggtitle('Plot 2')
p3 <- ggplot(mtcars) +
geom_point(aes(hp, wt, colour = mpg)) +
ggtitle('Plot 3')
design <- "
1111
223#
"
p1 + p2 + p3 + plot_layout(guides = 'collect') + plot_layout(design=design, guides = "collect") &
theme(legend.position = 'right',
legend.direction = 'vertical')

Alter your design object to include a fourth element and use guide_area() to place the guide.
library(patchwork)
library(ggplot2)
design <- "
1111
2234
"
p1 + p2 + p3 + guide_area() + plot_layout(design=design, guides = "collect")

Related

In R grid package , how to use viewport for merge ggplot2 plots

I want to merge geom_point() and geom_boxplot() into one plot as attached image.Below code can't work.Anyone can help on this? Thanks!
library(grid)
library(ggplot2)
grid.newpage()
vp <- viewport(x=0.5,y=0.5,width = 1,height = 1)
push.Viewport(vp)
ggplot(mtcars) + geom_point(aes(mpg, disp))
vp_sub <- viewport(x=0.5,y=0.7,width=0.3,height=0.3)
push.viewport(vp_sub)
ggplot(mtcars) + geom_boxplot(aes(gear, disp, group = gear))
Besides patchwork::inset_element a second option would be to add your boxplot via ggplot2::annotation_custom. However, in contrast to patchwork::inset_element you have to set the positions in absolute coordinates of the data range of your main plot:
library(ggplot2)
bp <- ggplot(mtcars) + geom_boxplot(aes(gear, disp, group = gear))
base <- ggplot(mtcars) +
geom_point(aes(mpg, disp))
base +
annotation_custom(grob = ggplotGrob(bp), xmin = 22.5, ymin = 250)
Maybe you can use the patchwork package, there's a section that describes your problem exactly.
library(tidyverse)
library(patchwork)
p1 <- ggplot(mtcars) + geom_point(aes(mpg, disp))
p2 <- ggplot(mtcars) + geom_boxplot(aes(gear, disp, group = gear))
p1 + inset_element(
p2,
left = 0.5,
bottom = 0.5,
right = unit(1, 'npc') - unit(1, 'cm'),
top = unit(1, 'npc') - unit(1, 'cm')
)
Using viewport you could accomplish your task this way. If you want to save in a png then just comment out the line #png("my_plot.png")
library(grid)
library(ggplot2)
grid.newpage()
p1 <- ggplot(mtcars) + geom_point(aes(mpg, disp))
p2 <- ggplot(mtcars) + geom_boxplot(aes(gear, disp, group = gear))
vp <- viewport(x=0.5,y=0.5,width = 1,height = 1)vp_sub <- viewport(x=0.73,y=0.8,width=0.4,height=0.3)
#png("my_plot.png")
print(p1, vp=vp)
print(p2, vp=vp_sub)
dev.off()

Patchwork doesn't recognize plots with ggMargnial distribution elements

Trying to display 2 plots with ggMarginal but the plots are not recognized as such.
I'm pasting a simplified version of the code below where:
Before adding the ggMarginal element Patchwork displays ggplots side by side as expected
After adding marginal distributions with ggMarginal, Patchwork doesn't seem to recognize the the plot any more. The error message that I get is the following:
p1m + p2m
#> Error in p1m + p2m: non-numeric argument to binary operator
Here's the code I'm running:
library(ggplot2)
library(ggExtra)
library(patchwork)
p1 <- ggplot(mtcars) +
geom_point(aes(mpg, disp)) +
ggtitle('Plot 1')
p1m <- ggMarginal(p1,
type = "density",
size = 3)
p2 <- ggplot(mtcars) +
geom_point(aes(hp, wt, colour = mpg)) +
ggtitle('Plot 3')
p2m <- ggMarginal(p2,
type = "density",
size = 3)
p1+p2
p1m + p2m
#> Error in p1m + p2m: non-numeric argument to binary operator
Thanks a lot for your help!
Created on 2021-10-09 by the reprex package (v2.0.1)
You can wrap your marginal plots in patchwork::wrap_elements()
patchwork::wrap_elements(p1m) + patchwork::wrap_elements(p2m)
Not sure whether there is an easy option to make patchwork work with objects of class ggMarginal.
Another option to add side plots would be to make use of the ggside package which works fine with patchwork. One drawback of ggside is that (to the best of my knowledge) as of the moment it does not offer any options to style the side plots, i.e. it will inherit the style of the main plot's theme:
library(ggplot2)
library(ggside)
#> Registered S3 method overwritten by 'ggside':
#> method from
#> +.gg ggplot2
library(patchwork)
p1 <- ggplot(mtcars, aes(mpg, disp)) +
geom_point() +
geom_xsidedensity(aes(y = after_stat(density)), position = "stack") +
geom_ysidedensity(aes(x = after_stat(density)), position = "stack") +
ggtitle('Plot 1') +
theme(ggside.panel.scale = .5)
p2 <- ggplot(mtcars, aes(hp, wt, colour = mpg)) +
geom_point() +
geom_xsidedensity(aes(y = after_stat(density)), position = "stack") +
geom_ysidedensity(aes(x = after_stat(density)), position = "stack") +
ggtitle('Plot 3') +
theme(ggside.panel.scale = .5,)
p1 + p2
A second but cumbersome option would be to make the marginal plots via ggplot2 too and glue all together using patchwork:
p1 <- ggplot(mtcars) +
geom_point(aes(mpg, disp)) +
ggtitle('Plot 1')
p2 <- ggplot(mtcars) +
geom_point(aes(mpg, disp, colour = mpg)) +
ggtitle('Plot 3')
p1ma <- ggplot(mtcars) +
geom_density(aes(mpg)) +
theme_void()
p1mb <- ggplot(mtcars) +
geom_density(aes(y = disp)) +
theme_void()
wrap_plots(list(p1ma, plot_spacer(), p1ma, plot_spacer(), p1, p1mb, p2, p1mb),
widths = c(2, 1, 2, 1), heights = c(1, 2),
ncol = 4, nrow = 2, byrow = TRUE) &
theme(legend.position = "bottom")

Controlling widths of many patchworked ggplots

I'm trying to make a graph with several ggplot2 graphs combined via patchwork.
I want first a shared y-axis for plot 1 and 3. Then plot 1 and 3 and at the end plot 2 and 4. This I have achieved with the help from #Allan Cameron - see the plot. Unfortunately I cannot control the width of plot 1,3,2 and 4. I would like plot 1 and 3 to be wider than plot 2 and 4. Also, for some reason the legend ends up in the middle of the plots. How can I put it all the way to the right?
Any ideas? All help is much appreaciated!
Here's the code:
mtcars
library(ggplot2)
library(patchwork)
p1 <- ggplot(mtcars) +
geom_point(aes(disp, wt, colour = mpg)) +
ggtitle('Plot 1')
p2 <- ggplot(mtcars) +
geom_point(aes(carb, wt)) +
ggtitle('Plot 2')
p3 <- ggplot(mtcars) +
geom_point(aes(hp, wt, colour = mpg)) +
ggtitle('Plot 3')
p4 <- ggplot(mtcars) +
geom_area(aes(gear, carb)) +
ggtitle('Plot 4')
# Patchwork graph with shared y-axis
y_axis <- ggplot(data.frame(l = p1$labels$y, x = 1, y = 1)) +
geom_text(aes(x, y, label = l), angle = 90) +
theme_void() +
coord_cartesian(clip = "off")
p1$labels$y <- p2$labels$y <- " "
y_axis + (p1 + p2) / (p3 + p4) + plot_layout(widths = c(1, 15, 5), guides = "collect")
With regards to the widths issue, the nesting you do -for example (p1 + p1)- causes the nested objects to respond differently. Instead you can use the design argument in plot_layout() to achieve the same, but responsive to the widths.
library(ggplot2)
library(patchwork)
p1 <- ggplot(mtcars) +
geom_point(aes(disp, wt, colour = mpg)) +
ggtitle('Plot 1')
p2 <- ggplot(mtcars) +
geom_point(aes(carb, wt)) +
ggtitle('Plot 2')
p3 <- ggplot(mtcars) +
geom_point(aes(hp, wt, colour = mpg)) +
ggtitle('Plot 3')
p4 <- ggplot(mtcars) +
geom_area(aes(gear, carb)) +
ggtitle('Plot 4')
# Patchwork graph with shared y-axis
y_axis <- ggplot(data.frame(l = p1$labels$y, x = 1, y = 1)) +
geom_text(aes(x, y, label = l), angle = 90) +
theme_void() +
coord_cartesian(clip = "off")
p1$labels$y <- p2$labels$y <- " "
y_axis + p1 + p2 + p3 + p4 +
plot_layout(widths = c(1, 15, 5),
guides = "collect",
design = "
123
145
")
Created on 2020-12-16 by the reprex package (v0.3.0)
Small note, you're deleting the y-axis lable of p2, whereas I think you meant to delete it from p3.
The patchwork can control the width of each part by iteral call the "plot_layout" in the sub-plot, just like :
patchwork <- (p1+p2)/(p3) + plot_layout(guides = "collect") while you want to make p1 wider than p2, you could try :
patchwork <- (p1+p2 + plot_layout(widths = c(2, 1), guides = "collect"))/(p3) + plot_layout(guides = "collect")

Merging two y-axes titles in patchwork

Any ideas as to how I can "merge" two identical y-axes titles into one, and then place this y-axis title in the middle between the plot? I have succeded in merging legends by using plot_layout(guides = "collect") but I cannot seem to find anything similar for axes. In this case I would merge the two axes titles called disp_disp_disp into one.
mtcars
library(ggplot2)
library(patchwork)
p1 <- ggplot(mtcars) +
geom_point(aes(mpg, disp)) +
labs(x = "mpg", y = "disp_disp_disp_disp_disp")
p2 <- ggplot(mtcars) +
geom_boxplot(aes(gear, disp, group = gear)) +
labs(x = "gear", y = "disp_disp_disp_disp_disp")
p3 <- ggplot(mtcars) +
geom_point(aes(hp, wt, colour = mpg)) +
ggtitle('Plot 3')
p1 / (p2 | p3)
I guess it would be slightly easier to strip out the y axis title before the plot is built then draw it back on after it is plotted:
library(ggplot2)
library(patchwork)
p1 <- ggplot(mtcars) +
geom_point(aes(mpg, disp)) +
labs(x = "mpg", y = "disp_disp_disp_disp_disp")
p2 <- ggplot(mtcars) +
geom_boxplot(aes(gear, disp, group = gear)) +
labs(x = "gear", y = "disp_disp_disp_disp_disp")
p3 <- ggplot(mtcars) +
geom_point(aes(hp, wt, colour = mpg)) +
ggtitle('Plot 3')
ylab <- p1$labels$y
p1$labels$y <- p2$labels$y <- " "
p1 / (p2 | p3)
grid::grid.draw(grid::textGrob(ylab, x = 0.02, rot = 90))
Another option if you want to avoid getting your hands dirty with grobs altogether is to specify a text-only ggplot and add that as your axis text:
p4 <- ggplot(data.frame(l = p1$labels$y, x = 1, y = 1)) +
geom_text(aes(x, y, label = l), angle = 90) +
theme_void() +
coord_cartesian(clip = "off")
p1$labels$y <- p2$labels$y <- " "
p4 + (p1 / (p2 | p3)) + plot_layout(widths = c(1, 25))
This behaves a bit better on resizing too.
The only way I could think of is to hack this at the gtable level, but I'd also be excited to learn more convenient ways. Here is the gtable method:
library(ggplot2)
library(patchwork)
library(grid)
p1 <- ggplot(mtcars) +
geom_point(aes(mpg, disp)) +
labs(x = "mpg", y = "disp_disp_disp_disp_disp")
p2 <- ggplot(mtcars) +
geom_boxplot(aes(gear, disp, group = gear)) +
labs(x = "gear", y = "disp_disp_disp_disp_disp")
p3 <- ggplot(mtcars) +
geom_point(aes(hp, wt, colour = mpg)) +
ggtitle('Plot 3')
p123 <- p1 / (p2 | p3)
# Convert to gtable
gt <- patchworkGrob(p123)
# Stretching one y-axis title
is_yaxis_title <- which(gt$layout$name == "ylab-l")
# Find new bottom position based on gtable::gtable_show_layout(gt)
gt$layout$b[is_yaxis_title] <- gt$layout$b[is_yaxis_title] + 18
# Deleting other y-axis title in sub-patchwork
is_patchwork <- which(gt$layout$name == "patchwork-table")
pw <- gt$grobs[[is_patchwork]]
pw <- gtable::gtable_filter(pw, "ylab-l", invert = TRUE)
# Set background to transparent
pw$grobs[[which(pw$layout$name == "background")[1]]]$gp$fill <- NA
# Putting sub-patchwork back into main patchwork
gt$grobs[[is_patchwork]] <- pw
# Render
grid.newpage(); grid.draw(gt)
Created on 2020-12-14 by the reprex package (v0.3.0)
Another way to do this with gridExtra.
library(ggplot2)
library(patchwork)
library(gridExtra)
p1 <- ggplot(mtcars) +
geom_point(aes(mpg, disp)) +
labs(x = "mpg") +
theme(axis.title.y = element_blank())
p2 <- ggplot(mtcars) +
geom_boxplot(aes(gear, disp, group = gear)) +
labs(x = "gear") +
theme(axis.title.y = element_blank())
p3 <- ggplot(mtcars) +
geom_point(aes(hp, wt, colour = mpg)) +
ggtitle('Plot 3')
grid.arrange(patchworkGrob(p1 / (p2 | p3)), left = "disp_disp_disp_disp_disp")

Colour in title of patchwork of ggplots using ggtext?

How can a patchwork of ggplots be given a colourful title using ggtext?
Example
Suppose we have four plots
library(ggplot2)
library(patchwork)
library(ggtext)
p1 <- ggplot(mtcars) +
geom_point(aes(mpg, disp)) +
ggtitle('Plot 1')
p2 <- ggplot(mtcars) +
geom_boxplot(aes(gear, disp, group = gear)) +
ggtitle('Plot 2')
p3 <- ggplot(mtcars) +
geom_point(aes(hp, wt, colour = mpg)) +
ggtitle('Plot 3')
p4 <- ggplot(mtcars) +
geom_bar(aes(gear)) +
facet_wrap(~cyl) +
ggtitle('Plot 4')
These can be arranged like so
patch <- (p1 + p2) / (p3 + p4)
patch
The patchwork can be given a title like so
patch +
plot_annotation(
title = "Here is a regular title")
A single ggplot can be given a colourful title like so
p1 +
ggtitle("Here <span style='color:#953011;'><strong>is a colourful title</strong></span>") +
theme(plot.title = element_markdown(lineheight = 1.1))
How can the patchwork of ggplots be given a colourful title. Here's my unsuccessful attempt
patch +
plot_annotation(
title = "Here<span style='color:#953011;'><strong>is a colourful title</strong></span>") +
theme(plot.title = element_markdown(lineheight = 1.1))
plot_annotation has a theme argument, so you can do
#remotes::install_github("wilkelab/ggtext")
library(ggplot2)
library(patchwork)
library(ggtext)
patch <- (p1 + p2)
patch +
plot_annotation(
title = "Here <span style='color:#953011;'><strong>is a colourful title</strong></span>",
theme = theme(plot.title = element_markdown(lineheight = 1.1)))

Resources