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

ali*_*boy 19 layout plot r heatmap

@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`?
Run Code Online (Sandbox Code Playgroud)

原创问题来自@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 ),                 
)
Run Code Online (Sandbox Code Playgroud)

但是,由于我想在单个图中比较多个热图,我使用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 ),                 
)
Run Code Online (Sandbox Code Playgroud)

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

Din*_*nre 19

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

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

我建议选择Paul Murrell的书"R Graphics"并翻阅'grid'包中的章节.这是一本疯狂有用的书,我的桌子上随时都有副本.

对于你的热图,我写了一本快速入门书,可以让你快速入门.

要知道的功能

  • 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'功能就很容易.

放弃

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

但是,如果你不喜欢编写脚本,'grid'将是你的敌人.此外,如果你考虑半天的时间来进行视觉,那么'网格'对你来说太过分了.(in)着名的'ggplot2'包是大多数人都喜欢的,我衷心推荐它,尽管我个人并不认为它有用.

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


bap*_*ste 8

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)
Run Code Online (Sandbox Code Playgroud)

  • 这是一个优雅的解决方案,允许继续使用诸如`pheatmap`之类的包.顺便说一句,要使用pheatmap,不需要调用`grid.echo()`,因为`pheatmap`已经使用了网格包. (3认同)