前言
在本专栏的上一篇文章R统计绘图(1):ggplot2入门指南里提到,ggplot2的facet
功能支持快速生成多张相同制作流程的统计图形,但是不能支持生成多张制作流程不同的统计图形。其实这很正常,制作流程不同,那就只能一张一张地制作——那么问题来了:这单独制作的多张图怎么随心所欲地按照你的设想进行摆放呢?上一篇文章给出了一段代码,可以绘制下面的这张图形:
代码里用到了R的一个包grid,原来是第三方的,由于其在R图形领域极其重要的地位,现在已经随R语言一同打包下载了。本文详细讲解grid包图形布局的核心概念viewport,并演示三种结合ggplot2绘制上图的方法。
viewport
viewport
是grid包的核心对象,说简单点就是一个矩形的绘图区域。
创建viewport时,可以指定以下几个重要参数:
x:绘图区域相对页面左下角原点的x坐标,默认单位为npc,Normalised Parent Coordinates,含义是归一化的父区域坐标
y:绘图区域相对页面左下角原点的y坐标
width:绘图区域的宽度(x轴方向)
height:绘图区域的高度(y轴方向)
just:x和y所指的位置,默认为矩形中心位置
angle:将绘图区域旋转多少度
name:此viewport的名字,用于搜索和定位
通过grid.show.viewport()
可以用可视化的方式了解到每个参数的含义:
library(grid)
grid.show.viewport(viewport(x=0.6, y=0.6, width=unit(1, "inches"), height=unit(1, "inches"), angle=30))
ggplot2的绘图结果默认情况下是打开一个全新的页面绘制。如果要在一个页面上绘制多个图形,可以使用print.ggplot()
函数。这个函数有一个选项vp
,可以指定这个图形要绘制的viewport的位置。绘图过程中可以使用grid.newpage()
创建一个全新的空白页面。我们使用目前了解到的知识重现一下前言里的统计图形:
library(grid)
library(ggplot2)
# prepare ggplot charts
p.hist.len <- ggplot(iris) + geom_histogram(aes(x=Sepal.Length))
p.hist.wid <- ggplot(iris) + geom_histogram(aes(x=Sepal.Width)) + coord_flip()
p.scatter <- ggplot(iris) + geom_point(aes(x=Sepal.Length, y=Sepal.Width))
# create viewports
grid.newpage()
vp.len <- viewport(x=0, y=0.66, width=0.66, height=0.34, just=c("left", "bottom"))
vp.wid <- viewport(x=0.66, y=0, width=0.34, height=0.66, just=c("left", "bottom"))
vp.scatter <- viewport(x=0, y=0, width=0.66, height=0.66, just=c("left", "bottom"))
# direct the charts into the specified viewport
print(p.hist.len, vp=vp.len)
print(p.hist.wid, vp=vp.wid)
print(p.scatter, vp=vp.scatter)
viewport树
grid可以创建多个viewport,所有的viewport组织成一棵树。grid提供了一套函数用于管理viewport对象。任何一个时刻,有一个当前viewport对象,初始状态下为树的根节点viewport。使用pushViewport()
可以将指定的viewport插入到当前viewport的子节点中,同时当前viewport对象移动为刚刚插入的viewport;使用popViewport()
可以删除当前viewport,同时当前viewport改为刚刚删除的viewport的父节点;使用upViewport()
当前viewport移动到父节点;使用downViewport()
当前viewport移动到指定name的子节点;使用seekViewport()
在整棵树范围内搜索指定name的viewport,将其设置为当前viewport。
# top left panel
grid.newpage()
vp.len <- viewport(x=0, y=0.66, width=0.66, height=0.34, just=c("left", "bottom"))
pushViewport(vp.len)
print(p.hist.len, newpage=F)
upViewport() # 返回父节点
# bottom right panel
vp.wid <- viewport(x=0.66, y=0, width=0.34, height=0.66, just=c("left", "bottom"))
pushViewport(vp.wid)
print(p.hist.wid, newpage=F)
upViewport()
# bottom left panel
vp.scatter <- viewport(x=0, y=0, width=0.66, height=0.66, just=c("left", "bottom"))
pushViewport(vp.scatter)
print(p.scatter, newpage=F)
upViewport()
viewport行列布局
viewport还支持行列布局,前面提到viewport
函数,还有几个参数我们还没涉及:
layout:
grid.layout
对象,用于将当前viewport拆分为子区域layout.pos.row:创建的viewport在父节点layout的行位置
layout.pos.col:创建的viewport在父节点layout的列位置
我们来看一下grid.layout()
的参数:
nrow:将该区域拆分为几行
ncol:将该区域拆分为几列
widths:每个子区域的宽度,向量长度等于ncol
heights:每个子区域的高度,向量长度等于nrow
我们来演示下这种方法生成前言统计图形的过程:
grid.newpage()
pushViewport(viewport(layout = grid.layout(3, 3)))
print(p.scatter, vp=viewport(layout.pos.row=2:3, layout.pos.col=1:2))
print(p.hist.len, vp=viewport(layout.pos.row=1, layout.pos.col=1:2))
print(p.hist.wid, vp=viewport(layout.pos.row=2:3, layout.pos.col=3))
关于作者:丹追兵,数据分析师一枚,编程语言python和R,使用Spark、Hadoop、Storm、ODPS。本文出自丹追兵的pytrafficR专栏,转载请注明作者与出处:https://segmentfault.com/blog...