左对齐两条图形边缘(ggplot)

112

我正在使用ggplot并且有两个图形要叠加显示。我使用gridExtra的grid.arrange将它们堆叠在一起。问题是无论轴标签如何,我都希望图形的左侧边缘对齐以及右侧边缘对齐。(问题出现在一个图形的标签很短,而另一个则很长)。

问题:
我该怎么做呢?我不一定非要用grid.arrange,但必须使用ggplot2。

我尝试过:
我尝试通过调整宽度和高度以及ncol和nrow来制作2x2网格,并将视觉效果放置在相反的角落,然后调整宽度,但是我无法将视觉效果放置在相反的角落。

require(ggplot2);require(gridExtra)
A <- ggplot(CO2, aes(x=Plant)) + geom_bar() +coord_flip() 
B <- ggplot(CO2, aes(x=Type)) + geom_bar() +coord_flip() 
grid.arrange(A, B, ncol=1)

在此输入图片描述


2
这里有两个可能的选项:这里这里 - joran
@Joran 我想让左轴对齐。我不认为这些会做到。但我希望我是错的。 - Tyler Rinker
9个回答

150

试一下这个,

 gA <- ggplotGrob(A)
 gB <- ggplotGrob(B)
 maxWidth = grid::unit.pmax(gA$widths[2:5], gB$widths[2:5])
 gA$widths[2:5] <- as.list(maxWidth)
 gB$widths[2:5] <- as.list(maxWidth)
 grid.arrange(gA, gB, ncol=1)

编辑

这里是一个更通用的解决方案(适用于任意数量的图形),使用gridExtra中包含的经过修改的rbind.gtable版本。

gA <- ggplotGrob(A)
gB <- ggplotGrob(B)
grid::grid.newpage()
grid::grid.draw(rbind(gA, gB))

3
漂亮而非常简单明了。感谢您提供的解决方案。 - Tyler Rinker
1
完美的解决方案!我一直在寻找这样的东西来对齐多个单独的时间序列图,因为每个图中都有很大的自定义内容,所以无法使用faceting。 - wahalulu
具体而言,请查看 gA$layout 以了解定义了哪些视口高度和它们将包含哪些 grobs。 - baptiste
4
谢谢你的解决方案,Baptiste。但是,当其中一个图形是 tableGrob 时,我无法使其正常工作。gtable::cbind 给了我一个令人失望的错误:nrow(x) == nrow(y) is not TRUE。有什么建议吗? - Gabra
2
这个解决方案对我有用,但我正在尝试理解它。[2:5]代表什么? - Hurlikus
显示剩余14条评论

41

我希望将此推广到任意数量的绘图。以下是使用Baptiste方法的逐步解决方案:

plots <- list(A, B, C, D)
grobs <- list()
widths <- list()
收集每个图的每个grob的宽度。
for (i in 1:length(plots)){
    grobs[[i]] <- ggplotGrob(plots[[i]])
    widths[[i]] <- grobs[[i]]$widths[2:5]
}

使用 do.call 获取最大宽度。

maxwidth <- do.call(grid::unit.pmax, widths)
将最大宽度分配给每个grob。
for (i in 1:length(grobs)){
     grobs[[i]]$widths[2:5] <- as.list(maxwidth)
}

绘图

do.call("grid.arrange", c(grobs, ncol = 1))

2
即使图例宽度不同,也能正常工作 - 非常好! - Keith Hughitt

35

使用 cowplot 包:

A <- ggplot(CO2, aes(x = Plant)) + geom_bar() + coord_flip() 
B <- ggplot(CO2, aes(x = Type)) + geom_bar() + coord_flip() 

library(cowplot)

plot_grid(A, B, ncol = 1, align = "v")

在此输入图片描述


12

http://rpubs.com/MarkusLoew/13295上有一个非常简单的解决方案可用(最后一项)。

require(ggplot2);require(gridExtra)
A <- ggplot(CO2, aes(x=Plant)) + geom_bar() +coord_flip() 
B <- ggplot(CO2, aes(x=Type)) + geom_bar() +coord_flip() 
grid.draw(rbind(ggplotGrob(A), ggplotGrob(B), size="first"))

你也可以将此用于宽度和高度:

require(ggplot2);require(gridExtra)
A <- ggplot(CO2, aes(x=Plant)) + geom_bar() +coord_flip() 
B <- ggplot(CO2, aes(x=Type)) + geom_bar() +coord_flip() 
C <- ggplot(CO2, aes(x=conc)) + geom_bar() +coord_flip()
D <- ggplot(CO2, aes(x=uptake)) + geom_bar() +coord_flip() 
grid.draw(cbind(
            rbind(ggplotGrob(A), ggplotGrob(B), size="first"),
            rbind(ggplotGrob(C), ggplotGrob(D), size="first"),
            size='first'))

2
使用 size="first" 意味着如果第二个图比第一个图大,对齐效果不会很好。 - baptiste

10

egg 包将 ggplot 对象包装成标准化的 3x3 gtable,使得可以对任意的 ggplots 进行面板对齐,包括分面绘图。

library(egg) # devtools::install_github('baptiste/egg')
library(ggplot2)

p1 <- ggplot(mtcars, aes(mpg, wt, colour = factor(cyl))) +
  geom_point() 

p2 <- ggplot(mtcars, aes(mpg, wt, colour = factor(cyl))) +
  geom_point() + facet_wrap( ~ cyl, ncol=2, scales = "free") +
  guides(colour="none") +
  theme()

ggarrange(p1, p2)

输入图像描述


对我来说,这可以正确地水平排列一个简单的热图(geom_tile),并在底部放置图例,以及一个多面热图(facet_gridgeom_tile),但未能对第三个图进行高度对齐,该图是一个树状图(geom_segment)。然而,cowplot或gridExtra::grid.arrange甚至无法完成前者,因此到目前为止,这是最好的解决方案。 - deeenes

8

这里是使用reshape2包中的meltfacet_wrap的另一种可能的解决方案:

library(ggplot2)
library(reshape2)

dat = CO2[, c(1, 2)]
dat$id = seq(nrow(dat))
mdat = melt(dat, id.vars="id")

head(mdat)
#   id variable value
# 1  1    Plant   Qn1
# 2  2    Plant   Qn1
# 3  3    Plant   Qn1
# 4  4    Plant   Qn1
# 5  5    Plant   Qn1
# 6  6    Plant   Qn1

plot_1 = ggplot(mdat, aes(x=value)) + 
         geom_bar() + 
         coord_flip() +
         facet_wrap(~ variable, nrow=2, scales="free", drop=TRUE)

ggsave(plot=plot_1, filename="plot_1.png", height=4, width=6)

enter image description here


这个解决方案假设每列中有相同数量的行。在我的 MRWE 中是正确的,但在现实中并非如此。 - Tyler Rinker
我不确定我理解了:你的意思是CO2$Plant和CO2$Type恰好具有相同的长度,但你的实际数据并不是这样吗? - bdemarest
这是两个不同的数据集,共享一个变量,因此行数不相同。 - Tyler Rinker

4

拼布包默认处理此问题:

library(ggplot2)
library(patchwork)

A <- ggplot(CO2, aes(x = Plant)) + geom_bar() + coord_flip() 
B <- ggplot(CO2, aes(x = Type)) + geom_bar() + coord_flip() 

A / B

reprex package (v0.3.0)于2019-12-08创建


1

我知道这是一个旧帖子,而且已经有答案了,但我可以建议将@baptiste的方法与purrr结合起来,使其更加美观:

library(purrr)
list(A, B) %>% 
  map(ggplotGrob) %>% 
  do.call(gridExtra::gtable_rbind, .) %>% 
  grid::grid.draw()

0

最好的情况下这只是一个hack:

library(wq)
layOut(list(A, 1, 2:16),  list(B, 2:3, 1:16))

虽然感觉非常不对劲。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接