I am combining two distinct plots into a grid layout with grid
as suggested by @lgautier in rpy2 using python. The top plot is a density and and the bottom a ba
Aligning two plots becomes much trickier when facets are involved. I don't know if there is a general solution, even in R. Consider this scenario,
p1 <- ggplot(mtcars, aes(mpg, wt)) + geom_point() +
facet_wrap(~ cyl, ncol=2,scales="free")
p2 <- p1 + facet_null() + aes(colour=am) + ylab("this\nis taller")
gridExtra::grid.arrange(p1, p2)
With some work, you can compare the widths for the left axis, and the legends (which may or may not be present on the right side).
library(gtable)
# legend, if it exists, may be the second last item on the right,
# unless it's not on the right side.
locate_guide <- function(g){
right <- max(g$layout$r)
gg <- subset(g$layout, (grepl("guide", g$layout$name) & r == right - 1L) |
r == right)
sort(gg$r)
}
compare_left <- function(g1, g2){
w1 <- g1$widths[1:3]
w2 <- g2$widths[1:3]
unit.pmax(w1, w2)
}
align_lr <- function(g1, g2){
# align the left side
left <- compare_left(g1, g2)
g1$widths[1:3] <- g2$widths[1:3] <- left
# now deal with the right side
gl1 <- locate_guide(g1)
gl2 <- locate_guide(g2)
if(length(gl1) < length(gl2)){
g1$widths[[gl1]] <- max(g1$widths[gl1], g2$widths[gl2[2]]) +
g2$widths[gl2[1]]
}
if(length(gl2) < length(gl1)){
g2$widths[[gl2]] <- max(g2$widths[gl2], g1$widths[gl1[2]]) +
g1$widths[gl1[1]]
}
if(length(gl1) == length(gl2)){
g1$widths[[gl1]] <- g2$widths[[gl2]] <- unit.pmax(g1$widths[gl1], g2$widths[gl2])
}
grid.arrange(g1, g2)
}
align_lr(g1, g2)
Note that I haven't tested other cases; I'm sure it's very easy to break. As far as I understand from the docs, rpy2
provides a mechanism to use an arbitrary piece of R code, so the conversion should not be a problem.
Split the legends from the plots (see ggplot separate legend and plot) , then use grid.arrange
library(gridExtra)
g_legend <- function(a.gplot){
tmp <- ggplot_gtable(ggplot_build(a.gplot))
leg <- which(sapply(tmp$grobs, function(x) x$name) == "guide-box")
legend <- tmp$grobs[[leg]]
legend
}
legend1 <- g_legend(p1)
legend2 <- g_legend(p2)
grid.arrange(p1 + theme(legend.position = 'none'), legend1,
p2 + theme(legend.position = 'none'), legend2,
ncol=2, widths = c(5/6,1/6))
This is obviously the R
implementation.
Untested translation of the answer using gridExtra
's grid.arrange()
. The left sides of the plots (where the labels for the y-axis are) might not always be aligned though.
from rpy2.robjects.packages import importr
gridextra = importr('gridExtra')
from rpy2.robjects.lib import ggplot2
_ggplot2 = ggplot2.ggplot2
def dollar(x, name): # should be included in rpy2.robjects, may be...
return x[x.index(name)]
def g_legend(a_gplot):
tmp = _ggplot2.ggplot_gtable(_ggplot2.ggplot_build(a_gplot))
leg = [dollar(x, 'name')[0] for x in dollar(tmp, 'grobs')].index('guide-box')
legend = dollar(tmp, 'grobs')[leg]
return legend
legend1 = g_legend(p1)
legend2 = g_legend(p2)
nolegend = ggplot2.theme(**{'legend.position': 'none'})
gridexta.grid_arrange(p1 + nolegend, legend1,
p2 + nolegend, legend2,
ncol=2, widths = FloatVector((5.0/6,1.0/6)))