20

@backlin 的介绍

layout使用或可以将多个简单图组合为单个图形中的面板par(mfrow=...)。但是,更复杂的绘图往往会在内部设置自己的面板布局,从而使它们无法用作面板。有没有办法创建嵌套布局并将复杂的绘图封装到单个面板中?

我有一种感觉,该grid包可以实现这一点,例如通过在单独的视口中绘制面板,但无法弄清楚如何。这是一个演示问题的玩具示例:

my.plot <- function(){
    a <- matrix(rnorm(100), 10, 10)
    plot.new()
    par(mfrow=c(2,2))
    plot(1:10, runif(10))
    plot(hclust(dist(a)))
    barplot(apply(a, 2, mean))
    image(a)
}
layout(matrix(1:4, 2, 2))
for(i in 1:4) my.plot()
# How to avoid reseting the outer layout when calling `my.plot`?

@alittleboy 的原始问题

我使用包中的heatmap.2函数gplots来生成热图。以下是单个热图的示例代码:

library(gplots)
row.scaled.expr <- matrix(sample(1:10000),nrow=1000,ncol=10)
heatmap.2(row.scaled.expr, dendrogram ='row',
          Colv=FALSE, col=greenred(800), 
          key=FALSE, keysize=1.0, symkey=FALSE, density.info='none',
          trace='none', colsep=1:10,
          sepcolor='white', sepwidth=0.05,
          scale="none",cexRow=0.2,cexCol=2,
          labCol = colnames(row.scaled.expr),                 
          hclustfun=function(c){hclust(c, method='mcquitty')},
          lmat=rbind( c(0, 3), c(2,1), c(0,4) ), lhei=c(0.25, 4, 0.25 ),                 
)

但是,由于我想在单个图中比较多个热图,所以我使用par(mfrow=c(2,2))然后调用heatmap.2四次,即

row.scaled.expr <- matrix(sample(1:10000),nrow=1000,ncol=10)
arr <- array(data=row.scaled.expr, dim=c(dim(row.scaled.expr),4))
par(mfrow=c(2,2))
for (i in 1:4)
heatmap.2(arr[ , ,i], dendrogram ='row',
          Colv=FALSE, col=greenred(800), 
          key=FALSE, keysize=1.0, symkey=FALSE, density.info='none',
          trace='none', colsep=1:10,
          sepcolor='white', sepwidth=0.05,
          scale="none",cexRow=0.2,cexCol=2,
          labCol = colnames(arr[ , ,i]),                 
          hclustfun=function(c){hclust(c, method='mcquitty')},
          lmat=rbind( c(0, 3), c(2,1), c(0,4) ), lhei=c(0.25, 4, 0.25 ),                 
)

但是,结果不是单个图中的四个热图,而是四个单独的热图。换句话说,如果我使用pdf()输出结果,文件是四页而不是一页。我需要在某处更改任何参数吗?非常感谢!

4

4 回答 4

20

好的。我想这个问题已经有足够长的时间没有得到回答,应该写下长长的答案。

最困难的图形问题的答案是(正如@backlin 建议的那样)'grid' 包的原始使用。许多预构建的图形包会覆盖所有当前的视口和绘图设备设置,因此如果您想要以非常具体的方式完成某些事情,您必须自己构建它。

我建议阅读 Paul Murrell 的书“R Graphics”并阅读有关“网格”包的章节。这是一本非常有用的书,我的办公桌上一直放着一本。

对于你的热图,我写了一个快速入门,可以让你快速入门。

要知道的功能

  • grid.newpage() 这将初始化绘图设备。不带参数使用它。
  • grid.rect() 这将绘制一个矩形。您的热图基本上只是一组巨大的彩色矩形,因此这将是您图形的大部分。它的工作原理是这样grid.rect(x=x_Position, y=y_Position, width=width_Value, height=height_Value, gp=gpar(col=section_Color, fill=section_Color), just=c("left", "bottom"), default.units="native") 的:'just' 参数指定矩形的哪个点将位于您指定的 (x, y) 坐标上。
  • grid.text() 这将绘制文本。它是这样工作的:grid.text("Label Text", x_Value, y_Value, gp=gpar(col=color_Value, cex=font_Size), just=c("right","center"), rot=rot_Degrees, default.units="native")
  • grid.lines() 这画了一条线。它是这样工作的:grid.lines(c(x_Start,x_End), c(y_Start, y_End), gp=gpar(col=color_Value), default.units="native")
  • dataViewport() 这定义了绘图窗口的属性,“网格”称为“视口”。像这样使用它:pushViewport(dataViewport(xData=x_Data, yData=y_Data, xscale=c(x_Min, x_Max), yscale=c(y_Min, y_Max), x=x_Value, y=y_Value, width=width_Value, height=height_Value, just=c("left","center"))) 这里有一些东西要记住......请参阅视口的更详细说明。
  • pushViewport() 这用于初始化一个veiwport。您将其包裹在视口定义中以实际执行视口,如下所示:pushViewport(dataViewport([stuff in here]))
  • popViewport() 这最终确定了一个视口,并将您在视口层次结构中上移一级。请参阅视口的更详细说明。

简而言之视口

视口是定义“网格”对象的绘制位置和方式的临时绘图空间。视口内的所有内容都是相对于视口绘制的。如果视口旋转,里面的所有东西都会旋转。视口可以嵌套,可以重叠,并且几乎可以无限灵活,但有一个例外:它们始终是一个矩形。

最初让很多人感到困惑的是坐标系。每个视口,包括初始的“grid.newpage()”视口,在 x 和 y 轴上都从 0 变为 1。原点 (0,0) 是最左下角,最大值 (1,1) 是最右上角。这是“npc”单位系统,所有没有指定一组单位的东西都可能最终根据这个系统绘制。这对您来说意味着两件事:

  1. 指定视口大小和位置时使用“npc”系统。只需假设您的视口必须使用“npc”坐标,您就会省去很多麻烦。这意味着如果我想绘制两个相邻的图,两个视口的定义将类似于:
    • viewport(x=0, y=0, width=0.5, height=1, just=c("left","lower"))
    • viewport(x=0.5, y=0, width=0.5, height=1, just=c("left","lower"))
  2. 如果您的视口具有不同的坐标系(例如用于绘制图形的视口),那么您将需要为您绘制的每个“网格”对象指定“default.units”参数。例如,如果您尝试在 (2,4) 处绘制一个点,您将永远看不到该点,因为它会远离屏幕。指定default.units="native"将告诉该点使用视口自己的坐标系并正确绘制该点。

视口可以直接导航和写入,但除非您正在做一些非常自动化的事情,否则指定一个视口,在其中绘制,然后“弹出”(最终确定)视口会更容易。这会将您返回到父视口,您可以从下一个视口开始。弹出每个视口是一种简洁的方法,适合大多数用途(并且更容易调试!)。

绘制图表时,“dataViewport”功能非常重要。这是一种特殊类型的视口,可以为您处理所有坐标和比例,只要您告诉它您正在使用什么数据。这是我用于任何绘图区域的那个。当我第一次开始使用“grid”包时,我调整了所有值以适应“npc”坐标系,但这是一个错误!只要您记得为每个绘图项使用“本机”单位,“dataViewport”功能就很容易了。

免责声明

数据可视化是我的强项,我不介意花半天时间编写一个好的视觉效果。“网格”包让我能够比我发现的任何其他东西更快地创建非常复杂的视觉效果。我将我的视觉效果编写为函数,因此我可以快速加载各种数据。我再高兴不过了。

但是,如果您不喜欢编写脚本,“网格”将是您的敌人。此外,如果你认为半天的时间对于视觉来说太长了,那么“网格”对你的帮助不会太大。(不)著名的“ggplot2”包是大多数人所接受的,我衷心推荐它,尽管我个人认为它没有用。

如果有人想要帮助学习“网格”图形,我非常愿意帮助教学。它彻底改变了我创建快速、智能和美观的数据视觉效果的能力。

于 2013-01-28T17:07:20.280 回答
10

gridGraphics软件包可能会有所帮助,

在此处输入图像描述

library(gridGraphics)
library(grid)

grab_grob <- function(){
  grid.echo()
  grid.grab()
}

arr <- replicate(4, matrix(sample(1:100),nrow=10,ncol=10), simplify = FALSE)

library(gplots)
gl <- lapply(1:4, function(i){
  heatmap.2(arr[[i]], dendrogram ='row',
            Colv=FALSE, col=greenred(800), 
            key=FALSE, keysize=1.0, symkey=FALSE, density.info='none',
            trace='none', colsep=1:10,
            sepcolor='white', sepwidth=0.05,
            scale="none",cexRow=0.2,cexCol=2,
            labCol = colnames(arr[[i]]),                 
            hclustfun=function(c){hclust(c, method='mcquitty')},
            lmat=rbind( c(0, 3), c(2,1), c(0,4) ), lhei=c(0.25, 4, 0.25 ),                 
  )
  grab_grob()
})

grid.newpage()
library(gridExtra)
grid.arrange(grobs=gl, ncol=2, clip=TRUE)
于 2015-08-02T03:15:48.667 回答
2

我遇到了类似的问题,并想出了一个非常简单但需要安装 imagemagick 的解决方案。这个想法是将热图绘制到单独的文件中,然后将它们与 montage 命令结合起来:

library(gplots)
row.scaled.expr <- matrix(sample(1:10000),nrow=1000,ncol=10)
arr <- array(data=row.scaled.expr, dim=c(dim(row.scaled.expr),4))
par(mfrow=c(2,2))
for (i in 1:4) {
    ifile <- paste0(i,'_heatmap.pdf')
    pdf(ifile)
    heatmap.2(arr[ , ,i], dendrogram ='row',
                        Colv=FALSE, col=greenred(800), 
                        key=FALSE, keysize=1.0, symkey=FALSE, density.info='none',
                        trace='none', colsep=1:10,
                        sepcolor='white', sepwidth=0.05,
                        scale="none",cexRow=0.2,cexCol=2,
                        labCol = colnames(arr[ , ,i]),                 
                        hclustfun=function(c){hclust(c, method='mcquitty')},
                        lmat=rbind( c(0, 3), c(2,1), c(0,4) ), lhei=c(0.25, 4, 0.25 ),                 
    )
    dev.off()
}
system('montage -geometry 100% -tile 2x2 ./*_heatmap.pdf outfile.pdf')
于 2015-03-27T09:44:26.830 回答
2

正如 Dinre 所说,“网格”包可以处理所有复杂的情节。对于@alittleboy 的原始问题,我认为 Bionconductor 的“ComplexHeatmap”包(也是基于网格的)可能是一个不错的解决方案(http://www.bioconductor.org/packages/release/bioc/vignettes/ComplexHeatmap /inst/doc/ComplexHeatmap.html )

于 2015-08-02T02:10:22.923 回答