【问题标题】:Add annotation box to grid of ggplot objects将注释框添加到 ggplot 对象的网格
【发布时间】:2013-09-19 23:12:39
【问题描述】:

我正在使用grid.arrange 函数准备一个包含 37 个ggplots 的网格。为了节省轴标签当前占用的空间并添加一些信息,例如Sys.time(),我将在图形网格的右下方添加一个框。

可以在下面找到使用mtcars 数据的最小示例。真实数据将涵盖 x 轴上非常不同的范围,因此刻面不是一种选择。

有没有办法在 R 中添加如下 *.pdf 中所示的“文本框”,以使用例如添加更多信息cat 还是 print?任何提示将不胜感激。

# load needed libraries
library(ggplot2)
library(gridExtra)

# Set loop counter and create list to store objects
imax=37 
plist <- list() 

# loop to generate 37 ggplot objects
# the real example covers different ranges on x-axis so facetting
# is not an option
for(i in 1:imax){
  p <- ggplot(mtcars, aes(x = wt, y = mpg)) + geom_line()  + ggtitle(i) 
  plist[[i]] <- p
}

# print to pdf in A3 format
pdf(file="out.pdf",width=16.5,height=11.7)
do.call(grid.arrange,c(plist,main="Main Title",sub="Subtitle"))
dev.off()

更新

Slowlearner 使用Baptiste 提供的代码的解决方案正是我想要的。

实现类似功能的另一种方法是在空白图上使用ggplot2annotate_custom() 函数。空意味着所有theme() 属性都设置为element_blank()。然后可以使用 Winston Chang 在他的 R Cookbook 网站上提供的 the following function 在网格中排列该图。然而,在这个解决方案中,textgrob 不会跨越所有剩余的空 grobs。

【问题讨论】:

  • 我认为ggplot中有annotate选项
  • @Metrics 非常感谢您的快速评论。我的理解是annotate 只允许在网格中组装的图上添加文本。但是,我想在剩余区域添加注释。我怀疑这与gridExtra 有关。但也许我错过了什么。如果是这样,您能否更具体地说明在这种情况下如何使用 annotate
  • 您可以将任意 grobs 传递给 grid.arrange
  • 使用基于 gtable 的 a version of grid.arrange 可能更容易
  • @Metrics 在研究了[这篇文章](stackoverflow.com/questions/14313285/…)后,我明白如果我将'element_blank()'用于“普通香草”主题,'annotate_custom()' 会让我走得更远.

标签: r ggplot2 gridextra


【解决方案1】:

基于对上面 Baptiste 的 cmets 的成熟考虑(即我基本上捏了他所有的代码),我整理了一个简单的例子。显然,您需要尝试绘图的格式和大小,textGrob 需要在其他地方定义和格式化,但这些都是细节。生成的图如下,代码如下。大部分都被the function definition占用了,情节代码在底部。

gtable_arrange <- function(..., grobs=list(), as.table=TRUE,
                           top = NULL, bottom = NULL, 
                           left = NULL, right = NULL, draw=TRUE){
  require(gtable)
  # alias
  gtable_add_grobs <- gtable_add_grob

  dots <- list(...)
  params <- c("nrow", "ncol", "widths", "heights",
              "respect", "just", "z") # TODO currently ignored

  layout.call <- intersect(names(dots), params)
  params.layout <- dots[layout.call]

  if(is.null(names(dots)))
    not.grobnames <- FALSE else
      not.grobnames <- names(dots) %in% layout.call

  if(!length(grobs))
  grobs <- dots[! not.grobnames ]

  ## figure out the layout
  n <- length(grobs)
  nm <- n2mfrow(n)

  if(is.null(params.layout$nrow) & is.null(params.layout$ncol)) 
  {
    params.layout$nrow = nm[1]
    params.layout$ncol = nm[2]
  }
  if(is.null(params.layout$nrow))
    params.layout$nrow = ceiling(n/params.layout$ncol)
  if(is.null(params.layout$ncol))
    params.layout$ncol = ceiling(n/params.layout$nrow)

  if(is.null(params.layout$widths))
    params.layout$widths <- unit(rep(1, params.layout$ncol), "null")
  if(is.null(params.layout$heights))
    params.layout$heights <- unit(rep(1,params.layout$nrow), "null")

  positions <- expand.grid(row = seq_len(params.layout$nrow), 
                           col = seq_len(params.layout$ncol))
  if(as.table) # fill table by rows
    positions <- positions[order(positions$row),]

  positions <- positions[seq_along(grobs), ] # n might be < ncol*nrow

  ## build the gtable, similar steps to gtable_matrix

  gt <- gtable(name="table")
  gt <- gtable_add_cols(gt, params.layout$widths)
  gt <- gtable_add_rows(gt, params.layout$heights)
  gt <- gtable_add_grobs(gt, grobs, t = positions$row, 
                            l = positions$col)

  ## titles given as strings are converted to text grobs
  if (is.character(top)) 
    top <- textGrob(top)
  if (is.character(bottom)) 
    bottom <- textGrob(bottom)
  if (is.character(right)) 
    right <- textGrob(right, rot = -90)
  if (is.character(left)) 
    left <- textGrob(left, rot = 90)

  if(!is.null(top)){
    gt <- gtable_add_rows(gt, heights=grobHeight(top), 0)
    gt <- gtable_add_grobs(gt, top, t=1, l=1, r=ncol(gt))
  }
  if(!is.null(bottom)){
    gt <- gtable_add_rows(gt, heights=grobHeight(bottom), -1)
    gt <- gtable_add_grobs(gt, bottom, t=nrow(gt), l=1, r=ncol(gt))
  }
  if(!is.null(left)){
    gt <- gtable_add_cols(gt, widths=grobWidth(left), 0)
    gt <- gtable_add_grobs(gt, left, t=1, b=nrow(gt), l=1, r=1)
  }
  if(!is.null(right)){
    gt <- gtable_add_cols(gt, widths=grobWidth(right), -1)
    gt <- gtable_add_grobs(gt, right, t=1, b=nrow(gt), l=ncol(gt), r=ncol(gt))
  }

  if(draw){
   grid.newpage()
   grid.draw(gt)
  }
  gt

}

# load needed libraries
library(ggplot2)

# Set loop counter and create list to store objects
imax=37
plist <- list()
p <- ggplot(mtcars, aes(x = wt, y = mpg)) + geom_line() 

for(i in 1:imax){
  plist[[i]] <- p + ggtitle(i)
}

# build list of grobs
grob.list <- lapply(plist, ggplotGrob)

# prepare titles
title.main <- textGrob("Main title")
title.sub <- textGrob("Subtitle")

# then arrange as required
g <- gtable_arrange(ncol=6, grobs=grob.list, 
                    top=title.main, bottom=title.sub, draw=FALSE)
ann <- grobTree(rectGrob(), textGrob("Annotation box here"))
g <- gtable_add_grobs(g, ann, t=nrow(g)-1, l=2, r=ncol(g))

# save it all together
png(file = "out.png",width=1000, height=710, units = "px")
grid.draw(g)
dev.off()    

【讨论】:

  • 感谢这个伟大的工作示例!在最后添加) 以关闭lapply 函数后,我能够顺利运行它。但是,结果与您发布的图形不同,因为所有图例和轴都丢失了。我试图通过更改 par() 以及 ggplottheme 来更改图形的大小和格式,但没有成功。如果您能指出我可以在函数或脚本中设置 ggplot-grobs 边距的位置(我希望我的术语正确),我会非常高兴。
  • 很高兴尝试更改代码以使其更好地工作,但我的答案中的情节似乎与您提出的情节几乎相同。我的答案中的情节有轴。他们没有传说,但是您问题中的情节也没有。您想要的与您在问题中提出的不同吗?请详细一点。
  • 不,您发布的内容正是我想要的。好吧,我在原始问题下方发布了我的结果。如您所见,所有图例和标签都已消失。我必须承认,我已经用 gridgridExtrafunctions 修补了几个小时。但是我没有更改任何设置文件的配置,所以我的理解是没有留下任何相关的痕迹。格式化和更改textgrob 很好。因此,如果您对这里发生的事情有所了解,我会很高兴知道。
  • 你心中的传奇是怎样的?在您问题的示例中,不需要图例,因为每个图只显示一个数据项(因此无需区分另一个)。但是,如果每个情节确实有两条线,我希望传说会出现。顺便说一句,我已经更新了代码以添加您在示例中拥有的标题。同时复制并粘贴上面的所有代码,然后重试 - 它对我有用。
  • 现在一切都很甜蜜!非常感谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-06-03
  • 2020-10-17
  • 2017-10-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-09-04
相关资源
最近更新 更多