本章代码,欢迎下载,有的稍微优化了一点。
第一章 简介
ggplot2
ggplot2基于Leland Wilkinson在Grammar of Graphics(图形的语法)中提出的理论,取首字母缩写再加上plot,于是得名ggplot。按照《图形的语法》一书中的观点,一张统计图形就是从数据到点、线或方块等几何对象的颜色、形状或大小等图形属性的一个映射,其中还可能包含对数据进行统计变换(如求均值或方差),最后将这个映射绘制在一定的坐标系中就得到了我们需要的图形。图中可能还有分面,就是生成关于数据的不同子集的图形。使用ggplot2绘图的过程就是选择合适的几何对象、图形属性和统计变换来充分暴露数据中所含有的信息的过程。ggplot2需要一定的时间去入门学习,但是当你掌握了ggplot2中图形的语法的时候,我相信你会感受到这套语法的优雅。
安装
install.packages("ggplot2")
第二章 从qplot开始入门
2.1 简介
qplot意为快速作图(quick plot),利用它你可以很方便地创建各种复杂的图形。有时在其他图形系统中需要好几行代码才能解决的作图问题,用qplot()只需要- -行就能完成。qplot() 之所以有这样的能力,是因为它基于图形语法,这使得你能用简单的、富有表达能力的语句来描述- - 张图形。在之后的章节中你将学习到完整的语法,而在这里我们先介绍简单的内容,以便你能快速入门。在本章中你将会同样学习到一些贯穿全书的ggplot2 术语。
qplot()被设计得与plot()很像,因此如果你已经对R中的作图比较熟悉,那么用起它来也会很容易。记住,在一次R会话中,你可以通过R的帮助命令?qplot
来获取qplot()的完整参数列表。
2.2 数据集
本章大部分的绘图都将只使用一一个数据源,这样你可以更好地熟悉作图的细节,而不需要去熟悉各种不同的数据集。diamonds 数据集包含了约54000颗钻石的价格和质量的信息,数据已经放在了ggplot2软件包中。这组数据涵盖了反映钻石质量的四个“C”一-克拉重量 (carat)、切工(cut)、颜色(color) 和净度(clarity), 以及五个物理指标一深度 (depth)、钻面宽度(table)、 x、y和z。
这个数据集没有经过很好的整理,所以在展示钻石一些有趣的关系时,它会显示出一些数据质量的问题。我们将同时使用另- -个数据集,dsmall, 它是原始数据的一一个容量为100的随机样本。我们将用这个数据集来进行小数据的作图展示。
使用以下代码创建dsmall数据集
set.seed(1410)
dsmall <- diamonds[sample(nrow(diamonds),100),]
2.3 基本用法
qplot(x, y, [data])
x和y分别代表所画对象的x坐标和y坐标。data参数是可选的,如果进行指定,那么qplot()将会首先在该数据框内查找该变量名,然后在R的工作空间中进行搜索。本书推荐使用data参数。
一个简单地例子:
qplot(carat, price, data = diamonds)
2.4 颜色、大小、形状和其他图形属性
qplot与plot的第一个大的差别在于它们在给图中的点设定颜色(或大小、形状)时采用了不同的实现方式。在plot中,用户需要将数据中的一个分类变量(例如,“苹果”、“香蕉”、“桃子”)转换为plot可以理解的形式(例
如,“red”、“yellow”、 “green” )。而qplot可以将这个过程自动完成,并能够自动生成一-张图例,用以展示数据取值与图形属性之间的对应关系。这使得向图中添加额外的信息非常简便。
下面两个例子中,我们向重量和价格的散点图添加了颜色和切工的信息。
qplot(carat, price, data = dsmall, colour = color)
qplot(carat, price, data = dsmall, shape = cut)
将两幅图放在同一排,需要调用gridExtra
包,,同时需要修改画板大小:
install.packages("gridExtra")
library(gridExtra)
p1 <- qplot(carat, price, data = dsmall, colour = color)
p2 <- qplot(carat, price, data = dsmall, shape = cut)
p <- grid.arrange(p1, p2, ncol=2)
ggsave(file = "test1.png", plot = p, width=8, height=4)
颜色、大小和形状是图形属性的具体例子,它们都是影响数据如何进行展示的视觉属性。每一个图形属性都对应了一个称为标度的函数,其作用是将数据的取值映射到该图形属性的有效取值。在ggplot2中,标度控制了点以及对应的图例的外观。例如,在上述图形中,颜色标度将J映射为紫色,将F映射为绿色。
你同样可以利用I()来手动设定图形属性,例如,(colour = I("red")或size = I(2)。这与之前解释的映射不同,其中更详细的内容会在4.5.2节进行介绍。对于大数据(如本例中的钻石数据)而言,使用半透明的颜色可以有效减轻图形元素重叠的现象。要创建半透明的颜色,你可以使用alpha 图形属性,其取值从0 (完全透明)变动到1 (完全不透明)。通常透明度可以用分数来进行表示,例如1/10或1/20,其分母表示经过多少次重叠之后颜色将变得不透明。
qplot(carat, price, data = diamonds, colour = color, alpha = I(1/200))
不同类型的变量有不同适用的图形属性。例如,颜色和形状适合于分类变量,而大小适合于连续变量。数据量的大小同样会有影响:如果数据量很大(如上图),那么不同组的数据之间就很难进行区分。对此,- -种可能的解决方案是利用分面,这在2.6节会有介绍。
2.5 几何对象
qplot并非只能画散点图,通过改变几何对象(简写为geom),它几乎可以画出任何一种类型的图形。
下图是ggplot2()函数中的添加不同类型的几何对象的对应的函数,对应于qplot,就是把geom_type
替换为gemo = "type"
。下面将仔细讲解。
2.5.1 向图中添加平滑曲线
如果在散点图中有非常多的数据点,那么数据展示的趋势可能并不明显,在这种情况下你应该在图中添加一.条平滑曲线。这可以通过使用smooth几何对象加以完成,如图2.4所示。注意到我们利用了c()函数来将多个几何对象组成一个向量传递给geom。几何对象会按照指定的顺序进行堆叠。
如果不想绘制标准误,则可以使用se = FALSE
。
qplot(carat, price, data = dsmall, geom =c("point", "smooth"))
qplot(carat, price, data = dsmall, geom =c("point", "smooth"), span = 0.2)
利用method参数你可以选择许多不同的平滑器:
- method = "loess"
当n较小时是默认选项,使用的是局部回归的方法。曲线的平滑程度是有span参数控制的,其取值范围是从0(很不平滑)到1(很平滑),默认为1,如上图所示
Loess对于大数据并不十分适用(内存消耗是O(n2)),因此当n超过1000时将默认采用另一种平滑算法。
- method = "gam"
对于大数据,要调用mgcv包拟合一个广义可加模型。请使用公式y ~ s(x, bs = "cs")
,这是数据量超过1000时默认适用的选项。
qplot(carat, price, data = dsmall, geom =c("point", "smooth"), method = "gam", formula = y ~ s(x, bs = "cs"))
- method = "lm"
拟合的是线性模型。也可以通过制定formula = y ~ poly(x, 2)
来拟合一个二次多项式。
- method = "rlm"
与lm类似,但采用了一种更稳健的拟合算法,使得结果对异常值不太敏感。这一方法是MASS包的一部分,因此需要先加载MASS包。
2.5.2 箱线图和扰动点图
如果一个数据集中包含了一个分类变量和一一个或多个连续变量,那么你可能会想知道连续变量会如何随着分类变量水平的变化而变化。箱线图和扰动点图提供了各自的方法来达到这个目的,图2.8展示了钻石每克拉的价格随颜色的变化情况,左图的扰动点图使用的是geoma = "jitter", 右图的箱线胡须图使用的是geom = "boxplot" 。
p1 <- qplot(color , price/carat, data = diamonds, geom ="jitter")
p2 <- qplot(color , price/carat, data = diamonds, geom ="boxplot")
p <- grid.arrange(p1, p2, ncol=2)
ggsave(file = "test1.png", plot = p, width=8, height=4)
两种图各有优势,但很显然箱线图能够给出更有价值的信息:它显示分布的中位数和四分位数都没有太大的变化。
扰动点的图形重叠问题可以通过降低点的透明度来部分解决(alpha参数)。
2.5.3 直方图和密度曲线图
直方图和密度曲线图可以展示单个变量的分布,相对于箱线图而言,它们提供了更多的关于单个分布的信息,但它们不太容易在不同组之间进行比较(尽.管我们将会看到- -种可行的办法)。图2.10展示了钻石重量的直方图和密度曲线图。
qplot(carat, data = diamonds, geom = "histogram")
qplot(carat, data = diamonds, geom = "density")
对于密度曲线图而言,adjust 参数控制了曲线的平滑程度(adjust 取值越大,曲线越平滑)。对于直方图,binwidth 参数通过设定组距来调节平滑度。(切分位置同样可以通过breaks参数进行显式的指定。) 绘制直方图或密度曲线
时,对平滑程度进行试验非常重要。在直方图中,你应该尝试多种组距:当组距较大时,图形能反映数据的总体特征;当组距较小时,则能显示出更多的细节。
p1 <- qplot(carat, data = diamonds, geom = "histogram")
p2 <- qplot(carat, data = diamonds, geom = "density")
p <- grid.arrange(p1, p2, ncol=2)
ggsave(file = "test1.png", plot = p, width=8, height=4)
对于密度曲线图而言,adjust 参数控制了曲线的平滑程度(adjust 取值越大,曲线越平滑)。对于直方图,binwidth参数通过设定组距来调节平滑度。(切分位置同样可以通过breaks参数进行显式的指定。) 绘制直方图或密度曲线
时,对平滑程度进行试验非常重要。在直方图中,你应该尝试多种组距:当组距较大时,图形能反映数据的总体特征;当组距较小时,则能显示出更多的细节。
p1 <- qplot(carat, data = diamonds, geom = "density", colour = color)
p2 <- qplot(carat, data = diamonds, geom = "histogram", fill = color)
p <- grid.arrange(p1, p2, ncol=2)
ggsave(file = "test1.png", plot = p, width=8, height=4)
当一个分类变量被映射到某个图形属性上,几何对象会自动按这个变量进行拆分。左图是重叠的密度曲线图,右图是堆叠起来的直方图。密度曲线图在第一一眼看来更吸引人,因为它似乎很容易阅读,而且适于在.不同的曲线之间进行比较。
然而,要真正理解密度曲线的含义则比较困难,而且密度曲线有一些隐含的假设,例如曲线应该是无界、连续和平滑的,而这些假设不- -定适用于真实的数据。
2.5.4 条形图
geom = "bar"
2.5.5时间序 列中的线条图和路径图
线条图和路径图常用于可视化时间序列数据。线条图将点从左到右进行连接,而路径图则按照点在数据集中的顺序对其进行连接(线条图就等价于将数据:按照x取值进行排序,然后绘制路径图)。线条图的x轴一般是时间,它展示了
单个变量随时间变化的情况。路径图则展示了两个变量随时间联动的情况,时间反映在点的顺序上。
由于钻石数据中没有包含时间变量,因此在这里我们使用economics数据集,它包含了美国过去40年的经济数据。图2.14展示了失业水平随时间变化的两张线条图,它们是用geom = "line" 进行绘制的。第- -张图 显示了失业率的变化,第二张图是失业星期数的中位数。我们已经可以看出这两个变量之间的一-些区别,例如在最后的一个峰值处,失业的比例要比前一个峰值低,但失业的时间却要更长。
p1 <- qplot(date, unemploy / pop, data = economics, geom = "line")
p2 <- qplot(date, uempmed, data = economics, geom = "line")
p <- grid.arrange(p1, p2, ncol=2)
ggsave(file = "test1.png", plot = p, width=12, height=4)
要考察这种关系的更多细节,我们可以将两个时间序列绘制在同一张图中。
尽管我们可以用一张散点图来表示失业率和失业时间长度之间的关系,但我们并不能从中看出变量随时间的变化。对此,解决的办法是将临近时点的散点连接起来,形成- -张路径图。在下面我们画出了失业率和失业时间长度随时间变化的路径。由于线条有很多交叉,因此在第一张图中时间变化的方向并不明显。在第二张图中,我们将年份映射到了colour属性上,这让我们更容易地看出时间的行进方向。
year <- function(x) as.POSIXlt(x)$year + 1900
p1 <- qplot(unemploy / pop, uempmed, data = economics, geom = c("point", "path"))
p2 <- qplot(unemploy / pop, uempmed, data = economics, geom = "path", colour = year(date))
p <- grid.arrange(p1, p2, ncol=2)
ggsave(file = "test1.png", plot = p, width=12, height=4)
上图展示失业率和失业时间长度之间关系的路径图。左图是重叠在一起的的散点图和路径图,右图只有路径图,其中年份用颜色进行了展示。
我们可以看出来失业率和失业时间长度是高度相关的,尽管最近几年,失业时间长度与失业率相比有增长的趋势。
对于纵向数据,你可能想把多个时间序列画在同- -张中,每一个序列代表一个个体。要用qplot()实现这一想法,你需要将group图形属性映射到一个表示分组的变量之上。这一部分内容在4.5.3 节有更深人的介绍。
2.6 分面
我们已经讨论了利用图形属性(颜色和形状)来比较不同分组的方法,它可以将所有的组绘制在同一张图中。分面是另外一种实现的方法:它将数据分割成若干子集,然后创建-一个 图形的矩阵,将每-一个子集绘制到图形矩阵的窗格
中。所有子图采用相同的图形类型,并进行了- -定的设计,使得它们之间方便进行比较。7.2节详细讨论了分面,包括它相对于图形属性分组的优势和劣势
qplot()中默认的分面方法是将图形拆分成若千个窗格,这可以通过形如row_ var ~ col. var的表达式进行指定。你可以指定任意数量的行变量和列变量,但请注意当变量数超过两个时,生成的图形可能会非常大,以至于不适合在屏幕上显示。如果只想指定- -行或一列,可以使用.作为占位符,例如row. _var ~ . 会创建-一个单列多行的图形矩阵。
下面的示例展示了这个技巧,它们是以颜色为条件的重量的直方图。左边一列直方图的y轴并不是原始数据的取值,而是将数据进行分组后的计数; . . density..则是一个新的语法,它告诉ggplot2将密度而不是频数映射到y轴。
2.7 其他选项
qplot中还有一些其他的选项用于控制图形的外观。这些参数与它们在plot中的作用相同:
- xlim, ylim: 设置x轴和y轴的显示区间,它们的取值都是一个长度为2的数值向量,例如xlim = c(0, 20) 或ylim = c(-0.9, -0.5);
- log:一个字符型向量,说明哪- -个坐标轴(如果有的话)应该取对数。例如,log="x"表示对x轴取对数,1og="xy"表示对x轴和y轴都取对数;
- main:
图形的主标题,放置在图形的顶端中部,以大字号显示。该参数可以是一个字符串(例如,main = "plot title") 或-一个表达式(例如main= expression(beta[1] = 1))。 可以运行?plotmath命令来查看更多的数学表达式的例子; - xlab, ylab: 设置x轴和y轴的标签文字,与主标题-样,这两个参数的取值可以是字符串或数学表达式。
下面的例子展示了这些选项的实际操作。
p1 <- qplot(carat, price, data = dsmall,
xlab = "Price ($)", ylab = "Weight (carat)",
main = "Price-weight relationship",
geom = c("point", "smooth"), colour = color)
p2 <- qplot(carat, price, data = dsmall, log ="xy",
geom = c("point", "smooth"), colour = color)
p <- grid.arrange(p1, p2, nrow=2)
ggsave(file = "test1.png", plot = p, width=4, height=6)
2.8 与plot函数的区别
- qplot 不是泛型函数①:当你将不同类型的R对象传入qplot时,它并不会匹配默认的函数调用。需要注意的是,ggplot() 是一个泛型函数,以它为起点,你可以对任意类型的R对象进行可视化操作。第9章对此有详细的描述;
①译者注: 0.9.0 版本之后引人了新的泛型函数autoplot, 此函数用来根据特定数据结构生成特定的完整图形。其默认定义是报错,该函数允许开发人员在其他包中对其针对不同对象进行不同定义,从而可以实现面向对象的图形设计。其功能类似于qplot,可以同样封装所需的参数,区别是当你将不同类型的R对象传入autoplot时,它可以自动匹配默认的函数调用。
- 一般而言,你可以将-一个变量传递给你感兴趣的图形属性,这样该变量将进行标度转换并显示在图例上。如果你想对其进行赋值,比如让点的颜色变为红色,则可以使用I()函数: colour = I("red")。 这部分内容在4.5.2节中有进一步的介绍 ;
- ggplot2中的图形属性名称(如colour, shape和size) 比基础绘图系统中的名称(如col, pch和cex等)更直观,且更容易记忆;
- 在基础绘图系统中,你可以通过points(), lines() 和text ()函数来向已有的图形中添加更多的元素。而在ggplot2中,你需要在当前的图形中加人额外的图层,这将在下一章中进行介绍。