Draw a "grid" between arranged plots - r

I'm working with 4 different plots and I'm using ggarrange() from the ggpubr-package to put them in a single plot. I've prepared an example:
library(ggpubr)
library(ggplot2)
p1 <- ggplot(iris, aes(x = Sepal.Length, y = Sepal.Width)) + geom_point() + ggtitle("Plot 1")
p2 <- ggplot(iris, aes(x = Petal.Length, y = Petal.Width)) + geom_point() + ggtitle("Plot 2")
p3 <- ggplot(iris, aes(x = Sepal.Length, y = Petal.Width)) + geom_point() + ggtitle("Plot 3")
p4 <- ggplot(iris, aes(x = Petal.Length, y = Sepal.Width)) + geom_point() + ggtitle("Plot 4") +
facet_wrap(~Species)
plot.list <- list(p1, p2, p3, p4)
ggarrange(plotlist = plot.list)
Output:
I would like to draw a border around the single plots, like so:
Is there any way to draw this border? Thanks!

grid.polygon() is quite manual but I think it can do the trick:
Using RStudio
library("ggpubr")
library(ggplot2)
library(gridExtra)
library(grid)
p1 <- ggplot(iris, aes(x = Sepal.Length, y = Sepal.Width)) + geom_point() + ggtitle("Plot 1")
p2 <- ggplot(iris, aes(x = Petal.Length, y = Petal.Width)) + geom_point() + ggtitle("Plot 2")
p3 <- ggplot(iris, aes(x = Sepal.Length, y = Petal.Width)) + geom_point() + ggtitle("Plot 3")
p4 <- ggplot(iris, aes(x = Petal.Length, y = Sepal.Width)) + geom_point() + ggtitle("Plot 4") +
facet_wrap(~Species)
plot.list <- list(p1, p2, p3, p4)
ggarrange(plotlist = plot.list)
x = c(0, 0.5, 1, 0.5, 0.5, 0.5)
y = c(0.5, 0.5, 0.5,0, 0.5, 1)
id = c(1,1,1,2,2,2)
grid.polygon(x,y,id)
Using Shiny (Edit)
When doing it within a shiny-app, ones needs to add the grid using annotation_custom(), as follows:
ggarrange(plotlist = plot.list) +
annotation_custom(
grid.polygon(c(0, 0.5, 1, 0.5, 0.5, 0.5),
c(0.5, 0.5, 0.5,0, 0.5, 1),
id = c(1,1,1,2,2,2),
gp = gpar(lwd = 1.5)))

Not sure if this will work for you, but you can just put borders around your individual plots. However, this includes borders on the outside of the layout. Your description seems like you're not opposed to that, but there are only inner gridlines in your example plot.
You could add the theme call when you create the plots; rather than editing the plot creation, I just did it to each plot in the list before sticking them all together.
library(ggpubr)
library(ggplot2)
#### same plot creation here ######
plot.list <- lapply(list(p1, p2, p3, p4),
function(p) p + theme(plot.background = element_rect(color = "black")))
ggarrange(plotlist = plot.list)

Related

R adding word in the middle of grid arrange

I am writing R ggplot, and I am arranging multiple plots with grid.arrange.
Is there a way to add some words in in between two plots?
I want the output to be like the red word.
Thank you for your help :)
library(ggplot2)
library(gridExtra)
P1 <- ggplot(mtcars, aes(x = mpg)) +
geom_histogram()
P2 <- ggplot(mtcars, aes(x = wt)) +
geom_histogram()
grid.arrange(P1, *I want to add some information here*,P2, ncol = 1, nrow = 2)
You could use the grid.text function from grid library as follows
### Libraries
library(grid)
library(ggplot2)
library(gridExtra)
### Data
data(cars)
### Initiating plots
P1 <- ggplot(mtcars, aes(x = mpg)) +
geom_histogram()
P2 <- ggplot(mtcars, aes(x = wt)) +
geom_histogram()
### Display plots
grid.arrange(P1, P2, ncol = 1, nrow = 2)+
grid.text("I want to add some information here",
x=unit(0.25, "npc"),
y=unit(.52, "npc"),
gp=gpar(fontsize=20, col="red"))
One approach would be to create another ggplot with only text that you want and use it in cowplot::plot_grid
library(ggplot2)
P1 <- ggplot(mtcars, aes(x = mpg)) + geom_histogram()
P2 <- ggplot() +
annotate("text", x = 4, y = 25, size=8,
label = "This is some text in the middle", color = "red") +
theme_void()
P3 <- ggplot(mtcars, aes(x = wt)) + geom_histogram()
cowplot::plot_grid(P1, P2, P3, rel_heights = c(1/2, 1/12, 1/2),
align = "v", nrow = 3)

Make ggplot panels the same height in ggarrange

I have two ggplots that I'm combining with ggarrange(). One plot has long labels, that I wrap with scale_x_discrete(labels = function(x) str_wrap(x, width = 10)), where str_wrap() is from the stringr package. However, when I combine them, the first panel is reduced in height to accommodate for the space required by the wrapped labels. I already tried all kinds of variations of adjusting the margins both in theme(plot.margin = margin()) as well as axis.text.x=element_text(margin = unit(c(), "cm")), to no avail. I'm probably missing something very obvious, but I just can't get the panels to match in height, regardless of how much space is needed by the axis labels.
Thanks!
plot1 <- ggplot(data=dat, aes(x=x, y=y)) +
theme_bw() +
geom_point(size=2) +
labs(x='', y='', title='') +
scale_x_discrete(labels = function(x) str_wrap(x, width = 10))
plot2 <- ggplot(data=dat, aes(x=x2, y=y2)) +
theme_bw() +
geom_point(size=2) +
labs(x='', y='', title='')
ggarrange(plot1, plot2, ncol=2)
it's doable within ggarrange using the argument align.
Here using the iris example from above
# The plots
plot1 <- ggplot(iris, aes(x = Species, y = Sepal.Length)) +
geom_boxplot() +
scale_x_discrete(labels = c("setosa" = "Setosa", "versicolor" = "Versicolor", "virginica" = "A very long name for \n Vircinica just to reproduce \n the problem"))
plot2 <- ggplot(iris, aes(x = Sepal.Length, fill = Species)) +
geom_density(alpha = 0.8) + theme(legend.position = "none")
Default:
ggarrange(plot1, plot2, ncol = 2)
With align:
ggarrange(plot1, plot2, ncol = 2, align = "h")
Apparently, I'm not allowed to post images so you'll have to click on the link or run the code.
Hope that helps!
One option to stick with ggarrange is to adjust the heights of your plots. To illustrate the case with iris data:
plot1 <- ggplot(iris, aes(x = Species, y = Sepal.Length)) +
geom_boxplot() +
scale_x_discrete(labels = c("setosa" = "Setosa", "versicolor" = "Versicolor", "virginica" = "A very long name for \n Vircinica just to reproduce \n the problem"))
plot2 <- ggplot(iris, aes(x = Sepal.Length, fill = Species)) +
geom_density(alpha = 0.8) + theme(legend.position = "none")
ggarrange(plot1, plot2, ncol = 2)
You can then use ggplotGrob and grid::unit.pmax to get the same hight for both plots:
pg1 <- ggplotGrob(plot1)
pg2 <- ggplotGrob(plot2)
maxHeight = grid::unit.pmax(pg1$heights, pg2$heights)
pg1$heights <- as.list(maxHeight)
pg2$heights <- as.list(maxHeight)
ggarrange(pg1, pg2, ncol = 2)

Combine and merge legends in ggplot2 with patchwork (discrete/categorical data)

I arranged 3 ggplot2 plots into a single figure by using the functionality of package patchwork. I tried to collect the legends and they appeared one next to the other. But still, they are 3 separate legends and I expected a single legend. So how can I merge the legends that contain identical values of the same factor variable into a single legend?
Notes:
And I do not want to remove the legends of separate plots by using, e.g., theme(legend.position = "none") in case some additional factor level appears. I expect patchwork specific solution.
A similar question was answered in Combine and merge legends in ggplot2 with patchwork but the data was continuous. And in my case, I have categorical data.
The code:
library(ggplot2)
library(patchwork)
iris_1 <-
ggplot(iris, aes(x = Sepal.Length, fill = Species, color = Species)) +
geom_density(alpha = 0.3, adjust = 1.5)
iris_2 <-
ggplot(iris, aes(x = Sepal.Length, y = Sepal.Width, color = Species)) +
geom_point()
iris_3 <-
ggplot(iris, aes(x = Species, y = Sepal.Width, fill = Species)) +
geom_boxplot()
(iris_1 + iris_2 + iris_3) + plot_layout(guides = "collect")
Created on 2020-10-14 by the reprex package (v0.3.0)
Update
I tried using the same aesthetic mappings (fill = Species and color = Species) as it was proposed in the comments below but it had no effect:
library(tidyverse)
library(patchwork)
iris_1 <-
ggplot(iris, aes(x = Sepal.Length, color = Species, fill = Species)) +
geom_density(alpha = 0.3, adjust = 1.5)
iris_2 <-
ggplot(iris, aes(x = Sepal.Length, y = Sepal.Width, color = Species, fill = Species)) +
geom_point()
iris_3 <-
ggplot(iris, aes(x = Species, y = Sepal.Width, color = Species, fill = Species)) +
geom_boxplot(color = "black")
(iris_1 + iris_2 + iris_3) + plot_layout(guides = "collect")
Created on 2020-10-14 by the reprex package (v0.3.0)
Unfortunately setting the same aes is only one condition. patchwork will merge legends only if they are identical. Therefore we have to ensure that the legends are the same for each plot. To this end I add a guides layer which makes the look of each legend the same by setting color, shape, size and alpha. Additionally we have to choose the same glyph for each geom using argument key_glyph. After these adjustments the three legends get merged into one.
library(ggplot2)
library(patchwork)
g <- guides(fill = guide_legend(override.aes = list(color = scales::hue_pal()(3),
shape = c(16, 16, 16),
size = c(1, 1, 1),
alpha = c(1, 1, 1)),))
iris_1 <-
ggplot(iris, aes(x = Sepal.Length)) +
geom_density(aes(fill = Species, color = Species), key_glyph = "point", alpha = 0.3, adjust = 1.5) +
g
iris_2 <-
ggplot(iris, aes(x = Sepal.Length, y = Sepal.Width)) +
geom_point(aes(fill = Species, color = Species), key_glyph = "point") +
g
iris_3 <-
ggplot(iris, aes(x = Species, y = Sepal.Width)) +
geom_boxplot(aes(fill = Species, color = Species), key_glyph = "point") +
scale_color_manual(values = c("black", "black", "black")) +
g
(iris_1 + iris_2 + iris_3) + plot_layout(guides = "collect")

Label each plot using ggpubr - R

I aranged a set of R plots into one using ggarange, How can I label them A,B and C at the top left of each subplot?
Code:
library(ggplot2)
library(ggpubr)
# default ggplot2 theme
P1 <- ggplot(iris, aes(Sepal.Length, Petal.Width, color = Species)) +
geom_point()
P2 <- ggplot(iris, aes(Sepal.Length, Sepal.Width, color = Species)) +
geom_point()
P3 <- ggplot(iris, aes(Petal.Length, Sepal.Width, color = Species)) +
geom_point()
ggarrange(P1, P2, P3, ncol=3, nrow=1, common.legend = TRUE, legend="bottom")
Use labels:
ggarrange(P1, P2, P3, labels = c("A", "B", "C"),
ncol = 3, nrow = 1,
common.legend = TRUE, legend = "bottom")

Adding a grob to patchwork plot R

I have a function that returns a patchwork plot and I can't make any changes to. I would like to add a rectGrob() on top of it. When I try to do this I remove two of the plots.
library(gridExtra)
library(patchwork)
library(ggplot2)
p1 <- ggplot(iris, aes(x = Sepal.Length, y = Sepal.Width)) + geom_point()
p2 <- ggplot(iris, aes(x = Sepal.Length, y = Sepal.Width, color = Species)) + geom_point()
p3 <- ggplot(iris, aes(x = Sepal.Length, y = Sepal.Width), col = 'blue') + geom_point()
p <- p1 + p2 + p3
grob_top <- grobTree(rectGrob(gp=gpar(fill='#F0F0F0',col= 'black')), textGrob('P1,P2, P3'))
grid.arrange(grob_top, p, heights = c(0.1, 0.9))
Using patchwork::wrap_elements() works better than gridExtra::grid.arrange()
patchwork::wrap_elements(grob_top) /
patchwork::wrap_elements(p) /
patchwork::wrap_elements(p) +
patchwork::plot_layout(ncol = 1, heights = c(0.1, 0.45, 0.45))
I think you want:
grid.arrange(grob_top,
p,
nrow = 2,
heights = c(0.1, 0.9))

Resources