ggplot2定位

简介

本文主要阐述ggplot2中图形的定位,主要介绍布局分面和坐标系如何工作两个问题。

  • 分面 即在一个页面上自动摆放多幅图形的技法:先将数据划分为多个子集,然后将每个子集依次绘制到页面的不同面板中。
  • 坐标系 即通过控制两个独立的位置标度来生成一个2维的坐标系。

分面

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

id

一行多列:”. ~ a”。电脑屏幕较宽,这个方向最适合数据的展示。另外纵坐标想同,有助于y位置的比较。

qplot(cty, hwy, data = mpg2) + facet_grid(. ~ cyl)

id

一列多行:”b ~ .”。横坐标轴相同,利于x位置的比较,尤其是对数据分布的比较。

qplot(cty, data = mpg2, geom = "histogram", binwidth = 2) + facet_grid(cyl ~ .)

id

多行多列:”a ~ b”。通常都将因子水平数目最大的变量按列排放,充分利用屏幕的宽高比。

qplot(cty, hwy, data = mpg2) + facet_grid(drv ~ cyl)

id

切割图形就好比创建一个列联表。列联表可展示每个单元格的值以及边际和(一个行或列的总和)。这种模式对于绘图也非常有用,可以使用参数margins来绘制边际图。

p <- qplot(displ, hwy, data = mpg2) + geom_smooth(method = "lm", se = F)
p + facet_grid(cyl ~ drv)

id

p + facet_grid(cyl ~ drv, margins = T)

id

qplot(displ, hwy, data = mpg2) + geom_smooth(aes(colour = drv), method = "lm", se = F) + facet_grid(cyl ~ drv, margins = T)

id

封装分面

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)

id

p + facet_wrap(~ cyl, scales = "free")

id

为展示自由标度的效果,我们首先需要把数据从“宽”变“长”,把不同的变量堆叠在一列中。下面给出例子:

library(reshape2)
em <- melt(economics, id = "date")
qplot(date, value, data = em, geom = "line", group = variable) + facet_grid(variable ~ ., scale = "free_y")

id

分面变量缺失

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

id

dplot + geom_point() + facet_grid(. ~ color)

id

dplot + geom_smooth(method = lm, se = F, fullrange = T)

id

dplot + geom_smooth(method = lm, se = F, fullrange = T) + facet_grid(. ~ color)

id

并列与分面

分面可以绘制出于图形并列类似的图形效果。但也有不同,下面给出一个例子:

qplot(color, data = diamonds, geom = "bar", fill = cut, position = "dodge")

id

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

id

除了上面例子所示的标注方式外,当两个变量的因子水平几乎完全交叉,而部分变量组合缺失时,两种绘图方式也就会有所不同。此时,并列图形的用处不大,因为它只是对条形图做局部地分割,没有任何标签。而分面就实用得多,它能控制分割方式是局部的(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)

id

last_plot() + facet_grid(. ~ manufacturer, scales = "free_x", space = "free")

id

base + aes(x = manufacturer)

id

总之,图形是选择分面还是并列,要视两变量间的关系而定:

  • 水平完全交叉: 分面和并列基本等同。
  • 水平几乎交叉: 有相同标度的分面保证了所有的水平组合可见,即使有些是空的。当存在非结构性的缺失组合时,绘制分面图形非常有用。
  • 水平无交叉(嵌套): 标度自由的分面会对每个有较高水平的组别分配充足的作图空间,并对每个条目都进行标注。

连续型变量

对连续型变量进行分面,首先需要将其变换为离散型。有三种方法:

  1. 将数据分为n个长度相同的部分:用cut_interval(x, n = 10)控制划分数目,或用cut_interval(x, length = 1控制每个部分的长度。
  2. 将数据划分为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)

id

plot + facet_wrap(~ disp_wn, nrow = 1)

id

plot + facet_wrap(~ disp_nn, nrow = 1)

id

坐标系

坐标系是将两种位置标度结合在一起组成的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())       ## 完整的数据集

id

p + scale_x_continuous(limits = c(325, 500))        ## x的标度范围设置为(325, 500)

id

p + coord_cartesian(xlim = c(325, 500))         ## 坐标系x轴范围设置为(325, 500)

id

(d <- ggplot(diamonds, aes(carat, price)) + stat_bin2d(bins = 25, colour = "grey70") + theme(legend.position = "none"))         ## 完整的数据集

id

d + scale_x_continuous(limits = c(0, 2))        ## x的标度范围设置为(0, 2)

id

d + coord_cartesian(xlim = c(0, 2))         ## 坐标系的x轴设置为(0, 2)

id

坐标轴翻转

假如对y值条件下的x值感兴趣(或仅仅想将图形翻转90度),我们可以使用coord_flip调换x和y轴。下面给出例子:

qplot(displ, cty, data = mpg) + geom_smooth()

id

qplot(cty, displ, data = mpg) + geom_smooth()

id

qplot(cty, displ, data = mpg) + geom_smooth() + coord_flip()

id

变换

标度层面的变换发生在统计量计算之前,且不会改变对象的几何形状。但坐标系层面的变换却发生在统计量计算之后,会影响几何形状。若两种变换一起使用,可先在变换的尺度上建模,然后再反演到变换前的图形以便于解释,这是常见的分析模式。

qplot(carat, price, data = diamonds, log = "xy") + geom_smooth(method = "lm")

id

library(scales)
last_plot() + coord_trans(x = exp_trans(10), y = exp_trans(10))

id

非笛卡尔坐标系

非笛卡尔坐标系有两种:极坐标和地图投影。

极坐标

下面给出利用极坐标生成图的例子:

(pie <- ggplot(mtcars, aes(x = factor(1), fill = factor(cyl))) + geom_bar(width = 1))       ## 堆叠条状图

id

pie + coord_polar(theta = "y")

id

pie + coord_polar()

id

地图投影

地图仍处在试验阶段,依赖于mapproj包(Mcllroy,2005)。

参考文献

[1][ggplot2:数据分析与图形艺术]