-
-
Save tomhopper/faa24797bb44addeba79 to your computer and use it in GitHub Desktop.
#' When plotting multiple data series that share a common x axis but different y axes, | |
#' we can just plot each graph separately. This suffers from the drawback that the shared axis will typically | |
#' not align across graphs due to different plot margins. | |
#' One easy solution is to reshape2::melt() the data and use ggplot2's facet_grid() mapping. However, there is | |
#' no way to label individual y axes. | |
#' facet_grid() and facet_wrap() were designed to plot small multiples, where both x- and y-axis ranges are | |
#' shared acros all plots in the facetting. While the facet_ calls allow us to use different scales with | |
#' the \code{scales = "free"} argument, they should not be used this way. | |
#' A more robust approach is to the grid package grid.draw(), rbind() and ggplotGrob() to create a grid of | |
#' individual plots where the plot axes are properly aligned within the grid. | |
#' Thanks to https://rpubs.com/MarkusLoew/13295 for the grid.arrange() idea. | |
library(ggplot2) | |
library(grid) | |
library(dplyr) | |
library(lubridate) | |
#' Create some data to play with. Two time series with the same timestamp. | |
df <- data.frame(DateTime = ymd("2010-07-01") + c(0:8760) * hours(2), series1 = rnorm(8761), series2 = rnorm(8761, 100)) | |
#' Create the two plots. | |
plot1 <- df %>% | |
select(DateTime, series1) %>% | |
na.omit() %>% | |
ggplot() + | |
geom_point(aes(x = DateTime, y = series1), size = 0.5, alpha = 0.75) + | |
ylab("Red dots / m") + | |
theme_minimal() + | |
theme(axis.title.x = element_blank()) | |
plot2 <- df %>% | |
select(DateTime, series2) %>% | |
na.omit() %>% | |
ggplot() + | |
geom_point(aes(x = DateTime, y = series2), size = 0.5, alpha = 0.75) + | |
ylab("Blue drops / L") + | |
theme_minimal() + | |
theme(axis.title.x = element_blank()) | |
grid.newpage() | |
grid.draw(rbind(ggplotGrob(plot1), ggplotGrob(plot2), size = "last")) | |
# To plot two series vertically aligned with only one labelled x-axis, | |
# we can remove the axes from the top plot and then plot the two graphs | |
# together using either the egg package or the cowplot package | |
library(ggplot2) # 3.2.1 | |
library(dplyr) | |
library(lubridate) | |
library(cowplot) # 1.0.0 | |
library(egg) # 0.4.5 | |
#' Create some data to play with. Two time series with the same timestamp. | |
df <- data.frame(DateTime = ymd("2010-07-01") + c(0:8760) * hours(2), | |
series1 = rnorm(8761), | |
series2 = rnorm(8761, 100)) | |
#' Create the two plots. | |
plot1 <- df %>% | |
select(DateTime, series1) %>% | |
na.omit() %>% | |
ggplot() + | |
geom_point(aes(x = DateTime, y = series1), size = 0.5, alpha = 0.75) + | |
ylab("Red dots / m") + | |
theme_minimal() + | |
theme(axis.title.x = element_blank(), | |
axis.text.x = element_blank()) | |
plot2 <- df %>% | |
select(DateTime, series2) %>% | |
na.omit() %>% | |
ggplot() + | |
geom_point(aes(x = DateTime, y = series2), size = 0.5, alpha = 0.75) + | |
ylab("Blue drops / L") + | |
theme_minimal() + | |
theme(axis.title.x = element_blank()) | |
# Draw the two plot aligned vertically, with the top plot 1/3 of the height | |
# of the bottom plot | |
cowplot::plot_grid(plot1, plot2, align = "v", ncol = 1, rel_heights = c(0.25, 0.75)) | |
egg::ggarrange(plot1, plot2, heights = c(0.25, 0.75)) |
Note: I had to install an extra library to get this code to work. I had to add the library(lubridate) for the ymd function to work.
Very neat. Thanks a lot, this was really helpful. I was struggling to align the y-axis in multiple graphs.
Very neat and tidy.
Thank you Tom!
Thanks, this is very helpful. I have a question. I ran grid.draw and my plot with the two aligned graphs appeared in the plot frame of RStudio. I then tried this assignment:
pdbox_spc_cms <- grid.draw(rbind(ggplotGrob(pdbox1), ggplotGrob(pdbox2), size = "last")) so that I then could use ggsave to save in the file format, dimensions and dpi that I needed. But, that assignment returned as NULL. Any suggestions as to how should I have made that assignment?
Nice! Cheers for this.
In response to mgm-cincy on March 16:
ggplot2's ggsave() function has the ability to save grid.draw().
eg.:
ggsave("File name.png", path = "File path", plot = grid.draw(rbind(ggplotGrob(plot1), ggplotGrob(plot2), size = "last")), ... )
Hope this helps!
Thanks, this is just what I needed.
Splendid! Is there a way to supress the printing of the same x axis label on each subplot except the last one?
Great solution, thanks!
Awesome! but a little bit extension: in this case, how can I seamlessly combine these two plots with only eaxcatly ONE x-axis (i.e. there is no space left between these two rows)?
Just beautiful code!
Thank you! This helps a lot!
Thannk you!! Easy and help me a lot too! 😁
Can both graphs have differents names?
I have managed to put 2 graphs in the same plot using grid.arrange : grid.arrange(graph_weight_ori, graph_weight_imp, nrow = 1)
how can I give them different names?
@ADam-Z514
It should work the same with grid.arrange. You should have to convert to Grob first and bind them together with rbind.
grid.arrange(rbind(ggplotGrob(graph_weight_ori), ggplotGrob(bpgraph_weight_imp), size = "first"))
This code is great, but is there a way that you can bind both plots together so that x-axis for the upper plot is only separated by a line so as not to show the numbers. Basically, I want two plots directly on top of each other with the same scales for the y-axis but two different variables. Is this possible? I am having trouble finding any code to do this exactly.
for some reason I got this plot to work and now the select command is giving me an error: "Error in select(., d34S, d13C) : unused arguments (d34S, d13C)". The libraries I am running in the background are:
library(tidyverse)
library(ggplot2)
library(broom)
library(sjstats)
library(plotly)
library(fastDummies)
library(MASS)
library(AICcmodavg)
library(car)
library(MASS)
library(grid)
library(dplyr)
library(lubridate)
Can anybody help me?
@bggalvenmon Some package is masking dplyr's select: use dplyr::select
@bggalvenmon Some package is masking dplyr's select: use
dplyr::select
I think there is a select
function loaded through the MASS
package - I recently had a script failing because of this issue.
If you don't want to keep calling the function direction you can add the following to the beginning of your script: select <- dplyr::select
did something like:
p1 <- ggplot(...) + geom_histogram() p2 <- ggplot(...) + geom_boxplot() + geom_point() + coord_flip() grid.newpage() grid.draw(...)
and the axis were close, but not right on.
edit: was able to get them to align by adding:
p1 <- p1 + scale_x_continuous(limits=c(x_min, x_max)) p2 <- p2 + scale_y_continuous(limits=c(x_min, x_max))
thanks!
very nice solution, especially if one wants to compile 2-by-2 histograms from separate data frames.
Thanks!
Great help, thanks very much for posting!!
Many thanks! Solved my issue.
Really nice solution!!
Thank you very much for your great help
If you want to take this:
p1 <- p1 + scale_x_continuous(limits=c(x_min, x_max)) p2 <- p2 + scale_y_continuous(limits=c(x_min, x_max))
a step further, you can do something like:
x_min=min(c(layer_scales(p1)$x$range$range[[1]],layer_scales(p2)$x$range$range[[1]]))
x_max=max(c(layer_scales(p1)$x$range$range[[2]],layer_scales(p2)$x$range$range[[2]]))
p1<-p1+scale_x_continuous(limits=c(x_min, x_max))
p2<-p2+scale_x_continuous(limits=c(x_min, x_max))
If you want to take this:
p1 <- p1 + scale_x_continuous(limits=c(x_min, x_max)) p2 <- p2 + scale_y_continuous(limits=c(x_min, x_max))
a step further, you can do something like:
x_min=min(c(layer_scales(p1)$x$range$range[[1]],layer_scales(plt_mod_cond)$x$range$range[[1]]))
x_max=max(c(layer_scales(plt_mod_all)$x$range$range[[2]],layer_scales(plt_mod_cond)$x$range$range[[2]]))
p1<-p1+scale_x_continuous(limits=c(x_min, x_max))
p2<-p2+scale_x_continuous(limits=c(x_min, x_max))
I note that in the last version the different heights code doesn't work properly. You have to get rid of the brackets:
maxWidth = grid::unit.pmax(gA$widths, gB$widths)
gA$widths <- as.list(maxWidth)
gB$widths <- as.list(maxWidth)