specify specific position for collected legend in patchwork - r

I am using the patchwork package in r to create panels of plots like:
panel<- (p1+ plot_spacer()+p2 + p3)+
plot_layout(ncol = 2) +
plot_layout(guides = "collect")
panel
I want to specify the legend to go to the empty top-right panel, more or less like this
Appreciate any pointers

For this use case patchwork provides guide_area() which could be used to place the legend:
library(patchwork)
library(ggplot2)
p1 <- p2 <- p3 <- ggplot(mtcars, aes(hp, mpg, color = factor(cyl))) +
geom_point()
p1 + guide_area() + p2 + p3 +
plot_layout(ncol = 2) +
plot_layout(guides = "collect")

as suggested by #kat, one solution is to use cowplot in combination to patchwork
library("cowplot")
library("patchwork")
legend_grob <-cowplot::get_legend(p1) #get legend
#emove legend in the original plots
p1 <- p1+theme(legend.position = "none")
p2 <- p2+theme(legend.position = "none")
p3 <- p3+theme(legend.position = "none")
# now patchwork it
p1+legend_grob+p2+p3)
plot_layout(ncol = 2)

Related

Annotate combined subplots (patchworks?) as single plots using patchwork

I'm trying to figure out how to annotate combined patchworks as if they were individual plots.
I've got one patchwork consisting of three combined plots and another single plot. The final composite plot is the first patchwork on top and the individual plot on the bottom. I have no problem getting the layout I want, but when I use plot_annotation, it gives letters to every plot, whereas what I'd like to see is an A for the top plot (patchwork of three subplots) and a B for the bottom one (just a single plot)
Here's what I'm currently doing:
library(ggplot2)
library(patchwork)
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')
top_plot = (p1 + p2 + p3)
bottom_plot = p4
combined_plot <- (top_plot / bottom_plot) + plot_annotation(tag_levels="A")
combined_plot
What I'd like to see, rather than A-D annotations, is A for the top plot (plots 1-3) and a B for the bottom one (plot 4). Is there a way to do this?
One solution is to create two complete patchwork plots, each with its own annotation. You have to put them each inside wrap_elements to declare them as complete patchworks for this to work. Thereafter you can combine them as you would combine ggplots:
# Set theme for annotations
thm <- theme(plot.title = element_text(face = 2, size = 16))
top_plot <- wrap_elements((p1 + p2 + p3) +
plot_annotation(title = "A", theme = thm))
bottom_plot <- wrap_elements(p4 + plot_annotation(title = "B", theme = thm))
top_plot / bottom_plot

Global x and y labels for plots arranged using ggplot and plot_layout with textGrob

I have created a plot with many elements using ggplot and plot_layout. I want to add common x and y axes. I would like to use textGrob to keep a common look with other plots I created this way.
MWE
library(patchwork)
library(ggplot2)
library(grid)
library(gridExtra)
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#
"
myplt <- (p1 + p2 + p3 + plot_layout(design=design, guides = "collect") &
theme(legend.position = 'right',
legend.direction = 'vertical'))
y.grob <- textGrob("My Y label",
gp=gpar(fontface="bold", fontsize=15), rot=90)
x.grob <- textGrob("My X label",
gp=gpar(fontface="bold", fontsize=15))
grid.arrange(arrangeGrob(myplt, left = y.grob, bottom = x.grob))
The above worked well for plots I arranged using plot_grid (i.e. adding the common labels). However, with the above I get either a blank plot with the correct labels, or with the MWE above the correct labels but only with Plot 3.
I also tried adding on
myplt
grid::grid.draw(grid::textGrob(y.grob))
grid::grid.draw(grid::textGrob(x.grob))
because of this answer, but that just put text[GRID text 26826] in the middle of my plot.
I did manage to get their other idea working, but the spacing was horrible and I couldn't get the font details to match what I have for other plots, so would prefer to find a solution using the textGrobs already created.
EDIT: The design had an extra row that was blank that I removed
To make grid.arrange work with a patch you have to convert it to a grob first using patchwork::patchworkGrob. Additionally there is no need for grid.arrange(arrangeGrob(...)). Just use grid.arrange:
grid.arrange(patchworkGrob(myplt), left = y.grob, bottom = x.grob)

Change color shape and edit legend in R ggplot2 object

I am trying to modify a ggplot2 object where I took an object and used the scale_colour_manual and scale_shape_manual to change the colors and shapes. However, now I'd like to only show the top two items in the legend.
As you can see in the code, I changed the first and third shapes to match each other and now the third is redundant but I still want that underlying data within the plot. The desired result would be plot2 but without the third value within the legend but with the data left within the plot.
ggplot(mtcars, aes(x=wt, y=mpg, group=as.factor(cyl))) +
geom_point(aes(shape=as.factor(cyl), color=as.factor(cyl)))
plot <- ggplot(mtcars, aes(x=wt, y=mpg, group=as.factor(cyl))) +
geom_point(aes(shape=as.factor(cyl), color=as.factor(cyl)))
plot2 <- plot +
scale_colour_manual(values = c('#999999', '#999999','#999999')) +
scale_shape_manual(values = c(0, 1, 0))
plot2
Adding breaks = ... to scale_colour_manual and scale_shape_manual should do it
plot3 <- plot +
scale_colour_manual(values = c('#999999', '#999999','#999999'),
breaks = c('4', '6')) +
scale_shape_manual(values = c(0, 1, 0),
breaks = c('4', '6'))
# https://github.com/thomasp85/patchwork
# install.packages("devtools", dependencies = TRUE)
# devtools::install_github("thomasp85/patchwork")
library(patchwork)
plot2 + plot3 +
plot_layout(ncol = 2)
Created on 2019-04-13 by the reprex package (v0.2.1.9000)

Smart association of graphs where one is faceted - ggplot2

I want to combine these two graphs :
p1 <- ggplot(iris, aes(Sepal.Length)) +
geom_density() +
facet_wrap(~ Species)
p2 <- ggplot(iris, aes(Sepal.Length)) +
geom_density()
To combine, I do :
multiplot(p1, p2, cols = 2)
But it is not the desired shape.
I would like the graph p2 has the same dimensions than others and is situated just next to the last faceted graph.
Thanks for help
Not sure if this is applicable in you generic case, but with facet_grid instead of facet_wrap, you can use the margins argument:
library(ggplot2)
ggplot(iris, aes(Sepal.Length)) +
geom_density() +
facet_grid(. ~ Species, margins = T)
If you question is more generic the answer probably lies in grid.arrange.
Something like this could be a start:
library(gridExtra)
grid.arrange(arrangeGrob(p1, p2,
widths = c(3,1),
heights = c(1,20),
layout_matrix = matrix(c(1,1,NA,2),2)))
As you can see there are several problems (different axes, top strip), but working with grid could gets complicated quickly.
This code should work:
p1 <- ggplot(iris, aes(Sepal.Length)) +
geom_density() +
ylim(limits = c(0, 1.25))+
facet_wrap(~ Species)
p2 <- ggplot(iris, aes(Sepal.Length)) +
geom_density() +
ggtitle("") + # ad empty title as place holder
labs(y = "", x = "") + # hide axis labels
ylim(limits = c(0, 1.25)) + # y axis values should be fixed in both plots
coord_fixed(ratio=20/1) + # ratio of x- and y-axis to reduce width of plot
theme(axis.ticks.y = element_blank(), axis.text.y = element_blank(), axis.line.y = element_blank(),
plot.margin=unit(c(0,0,0.65,-10), "lines")) # margin between plots = "0.65"
I fiddled a bit and used just different styling options to produce this result. If you have more plots than this one I would recommend to use one theme for all.
You can use either the multiplot function that you are already using
multiplot(p1, p2, cols = 2)
or you install the packages gridExtra and grid and use that one:
grid.arrange(p1, p2, ncol=2)
Hope this helps!

Separate y-axis labels by facet OR remove legend but keep the space

Ok, I'm stumped on a home-brew ggplot.
What I would like to do is have a three row, one column faceted plot with a different y-axis label for each facet. The units of the y-axis are all the same. This would be the most convenient, but googling tells me it may not be possible.
Alternatively, I found this solution using grid.arrange, which seems like it will work. However, I want to keep a legend only for one plot and remove it from the other two, but maintain the spacing as if it were still there so that everything lines up nice. Someone had the same problem a few years ago, but the suggested solution is depreciated and I can't sort out how to make it work in modern ggplot.
Any help is appreciated! Using facets would be easiest!
Edited to add copy of plot after using user20560's gridArrange solution below. Very nearly there, just would like to get back the box around the top and bottom facet panels!
I have assumed (possibly wrongly) that you are wanting to add separate y-axis titles rather than axis labels. [If it is the labels you want different you can use the scales argument in facet_grid]
There will be a ggplot way to do this but here are a couple of ways you could tweak the grobs yourself.
So using mtcars dataset as example
library(ggplot2)
library(grid)
library(gridExtra)
One way
p <- ggplot(mtcars, aes(mpg, wt, col=factor(vs))) + geom_point() +
facet_grid(gear ~ .)
# change the y axis labels manually
g <- ggplotGrob(p)
yax <- which(g$layout$name=="ylab")
# define y-axis labels
g[["grobs"]][[yax]]$label <- c("aa","bb", "cc")
# position of labels (ive just manually specified)
g[["grobs"]][[yax]]$y <- grid::unit(seq(0.15, 0.85, length=3),"npc")
grid::grid.draw(g)
Or using grid.arrange
# Create a plot for each level of grouping variable and y-axis label
p1 <- ggplot(mtcars[mtcars$gear==3, ], aes(mpg, wt, col=factor(vs))) +
geom_point() + labs(y="aa") + theme_bw()
p2 <- ggplot(mtcars[mtcars$gear==4, ], aes(mpg, wt, col=factor(vs))) +
geom_point() + labs(y="bb") + theme_bw()
p3 <- ggplot(mtcars[mtcars$gear==5, ], aes(mpg, wt, col=factor(vs))) +
geom_point() + labs(y="cc") + theme_bw()
# remove legends from two of the plots
g1 <- ggplotGrob(p1)
g1[["grobs"]][[which(g1$layout$name=="guide-box")]][["grobs"]] <- NULL
g3 <- ggplotGrob(p3)
g3[["grobs"]][[which(g3$layout$name=="guide-box")]][["grobs"]] <- NULL
gridExtra::grid.arrange(g1,p2,g3)
If it is the axis titles you want to add I should ask why you want a different titles - can the facet strip text not do?
Following the comments by Axeman and aosmith (thank you), here's a way to do this using the facet labels using ggplot2 version 2.2.0
library(ggplot2) # From sessionInfo(): ggplot2_2.2.0
ggplot(mtcars, aes(mpg, wt, col=factor(vs))) + geom_point() +
facet_grid(gear ~ ., switch = 'y') +
theme( axis.title.y = element_blank(), # remove the default y-axis title, "wt"
strip.background = element_rect(fill = 'transparent'), # replace the strip backgrounds with transparent
strip.placement = 'outside', # put the facet strips on the outside
strip.text.y = element_text(angle=180)) # rotate the y-axis text (optional)
# (see ?ggplot2::theme for a list of theme elements (args to theme()))
I know this is an old post, but after finding it, I could not get #user20560's response to work.
I've edited #user20560's grid.extra approach as follows:
library(ggplot2)
library(gridExtra)
library(grid)
# Create a plot for each level of grouping variable and y-axis label
p1 <- ggplot(mtcars[mtcars$gear==3, ], aes(mpg, wt, col=factor(vs))) +
geom_point() + labs(y="aa") + theme_bw()
p2 <- ggplot(mtcars[mtcars$gear==4, ], aes(mpg, wt, col=factor(vs))) +
geom_point() + labs(y="bb") + theme_bw()
p3 <- ggplot(mtcars[mtcars$gear==5, ], aes(mpg, wt, col=factor(vs))) +
geom_point() + labs(y="cc") + theme_bw()
# get the legend as a grob
legend <- ggplotGrob(p1)
legend <- legend$grobs[[which(legend$layout$name=="guide-box")]]
lheight <- sum(legend$height)
lwidth <- sum(legend$width)
# remove the legend from all the plots
p1 <- p1 + theme(legend.position = 'none')
p2 <- p2 + theme(legend.position = 'none')
p3 <- p3 + theme(legend.position = 'none')
# force the layout to the right side
layoutMat <- matrix(c(1,2,3,4,4,4),ncol = 2)
grid.arrange(p1,p2,p3,legend, layout_matrix = layoutMat, ncol = 2,
widths = grid::unit.c(unit(1,'npc') - lwidth, lwidth))
This example is somewhat specific to this particular layout. There is a more general approach on the ggplot2 wiki.
I too had trouble getting the first approach in the answer of user20560 (above) to work. This is probably because the internals of ggplot2 have evolved, and there is no guarantee that these internals should stay the same. In any case, here is a version that currently works:
library(ggplot2) # From sessionInfo(): ggplot2_2.1.0
library(grid)
p <- ggplot(mtcars, aes(mpg, wt, col=factor(vs))) + geom_point() + facet_grid(gear ~ .)
g <- ggplotGrob(p)
yax <- which(g$layout$name == "ylab")
g[["grobs"]][[yax]]$children[[1]]$label <- c('fo','bar','foobar')
g[["grobs"]][[yax]]$children[[1]]$y <- grid::unit(seq(0.15, 0.85, length=3), "npc")
grid.draw(g)
Note that this is the approach that keeps the facets and does not repeat the x-axes.

Resources