中科白癜风 http://baidianfeng.39.net/R数据可视化——grid系统(二)前言
在前面一节中,我们主要介绍了如何使用grid来生成图形输出,以及图形窗口的布局。利用这些知识,可以很容易地为图形添加注释,编写一些简单的绘图函数
这一节,我们将重点介绍如何使用grid函数来创建和操作图形对象。利用这些信息,可以交互式地编辑和修改图形输出
图形对象1.控制图像输出我们可以使用图形原语来绘制图形输出,并返回一个图形对象(grob),例如
library(RColorBrewer)grid.circle(name="circles",x=seq(0.1,0.9,length=40),y=0.5+0.4*sin(seq(0,2*pi,length=40)),r=abs(0.1*cos(seq(0,2*pi,length=40))),gp=gpar(col=brewer.pal(40,"Set2")))
这段代码将会绘制一串圆形
同时也会生成一个circlegrob,该对象保存了当前绘制的这些圆形的信息
grid保留了一个显示列表,用于记录当前画布中的所有的viewport和grobs。因此,grid.circle()函数构造的对象也会保存在显示列表中,意味着我们可以根据对象的名称circles来获取、修改该对象
使用grid.get()函数,可以获取该circle对象的拷贝
grid.get("circles")circle[circles]
使用grid.edit()可以用来修改该circle对象的图像属性
grid.edit("circles",gp=gpar(col=brewer.pal(10,"RdBu")))
修改颜色属性之后,会直接显示在图形输出中
还可以使用grid.remove()函数,从显示列表中删除图形对象的输出
grid.remove("circles")
一片空白,什么也没有
1.1标准的函数及参数控制grobs的函数包括:
所有图像输出函数的第一个参数都是图像对象的名称,如果参数grep=TRUE,可以接受正则表达式对象名称
如果global=TRUE,则会返回显示列表中所有匹配的对象,例如
suffix-c("even","odd")for(iin1:8)grid.circle(name=paste0("circle.",suffix[i%%2+1]),r=(9-i)/20,gp=gpar(col=NA,fill=grey(i/10)))
我们绘制了8个同心圆,并根据奇偶顺序将circlegrob命名为circle.odd和circle.even
然后,我们可以使用grid.edit()函数,修改所有名为circle.odd的grobs的颜色
grid.edit("circle.odd",gp=gpar(fill=brewer.pal(4,"Set3")[4]),global=TRUE)
或者,用正则表达式来匹配以circle开头的grob
grid.edit("circle",gp=gpar(col="#80b1d3",fill="#fdb"),grep=TRUE,global=TRUE)
只要我们知道了grobs的名称,就可以对其获取、修改或删除
而getNames()函数,可以帮助我们获取当前图形中所有grobs的名称
2.grob排布结构grob的排布结果包括:
gList:包含多个grobs的listgTree:grobs的树形结构,即一个grob中包含其他的grob例如,对于xaxisgrob
pushViewport(viewport(y=0.5,height=0.5,width=0.5))grid.rect()pushViewport(viewport(y=0.5,height=0.5,width=0.5))grid.xaxis(name="axis1",at=1:4/5)
会包含有多个子grob,如线条,文本等
childNames(grid.get("axis1"))[1]"major""ticks""labels"
如果把xaxisgrob看作为一棵树的根,那么它包含三个子grob。
其中major和ticks是linesgrob,labels为textgrob。
其中at参数设置了轴刻度,我们可以使用grid.edit来修改
grid.edit("axis1",at=1:3/4)
那想要修改labels的格式,怎么办?即如何访问一个对象的子对象呢?
可以使用gPath(grobpath)函数,类似于vpPath,可以使用父节点名称加子节点名称来访问
grid.edit(gPath("axis1","labels"),rot=45)
或者,也可以使用axis1::labels方式来访问
注意:grobs的搜索是深度优先,也就是说,如果在显示列表中遍历到了一个gTreegrob,且未找到匹配项,则会对该grob执行深度优先遍历
这种gTree结构对象,也包含gp和vp参数。
在父节点上设置对应的gp参数值,会作为默认值传递给子对象。例如
grid.xaxis(gp=gpar(col="grey"))
也可以将一个viewport直接作为参数值传递
grid.xaxis(vp=viewport(y=0.75,height=0.5))3.图形对象
在前面的章节中,我们介绍的都是如何使用函数直接生成图形输出并返回图形对象(grob)
在这一节,我们将介绍如果创建grob,但不绘制图形,通过对grob创建及修改,并在最后使用grid.draw()函数来绘制出图形。
每个能产生图形输出和图形对象的grid函数都有一个对应的只创建图形对象,没有图形输出的函数
例如,grid.circle()对应于circleGrob(),grid.edit()对应于editGrob(),在前面的函数表中都有列出
例如
grid.newpage()pushViewport(viewport(width=0.5,height=0.5))#创建x轴对象ag-xaxisGrob(at=1:4/5)#修改对象,将标签的字体变为斜体ag-editGrob(ag,"labels",gp=gpar(fontface="italic"))#绘图grid.draw(ag)
我们可以将不同的grob组合在一起,生成一个复杂的图形。比如
grid.newpage()tg-textGrob("sampletext")rg-rectGrob(width=1.2*grobWidth(tg),height=1.5*grobHeight(tg))boxedText-gTree(children=gList(rg,tg))
我们构建一个名为boxedText的gTree对象,包含其子对象包括一个文本和一个矩形
我们直接可以绘制组合对象
grid.draw(boxedText)
而对该对象的图形属性的修改,会反映到具体的子对象中
grid.draw(editGrob(boxedText,gp=gpar(col="skyblue")))
指定viewport
grid.draw(editGrob(boxedText,vp=viewport(angle=45),gp=gpar(fontsize=18)))3.1捕获输出
在上面的例子中,我们先构建了一个组合对象,然后绘制该对象
还可以反着来,先绘制图形对象,然后对它们进行组合。
使用grid.grab()函数,可以获取当前画布中所有输出的图形对象,并以gTree的形式返回
例如,我们使用ggplot2绘制一个直方图,并获取所有图形对象
ggplot(mpg)+geom_histogram(aes(displ,fill=class),bins=10,position="dodge")histTree-grid.grab()
然后,你可以尝试运行下面的代码
grid.newpage()grid.draw(histTree)
你会发现,可以绘制出一张一模一样的图
也可以使用grid.grabExpr来获取表达式的输出图形对象
grid.grabExpr(print(ggplot(mpg)+geom_histogram(aes(displ,fill=class),bins=10,position="dodge")))4.图形对象的放置
假设我们有一个复杂图形
#文本对象label-textGrob("A\nPlot\nLabel",x=0,just="left")x-seq(0.1,0.9,length=50)y-runif(50,0.1,0.9)#gTree结构图形对象,包括矩形、线图、点图gplot-gTree(children=gList(rectGrob(gp=gpar(col="grey60",fill="#cbd5e8",alpha=0.3)),linesGrob(x,y,gp=gpar(col="#33a02c")),pointsGrob(x,y,pch=16,size=unit(5,"mm"),gp=gpar(col="#fb"))),vp=viewport(width=unit(1,"npc")-unit(5,"mm"),height=unit(1,"npc")-unit(5,"mm")))
我们可以使用上一章节提到的布局方法,将该图像设计为1行2列的布局
layout-grid.layout(nrow=1,ncol=2,widths=unit(c(1,1),c("null","grobwidth"),list(NULL,label)))
然后将图形绘制到指定位置中
pushViewport(viewport(layout=layout))pushViewport(viewport(layout.pos.col=2))grid.draw(label)popViewport()pushViewport(viewport(layout.pos.col=1))grid.draw(gplot)popViewport(2)
但其实,grid提供了更简便的函数用于放置grobs
grid.frame()函数创建一个没有子对象的gTree,可以使用grid.pack()向其中添加子对象,同时确保为每个子对象保留足够的绘图空间
上面的代码可以改写成
grid.newpage()#新建一个空framegrid.frame(name="frame1")#放置gplot对象,在这一阶段,gplot会占据整个framegrid.pack("frame1",gplot)#在frame的右边放置label对象grid.pack("frame1",label,side="right")
这种动态的方式很简便,但是也带来了时间上的花费,随着需要放置的对象越来越多,速度会越来越慢。
另一种替代的方式是,先定义一个布局,然后再放置对象
grid.frame(name="frame1",layout=layout)grid.place("frame1",gplot,col=1)grid.place("frame1",label,col=2)4.1安静模式
在上面两个例子中,每次放置一个grob都会更新一遍图形输出。所以,一个更好的方式是,在安静模式下创建一个frame,然后放置grobs。
安静模式,即使用对象函数frameGrob()和placeGrob()/packGrob创建frame、放置grobs,但是不会输出图形,只有在所有设置完成之后,使用grid.draw一次性绘制
#创建framefg-frameGrob(layout=layout)#添加grobfg-placeGrob(fg,gplot,col=1)fg-placeGrob(fg,label,col=2)#一次性绘制grid.draw(fg)-END-生信学习手册