将多个复杂图形组合在一个图中作为面板

时间:2012-10-26 05:34:59

标签: r layout plot heatmap

@backlin介绍

使用layoutpar(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 个答案:

答案 0 :(得分:19)

好。我想这个问题一直没有得到答复,应该写下长篇答案。

对于最困难的图形问题的答案是(正如@backlin建议的那样)对网格的原始使用&#39;包。许多预先构建的图形包会覆盖所有当前视口并绘制设备设置,因此如果您想要以非常特定的方式完成某些操作,则必须自己构建它。

我建议选择Paul Murrell的书&#34; R Graphics&#34;并仔细阅读关于网格的章节&#39;包。这是一本疯狂有用的书,我的桌子上随时都有副本。

对于你的热图,我已经写了一本快速入门书,可以帮助你快速入门。

要了解的功能

  • 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")&#39; just&#39;参数指定矩形的哪个点将位于指定的(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()这定义了绘图窗口的属性,其中&#39; grid&#39;指的是&#34;视口。&#34;像这样使用它: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()这将最终确定一个视口,并将您向上移动到视口层次结构中的一个级别。请参阅视口的更详细说明。

简介视口

视口是临时绘图空间,用于定义网格&#39;的位置和方式。将绘制对象。视口内的所有内容都将 relative 绘制到视口中。如果旋转视口,则内部的所有内容都将旋转。视口可以嵌套,可以重叠,几乎无限灵活,但有一个例外:它们总是一个矩形。

最初让很多人迷失的是坐标系。每个视口,包括初始&#39; grid.newpage()&#39;视口,在x和y轴上从0到1。原点(0,0)是最左下角,最大(1,1)是最右上角。这是&#34; npc&#34;单位系统,并且没有指定一组单位的所有单元都可能最终根据该系统绘制。这对您来说意味着两件事:

  1. 使用&#34; npc&#34;指定视口大小和位置时的系统。假设您的视口必须使用&#34; npc&#34;坐标,你将为自己省去很多麻烦。这意味着如果我想彼此相邻绘制两个图,两个视口的定义将类似于:
    • 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. 如果您的视口具有不同的坐标系(例如用于绘制图形的视口),则需要指定&#39; default.units&#39;每个网格的论据&#39;你绘制的对象。例如,如果你试图在(2,4)处绘制一个点,你将永远不会看到这一点,因为它远离屏幕。指定default.units="native"会告诉该点使用视口自己的坐标系并正确绘制点。
  3. 可以直接导航和写入视口,但除非您自动执行某些操作,否则更容易指定视口,在其中绘制,然后弹出&#34; (最终确定)视口。这会返回到父视口,您可以从下一个视口开始。弹出每个视口是一种无杂乱的方法,适合大多数目的(并使其更容易调试!)。

    &#39; dataViewport&#39;绘制图形时,函数非常重要。这是一种特殊类型的视口,可以为您处理所有坐标和比例,只要您告诉它您正在使用哪些数据。这是我用于任何绘图区域的那个。当我第一次开始使用&#39;网格&#39;包,我调整了所有值以适应&#34; npc&#34;坐标系,但这是一个错误! &#39; dataViewport&#39;只要你记得使用&#34; native&#34;每个图纸项目的单位。

    <强>声明

    数据可视化是我的强项,我不介意花半天时间编写好的视觉效果。 &#39;网格&#39;包允许我创建非常复杂的视觉效果比我发现的任何其他东西更快。我将我的视觉效果编写为函数,因此我可以快速加载各种数据。我不能幸福。

    但是,如果你不想编写任何东西,那就是网格&#39;将是你的敌人。此外,如果你考虑半天的时间来进行视觉,那么“网格”就是这样的。不会帮助你太多。着名的&#39; ggplot2&#39;包装是大多数人的定义,我衷心推荐它,即使我个人觉得它没用。

    如果有人想要帮助学习&#39; grid&#39;图形,我更愿意帮助教学。它彻底改变了我创建快速,智能和美观数据视觉的能力。

答案 1 :(得分:8)

gridGraphics包可能有帮助,

enter image description here

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)

答案 2 :(得分:2)

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

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')

答案 3 :(得分:2)

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