【问题标题】:Perfectly align several plots完美对齐多个图
【发布时间】:2016-12-02 21:33:59
【问题描述】:

我的目标是一个复合图,它结合了一个散点图和 2 个密度估计图。我面临的问题是由于缺少密度图的轴标记和散点图的图例,密度图与散点图没有正确对齐。可以通过使用plot.margin 来调整它。但是,这不是一个更可取的解决方案,因为如果对绘图进行更改,我将不得不一遍又一遍地调整它。有没有办法以某种方式定位所有绘图,以便实际绘图面板完美对齐?

我试图使代码尽可能少,但为了重现问题,它仍然很多。

library(ggplot2)
library(gridExtra)

df <- data.frame(y     = c(rnorm(50, 1, 1), rnorm(50, -1, 1)), 
                 x     = c(rnorm(50, 1, 1), rnorm(50, -1, 1)), 
                 group = factor(c(rep(0, 50), rep(1,50))))


empty <- ggplot() + 
              geom_point(aes(1,1), colour="white") +
              theme(                              
                plot.background = element_blank(), 
                panel.grid.major = element_blank(), 
                panel.grid.minor = element_blank(), 
                panel.border = element_blank(), 
                panel.background = element_blank(),
                axis.title.x = element_blank(),
                axis.title.y = element_blank(),
                axis.text.x = element_blank(),
                axis.text.y = element_blank(),
                axis.ticks = element_blank()
              )


scatter <-  ggplot(df, aes(x = x, y = y, color = group)) + 
                geom_point() +
                theme(legend.position = "bottom")

top_plot <- ggplot(df, aes(x = y)) + 
                geom_density(alpha=.5, mapping = aes(fill = group)) + 
                theme(legend.position = "none") +
                theme(axis.title.y = element_blank(),
                      axis.title.x = element_blank(),
                      axis.text.y=element_blank(),
                      axis.text.x=element_blank(),
                      axis.ticks=element_blank() )

right_plot <- ggplot(df, aes(x = x)) + 
                geom_density(alpha=.5, mapping = aes(fill = group)) + 
                coord_flip() + theme(legend.position = "none") +
                theme(axis.title.y = element_blank(),
                      axis.title.x = element_blank(),
                      axis.text.y  = element_blank(),
                      axis.text.x=element_blank(),
                      axis.ticks=element_blank())

grid.arrange(top_plot, empty, scatter, right_plot, ncol=2, nrow=2, widths=c(4, 1), heights=c(1, 4))

【问题讨论】:

  • 仅供参考,您应该在 R 中采样的示例之前set.seed(),以便输出可重现
  • @Chris:实际数据在这里并不重要。所以我觉得没关系。
  • @user20650:感谢您的链接。我正在玩它,但还不能让它工作。但我认为这是要走的路。

标签: r ggplot2


【解决方案1】:

这是一个基于 R 的解决方案。它使用 this question 中的 line2user 函数。

par(mar = c(5, 4, 6, 6))
with(df, plot(y ~ x, bty = "n", type = "n"))
with(df[df$group == 0, ], points(y ~ x, col = "dodgerblue2"))
with(df[df$group == 1, ], points(y ~ x, col = "darkorange"))

x0_den <- with(df[df$group == 0, ], 
               density(x, from = par()$usr[1], to = par()$usr[2]))
x1_den <- with(df[df$group == 1, ], 
               density(x, from = par()$usr[1], to = par()$usr[2]))
y0_den <- with(df[df$group == 0, ], 
               density(y, from = par()$usr[3], to = par()$usr[4]))
y1_den <- with(df[df$group == 1, ], 
               density(y, from = par()$usr[3], to = par()$usr[4]))

x_scale <- max(c(x0_den$y, x1_den$y))
y_scale <- max(c(y0_den$y, y1_den$y))

lines(x = x0_den$x, y = x0_den$y/x_scale*2 + line2user(1, 3), 
      col = "dodgerblue2", xpd = TRUE)
lines(x = x1_den$x, y = x1_den$y/x_scale*2 + line2user(1, 3), 
      col = "darkorange", xpd = TRUE)

lines(y = y0_den$x, x = y0_den$y/x_scale*2 + line2user(1, 4), 
      col = "dodgerblue2", xpd = TRUE)
lines(y = y1_den$x, x = y1_den$y/x_scale*2 + line2user(1, 4), 
      col = "darkorange", xpd = TRUE)

【讨论】:

  • 谢谢,但我想坚持ggplot2
  • 这里需要ggplot做什么?
  • 我所有的情节都使用 ggplot,我也很好奇如何用 ggplot 完成。
【解决方案2】:

当您将轴设置为 element_blank() 时,它会移除轴并允许图表填充其余空间。相反,设置为 color = "white" (或任何你的背景):

# All other code remains the same:

scatter <-  ggplot(df, aes(x = x, y = y, color = group)) + 
  geom_point() +
  theme(legend.position = "bottom")

top_plot <- ggplot(df, aes(x = y)) + 
  geom_density(alpha=.5, mapping = aes(fill = group)) + 
  theme(legend.position = "none")+
  theme(axis.title = element_text(color = "white"),
        axis.text=element_text(color = "white"),
        axis.ticks=element_line(color = "white") )

right_plot <- ggplot(df, aes(x = x)) + 
  geom_density(alpha=.5, mapping = aes(fill = group)) + 
  coord_flip() +
  theme(legend.position = "bottom") +
  theme(axis.title = element_text(color = "white"),
        axis.text  = element_text(color = "white"),
        axis.ticks=element_line(color = "white"))

grid.arrange(top_plot, empty, scatter, right_plot, ncol=2, nrow=2, widths=c(4, 1), heights=c(1, 4))

我还必须在正确的情节中添加一个图例。如果您不希望这样,我还建议将散点图中的图例移动到情节内:

scatter <-  ggplot(df, aes(x = x, y = y, color = group)) + 
  geom_point() +
  theme(legend.position = c(0.05,0.1))

top_plot <- ggplot(df, aes(x = y)) + 
  geom_density(alpha=.5, mapping = aes(fill = group)) + 
  theme(legend.position = "none")+
  theme(axis.title = element_text(color = "white"),
        axis.text=element_text(color = "white"),
        axis.ticks=element_line(color = "white") )

right_plot <- ggplot(df, aes(x = x)) + 
  geom_density(alpha=.5, mapping = aes(fill = group)) + 
  coord_flip() +
  theme(legend.position = "none") +
  theme(axis.title = element_text(color = "white"),
        axis.text  = element_text(color = "white"),
        axis.ticks=element_line(color = "white"))

grid.arrange(top_plot, empty, scatter, right_plot, ncol=2, nrow=2, widths=c(4, 1), heights=c(1, 4))

【讨论】:

  • 这更接近我想要的,但我希望图例位于它所在的位置,并且如果散点图在 y 轴上有标签,它仍然无法在左侧正确对齐。跨度>
【解决方案3】:

使用来自Align ggplot2 plots vertically 的答案通过添加到 gtable 来对齐图(很可能过于复杂了!!)

library(ggplot2)
library(gtable)
library(grid)

您的数据和图表

set.seed(1)
df <- data.frame(y     = c(rnorm(50, 1, 1), rnorm(50, -1, 1)), 
                 x     = c(rnorm(50, 1, 1), rnorm(50, -1, 1)), 
                 group = factor(c(rep(0, 50), rep(1,50))))

scatter <-  ggplot(df, aes(x = x, y = y, color = group)) + 
                geom_point() +  theme(legend.position = "bottom")

top_plot <- ggplot(df, aes(x = y)) + 
                geom_density(alpha=.5, mapping = aes(fill = group))+
               theme(legend.position = "none") 

right_plot <- ggplot(df, aes(x = x)) + 
                geom_density(alpha=.5, mapping = aes(fill = group)) + 
                coord_flip() + theme(legend.position = "none") 

使用 Bapistes 答案中的想法

g <- ggplotGrob(scatter)

g <- gtable_add_cols(g, unit(0.2,"npc"))    
g <- gtable_add_grob(g, ggplotGrob(right_plot)$grobs[[4]], t = 2, l=ncol(g), b=3, r=ncol(g))

g <- gtable_add_rows(g, unit(0.2,"npc"), 0)
g <- gtable_add_grob(g, ggplotGrob(top_plot)$grobs[[4]], t = 1, l=4, b=1, r=4)

grid.newpage()
grid.draw(g)

哪个产生

我使用ggplotGrob(right_plot)$grobs[[4]] 手动选择panel grob,但当然您可以自动执行此操作

还有其他选择:Scatterplot with marginal histograms in ggplot2

【讨论】:

  • 我真的很喜欢gtable 的可能性。不幸的是,我不明白它的概念。有什么地方可以很好地解释它的工作方式吗?我找不到任何有用的东西。
  • 它的文档记录很差。我可以通过玩这个网站上的问题和答案来使用它一点(很少)。 Baptiste 在他的 wiki 上写了一点 github.com/baptiste/gridextra/wiki/gtable
  • 在这种情况下,它可能是新文档功能的理想选择。
  • @Alex;可能值得在 Baptiste 的答案中添加复选标记,因为它更加自动化。
  • 我同意。另一个解决方案看起来不错。不过,我现在对gtable 解决方案非常满意,因为我了解发生了什么。
【解决方案4】:

这是一个组合使用来自cowplot 包的plot_grid 和来自gridExtra 包的grid.arrange 的选项:

library(ggplot2)
library(gridExtra)
library(grid)
library(cowplot)

df <- data.frame(y     = c(rnorm(50, 1, 1), rnorm(50, -1, 1)), 
                 x     = c(rnorm(50, 1, 1), rnorm(50, -1, 1)), 
                 group = factor(c(rep(0, 50), rep(1,50))))

首先,进行一些设置:将绘图图例提取为单独的 grob 的函数,以及几个可重复使用的绘图组件:

# Function to extract legend
# https://github.com/hadley/ggplot2/wiki/Share-a-legend-between-two-ggplot2-graphs
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]]
  return(legend)
  }


# Set up reusable plot components
my_thm = list(theme_bw(),
              theme(legend.position = "none",
                    axis.title.y = element_blank(),
                    axis.title.x = element_blank(),
                    axis.text.y=element_blank(),
                    axis.text.x=element_blank(),
                    axis.ticks=element_blank()))

marg = theme(plot.margin=unit(rep(0,4),"lines"))

创建图:

## Empty plot
empty <- ggplot() + geom_blank() + marg

## Scatterplot
scatter <-  ggplot(df, aes(x = x, y = y, color = group)) + 
  geom_point() +
  theme_bw() + marg +
  guides(colour=guide_legend(ncol=2))

# Copy legend from scatterplot as a separate grob
leg = g_legend(scatter)

# Remove legend from scatterplot
scatter = scatter + theme(legend.position = "none")

## Top density plot
top_plot <- ggplot(df, aes(x = y)) + 
  geom_density(alpha=.5, mapping = aes(fill = group)) + 
  my_thm + marg

## Right density plot
right_plot <- ggplot(df, aes(x = x)) + 
  geom_density(alpha=.5, mapping = aes(fill = group)) + 
  coord_flip() + my_thm + marg

现在把三个情节加上图例布置出来:

# Lay out the three plots
p1 = plot_grid(top_plot, empty, scatter, right_plot, align="hv",
               rel_widths=c(3,1), rel_heights=c(1,3))

# Combine plot layout and legend
grid.arrange(p1, leg, heights=c(10,1))

【讨论】:

  • 谢谢!这也是我很好的解决方案,但在这种情况下我更喜欢 gtable。
【解决方案5】:

另一种选择,

library(egg) 
ggarrange(top_plot, empty, scatter, right_plot, 
          ncol=2, nrow=2, widths=c(4, 1), heights=c(1, 4))

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-10-07
    • 2012-12-26
    • 2012-10-30
    • 2014-08-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多