【问题标题】:How can I make consistent-width plots in ggplot (with legends)?如何在 ggplot 中制作一致宽度的图(带图例)?
【发布时间】:2013-04-27 18:36:48
【问题描述】:

我有几个不同的类别要绘制。这些是不同的类别,每个类别都有自己的一组标签,但在文档中组合在一起是有意义的。下面给出一些简单的堆积条形图示例:

df <- data.frame(x=c("a", "b", "c"),
                 y=c("happy", "sad", "ambivalent about life"))
ggplot(df, aes(x=factor(0), fill=x)) + geom_bar()
ggplot(df, aes(x=factor(0), fill=y)) + geom_bar()

问题在于,使用不同的标签,图例有不同的宽度,这意味着图有不同的宽度,如果我制作一张表格或\subfigure 元素,这会导致事情看起来有点傻。我该如何解决这个问题?

有没有办法明确设置绘图或图例的宽度(绝对或相对)?

【问题讨论】:

  • 另一种选择:您可以将图例放在图的顶部/底部/内部?
  • + theme(legend.position = "top")(或“底部”)(或)+ theme(legend.position=c(1,0), legend.justification=c(1,0))

标签: r ggplot2


【解决方案1】:

编辑:使用egg 包非常简单

# install.packages("egg")

library(egg)

p1 <- ggplot(data.frame(x=c("a","b","c"),
                        y=c("happy","sad","ambivalent about life")),
             aes(x=factor(0),fill=x)) + 
      geom_bar()
p2 <- ggplot(data.frame(x=c("a","b","c"),
                        y=c("happy","sad","ambivalent about life")),
             aes(x=factor(0),fill=y)) + 
      geom_bar()

ggarrange(p1,p2, ncol = 1)

原创更新至 ggplot2 2.2.1

这是一个使用 gtable 包中的函数的解决方案,并侧重于图例框的宽度。 (可以找到更通用的解决方案here。)

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

# Your plots
p1 <- ggplot(data.frame(x=c("a","b","c"),y=c("happy","sad","ambivalent about life")),aes(x=factor(0),fill=x)) + geom_bar()
p2 <- ggplot(data.frame(x=c("a","b","c"),y=c("happy","sad","ambivalent about life")),aes(x=factor(0),fill=y)) + geom_bar()

# Get the gtables
gA <- ggplotGrob(p1)
gB <- ggplotGrob(p2)

# Set the widths
gA$widths <- gB$widths

# Arrange the two charts.
# The legend boxes are centered
grid.newpage()
grid.arrange(gA, gB, nrow = 2)

如果另外,图例框需要左对齐,借用@Julius写的here的一些代码

p1 <- ggplot(data.frame(x=c("a","b","c"),y=c("happy","sad","ambivalent about life")),aes(x=factor(0),fill=x)) + geom_bar()
p2 <- ggplot(data.frame(x=c("a","b","c"),y=c("happy","sad","ambivalent about life")),aes(x=factor(0),fill=y)) + geom_bar()

# Get the widths
gA <- ggplotGrob(p1)
gB <- ggplotGrob(p2)

# The parts that differs in width
leg1 <- convertX(sum(with(gA$grobs[[15]], grobs[[1]]$widths)), "mm")
leg2 <- convertX(sum(with(gB$grobs[[15]], grobs[[1]]$widths)), "mm")

# Set the widths
gA$widths <- gB$widths

# Add an empty column of "abs(diff(widths)) mm" width on the right of 
# legend box for gA (the smaller legend box)
gA$grobs[[15]] <- gtable_add_cols(gA$grobs[[15]], unit(abs(diff(c(leg1, leg2))), "mm"))

# Arrange the two charts
grid.newpage()
grid.arrange(gA, gB, nrow = 2)

替代解决方案 gtable 包中有 rbindcbind 函数,用于将 grobs 组合成一个 grob。对于此处的图表,应使用size = "max" 设置宽度,但gtable 的CRAN 版本会引发错误。

一个选项:很明显,第二个情节中的图例更宽。因此,请使用size = "last" 选项。

# Get the grobs
gA <- ggplotGrob(p1)
gB <- ggplotGrob(p2)

# Combine the plots
g = rbind(gA, gB, size = "last")

# Draw it
grid.newpage()
grid.draw(g)

左对齐图例:

# Get the grobs
gA <- ggplotGrob(p1)
gB <- ggplotGrob(p2)

# The parts that differs in width
leg1 <- convertX(sum(with(gA$grobs[[15]], grobs[[1]]$widths)), "mm")
leg2 <- convertX(sum(with(gB$grobs[[15]], grobs[[1]]$widths)), "mm")

# Add an empty column of "abs(diff(widths)) mm" width on the right of 
# legend box for gA (the smaller legend box)
gA$grobs[[15]] <- gtable_add_cols(gA$grobs[[15]], unit(abs(diff(c(leg1, leg2))), "mm"))

# Combine the plots
g = rbind(gA, gB, size = "last")

# Draw it
grid.newpage()
grid.draw(g)

第二种选择是使用 Baptiste 的 gridExtra 包中的 rbind

# Get the grobs
gA <- ggplotGrob(p1)
gB <- ggplotGrob(p2)

# Combine the plots
g = gridExtra::rbind.gtable(gA, gB, size = "max")

# Draw it
grid.newpage()
grid.draw(g)

左对齐图例:

# Get the grobs
gA <- ggplotGrob(p1)
gB <- ggplotGrob(p2)

# The parts that differs in width
leg1 <- convertX(sum(with(gA$grobs[[15]], grobs[[1]]$widths)), "mm")
leg2 <- convertX(sum(with(gB$grobs[[15]], grobs[[1]]$widths)), "mm")

# Add an empty column of "abs(diff(widths)) mm" width on the right of 
# legend box for gA (the smaller legend box)
gA$grobs[[15]] <- gtable_add_cols(gA$grobs[[15]], unit(abs(diff(c(leg1, leg2))), "mm"))

# Combine the plots
g = gridExtra::rbind.gtable(gA, gB, size = "max")

# Draw it
grid.newpage()
grid.draw(g)

【讨论】:

  • 太棒了!这些函数和变量是否记录在任何地方,或者您只是查看了ggplot 源代码?
  • @jamie.f.olson CRAN 上有 gtable manual,但除此之外,需要注意其他人在 SO 上的示例,有时在 ggplot mailing list 上。跨度>
  • 你有没有尝试rbind()ing这些情节?那应该可以,但我不记得我们是否完成了该代码。
  • 我似乎遇到了@baptiste 在使用rbind时提到的单位问题
  • 这种方法仍然是对齐图例框的最佳方法,还是有任何新功能可以帮助解决这个问题?经常这样做似乎需要大量的手工工作。
【解决方案2】:

cowplot 包还具有用于此目的的 align_plots 函数(输出未显示),

both2 <- align_plots(p1, p2, align="hv", axis="tblr")
p1x <- ggdraw(both2[[1]])
p2x <- ggdraw(both2[[2]])
save_plot("cow1.png", p1x)
save_plot("cow2.png", p2x)

还有plot_grid,它将绘图保存到同一个文件中。

library(cowplot)
both <- plot_grid(p1, p2, ncol=1, labels = c("A", "B"), align = "v")
save_plot("cow.png", both)

【讨论】:

    【解决方案3】:

    正如@hadley 建议的那样,rbind.gtable 应该能够处理这个问题,

      grid.draw(rbind(ggplotGrob(p1), ggplotGrob(p2), size="last"))
    

    但是,理想的布局宽度应为size="max",其中doesn't cope well 具有某些类型的网格单元。

    【讨论】:

    • 是:Error in mmm &lt; each : comparison of these types is not implemented 你是什么意思?我没有在问题页面上看到错误消息的示例。
    【解决方案4】:

    偶然地,我注意到他在 cmets 中建议的 Arun 的解决方案尚未被采纳。我觉得他的简单有效的方法真的很值得说明。

    Arun 建议将图例移到顶部或底部:

    ggplot(df, aes(x=factor(0), fill=x)) + geom_bar() + theme(legend.position = "bottom")
    ggplot(df, aes(x=factor(0), fill=y)) + geom_bar() + theme(legend.position = "bottom")
    

    现在,绘图的宽度与请求的宽度相同。此外,两种情况下绘图区域的大小相同。

    如果有更多因素甚至更长的标签,则可能有必要使用图例,例如,将图例显示在两行或更多行中。 theme()guide_legend() 有几个参数来控制ggplot2 中图例的位置和外观。

    【讨论】:

      【解决方案5】:

      我根据@Sandy 的回答创建了一个小函数。

      same.size.ggplot <- function(vector.string.graph, # a vector of strings which correspond to Robject ggplot graphs
                                   reference.string.graph, # a string of a  Robject ggplot graphs where height and/or height will be taken for reference
                                   width = T, # if you wanna adapat only the width
                                   height = F # if you wanna adapat only the height
      ) {
      
        # example: same.size.ggplot(p0rep(c("a", "b"), thre), "a30") 
      
      
        which(vector.string.graph %in% reference.string.graph)
      
        newref <- ggplotGrob(get(reference.string.graph))
        ref.width <- newref$widths
        ref.height <- newref$heights
      
        assign(reference.string.graph, newref, env = parent.frame(1))
      
        for(i in seq_along(vector.string.graph)) {
          if(vector.string.graph[i] != reference.string.graph) {
            new <- ggplotGrob(get(vector.string.graph[i]))
            if( width ) {
              new$widths <- ref.width
            }
            if( height ) {
              new$heights <- ref.height
            }
            assign(vector.string.graph[i], new, env = parent.frame(1))
          }
        }
      }
      
      p1 <- ggplot(data.frame(x=c("a","b","c"),y=c("happy","sad","ambivalent about life")),aes(x=factor(0),fill=x)) + geom_bar()
      p2 <- ggplot(data.frame(x=c("a","b","c"),y=c("happy","sad","ambivalent about life")),aes(x=factor(0),fill=y)) + geom_bar()
      p3 <- ggplot(data.frame(x=c("a","b","c"),y=c("Crazy happy","sad","Just follow the flow")),aes(x=factor(0),fill=y)) + geom_bar()
      
      grid.arrange(p1, p2, p3, ncol = 1)
      
      same.size.ggplot(c("p1", "p2", "p3"), "p2") # same as same.size.ggplot(c("p2", "p3"), "p1") 
      
      grid.arrange(p1, p2, p3, ncol = 1)
      

      之前

      之后

      【讨论】:

        【解决方案6】:

        您也可以为此使用patchwork-package:

        require(ggplot2)
        require(patchwork)
        # data
        df = data.frame(x = c("a", "b", "c"),
                        y = c("happy", "sad", "ambivalent about life"))
        p1 = ggplot(df, aes(x=factor(0), fill=x)) + geom_bar()
        p2 = ggplot(df, aes(x=factor(0), fill=y)) + geom_bar()
        
        # Patchwork 1: Does it automatically
        p1 / p2
        
        # Patchwork 2: Create a list
        l = patchwork::align_patches(p1, p2)
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-05-19
          • 1970-01-01
          • 2013-08-28
          相关资源
          最近更新 更多