简介
本文主要阐述ggplot2中图形的定位,主要介绍布局分面和坐标系如何工作两个问题。
分面
ggplot2提供两种分面类型:网格型(facet_grid)和封装型(facet_wrap)。网格分面生成的是一个2维的面板网格,面板的行与列通过变量来定义;封装分面则先生成一个1维的面板条块,然后再封装到2维中。
分面系统有两个基本参数:一个是分面变量的设置,另一个是指定分面的位置标度是全局还是局部。
分面绘图通常会占用大量空间,因此这里使用mpg数据集的子集来进行展示。它有几个简单的水平:三种气缸(4, 6, 8)和两种驱动轮(4和f)。
mpg2 <- subset(mpg, cyl != 5 & drv %in% c("4", "f"))
网格分面
网格分面在2维网格中展示图形。输入分面表达式时,需要设定哪些变量作为分面绘图的行,哪些变量作为列,规则如下:
不进行分面:即不使用函数facet_grid
或者加上命令facet_null()
,此时就得到一个单独的面板。
## 和 qplot(cty, hwy, data = mpg2) 效果一样
qplot(cty, hwy, data = mpg2) + facet_null()
一行多列:”. ~ a”。电脑屏幕较宽,这个方向最适合数据的展示。另外纵坐标想同,有助于y位置的比较。
qplot(cty, hwy, data = mpg2) + facet_grid(. ~ cyl)
一列多行:”b ~ .”。横坐标轴相同,利于x位置的比较,尤其是对数据分布的比较。
qplot(cty, data = mpg2, geom = "histogram", binwidth = 2) + facet_grid(cyl ~ .)
多行多列:”a ~ b”。通常都将因子水平数目最大的变量按列排放,充分利用屏幕的宽高比。
qplot(cty, hwy, data = mpg2) + facet_grid(drv ~ cyl)
切割图形就好比创建一个列联表。列联表可展示每个单元格的值以及边际和(一个行或列的总和)。这种模式对于绘图也非常有用,可以使用参数margins来绘制边际图。
p <- qplot(displ, hwy, data = mpg2) + geom_smooth(method = "lm", se = F)
p + facet_grid(cyl ~ drv)
p + facet_grid(cyl ~ drv, margins = T)
qplot(displ, hwy, data = mpg2) + geom_smooth(aes(colour = drv), method = "lm", se = F) + facet_grid(cyl ~ drv, margins = T)
封装分面
facet_wrap
并不是用两个或者更多的变量来生成一个2维网格,而是先生成一个长的面板条块(由任意数目的变量生成),然后将它封装在2维中。
分面变量的设置形式为 ~ a + b + c。facet_wrap
默认把图形面板尽可能摆成方形,且更偏好于宽扁形的矩形。可以通过设置ncol,nrow来更新默认设置。
标度控制
对于两种分面,可以通过调整参数scales来控制面板的位置标度是相同(固定)还是允许变化(自由):
- scales = “fixed”: x和y的标度在所有面板中都相同。
- scales = “free”: x和y的标度在每个面板都可以变化。
- scales = “free_x”: x的标度可变,y的尺度固定。
- scales = “free_y”: y的标度可变,x的尺度固定。
下面给出固定标度与自由标度间的差异:
p <- qplot(cty, hwy, data = mpg)
p + facet_wrap(~ cyl)
p + facet_wrap(~ cyl, scales = "free")
为展示自由标度的效果,我们首先需要把数据从“宽”变“长”,把不同的变量堆叠在一列中。下面给出例子:
library(reshape2)
em <- melt(economics, id = "date")
qplot(date, value, data = em, geom = "line", group = variable) + facet_grid(variable ~ ., scale = "free_y")
分面变量缺失
ggplot2会在缺失的分面变量按包含该分面变量的所有的值来处理。
分组与分面
与通过调整图形属性(比如颜色、形状或大小)来分组不同,分面提供了另外一种分组途径。依据子集相对位置的不同,这两种绘图技巧都有相应的优缺点。下面给个例子:
xmaj <- c(0.3, 0.5, 1, 3, 5)
xmin <- as.vector(outer(1:10, 10^c(-1, 0)))
ymaj <- c(500, 1000, 5000, 10000)
ymin <- as.vector(outer(1:10, 10^c(2, 3, 4)))
dplot <- ggplot(subset(diamonds, color %in% c("D", "E", "G", "J")), aes(carat, price, colour = color)) + scale_x_log10(breaks = xmaj, labels = xmaj, minor = xmin) + scale_y_log10(breaks = ymaj, labels = ymaj, minor = ymin) + scale_colour_hue(limits = levels(diamonds$color)) + theme(legend.position = "none")
dplot + geom_point()
dplot + geom_point() + facet_grid(. ~ color)
dplot + geom_smooth(method = lm, se = F, fullrange = T)
dplot + geom_smooth(method = lm, se = F, fullrange = T) + facet_grid(. ~ color)
并列与分面
分面可以绘制出于图形并列类似的图形效果。但也有不同,下面给出一个例子:
qplot(color, data = diamonds, geom = "bar", fill = cut, position = "dodge")
qplot(cut, data = diamonds, geom = "bar", fill = cut) + facet_grid(. ~ color) + theme(axis.text.x = element_text(angle = 90, hjust = 1, size = 8, colour = "grey50"))
除了上面例子所示的标注方式外,当两个变量的因子水平几乎完全交叉,而部分变量组合缺失时,两种绘图方式也就会有所不同。此时,并列图形的用处不大,因为它只是对条形图做局部地分割,没有任何标签。而分面就实用得多,它能控制分割方式是局部的(scales = “free_x”,space = “free”)还是全局的(scales = “fixed”)。下面给出一个例子:
mpg4 <- subset(mpg, manufacturer %in% c("audi", "volkswagen", "jeep"))
mpg4$manufacturer <- as.character(mpg4$manufacturer)
mpg4$model <- as.character(mpg4$model)
base <- ggplot(mpg4, aes(fill = model)) + geom_bar(position = "dodge") + theme(legend.position = "none")
base + aes(x = model) + facet_grid(. ~ manufacturer)
last_plot() + facet_grid(. ~ manufacturer, scales = "free_x", space = "free")
base + aes(x = manufacturer)
总之,图形是选择分面还是并列,要视两变量间的关系而定:
- 水平完全交叉: 分面和并列基本等同。
- 水平几乎交叉: 有相同标度的分面保证了所有的水平组合可见,即使有些是空的。当存在非结构性的缺失组合时,绘制分面图形非常有用。
- 水平无交叉(嵌套): 标度自由的分面会对每个有较高水平的组别分配充足的作图空间,并对每个条目都进行标注。
连续型变量
对连续型变量进行分面,首先需要将其变换为离散型。有三种方法:
- 将数据分为n个长度相同的部分:用
cut_interval(x, n = 10)
控制划分数目,或用cut_interval(x, length = 1
控制每个部分的长度。 - 将数据划分为n个有相同数目点的部分:
cut_number(x, n = 10)
。这使得分面间进行对比会更容易(分面有相同数目的点),但需要注意每个部分的标度范围是不同的。
下面的例子展示三种转化方式:
mpg2$disp_ww <- cut_interval(mpg2$displ, length = 1)
mpg2$disp_wn <- cut_interval(mpg2$displ, n = 6)
mpg2$disp_nn <- cut_number(mpg2$displ, n = 6)
plot <- qplot(cty, hwy, data = mpg2) + labs(x = NULL, y = NULL)
plot + facet_wrap(~ disp_ww, nrow = 1)
plot + facet_wrap(~ disp_wn, nrow = 1)
plot + facet_wrap(~ disp_nn, nrow = 1)
坐标系
坐标系是将两种位置标度结合在一起组成的2维定位系统。坐标系主要有两大功能:
- 将2个位置图形属性组合起来在图形中形成2维方位系统。
- 配合分面,坐标系将绘出坐标轴和面板背景。
变换
坐标系变换分为两步。
- 首先,几何形状的参数变化只依据定位,而不是定位和维度。
- 将几何形状变为基于定位的表现形式后,下一步就是将每个位置转化到新的坐标系中。
笛卡尔坐标系
有四种基于笛卡尔的坐标系:coord_cartesian,coord_equal,coord_flip和coord_trans,由于x和y的位置都是正交映射到图形的位置上,因此四种坐标系本质上仍是笛卡尔型的,它们之间有许多共同之处。
设置范围
coord_cartesian有两个参数xlim和ylim。标度中也有范围参数,两者关键的不同之处在于范围参数的工作原理:当设定标度范围时,任何超出此范围的数据都会被删除;但当设定笛卡尔坐标系的范围时,我们使用的仍是所以的数据,只不过展示一小片图形区域。下面给出例子:
(p <- qplot(disp, wt, data = mtcars) + geom_smooth()) ## 完整的数据集
p + scale_x_continuous(limits = c(325, 500)) ## x的标度范围设置为(325, 500)
p + coord_cartesian(xlim = c(325, 500)) ## 坐标系x轴范围设置为(325, 500)
(d <- ggplot(diamonds, aes(carat, price)) + stat_bin2d(bins = 25, colour = "grey70") + theme(legend.position = "none")) ## 完整的数据集
d + scale_x_continuous(limits = c(0, 2)) ## x的标度范围设置为(0, 2)
d + coord_cartesian(xlim = c(0, 2)) ## 坐标系的x轴设置为(0, 2)
坐标轴翻转
假如对y值条件下的x值感兴趣(或仅仅想将图形翻转90度),我们可以使用coord_flip调换x和y轴。下面给出例子:
qplot(displ, cty, data = mpg) + geom_smooth()
qplot(cty, displ, data = mpg) + geom_smooth()
qplot(cty, displ, data = mpg) + geom_smooth() + coord_flip()
变换
标度层面的变换发生在统计量计算之前,且不会改变对象的几何形状。但坐标系层面的变换却发生在统计量计算之后,会影响几何形状。若两种变换一起使用,可先在变换的尺度上建模,然后再反演到变换前的图形以便于解释,这是常见的分析模式。
qplot(carat, price, data = diamonds, log = "xy") + geom_smooth(method = "lm")
library(scales)
last_plot() + coord_trans(x = exp_trans(10), y = exp_trans(10))
非笛卡尔坐标系
非笛卡尔坐标系有两种:极坐标和地图投影。
极坐标
下面给出利用极坐标生成图的例子:
(pie <- ggplot(mtcars, aes(x = factor(1), fill = factor(cyl))) + geom_bar(width = 1)) ## 堆叠条状图
pie + coord_polar(theta = "y")
pie + coord_polar()
地图投影
地图仍处在试验阶段,依赖于mapproj包(Mcllroy,2005)。
参考文献
[1][ggplot2:数据分析与图形艺术]