我曾经多次向客户展示以数字和文字表示的、精心整理的统计分析结果,得到的只是客户呆滞的眼神,尴尬得房间里只能听到鸟语虫鸣。然而,当我使用图形向相同的用户展示相同的信息时,他们往往会兴致盎然,甚至豁然开朗。我经常通过看图才得以发现了数据中的模式,或是检查出了数据中的异常值——这些模式和异常都是在我进行更为正式的统计分析时彻底遗漏的。
人类非常善于从视觉呈现中洞察关系。一幅精心绘制的图形能够帮助你在数以千计的零散信息中做出有意义的比较,提炼出使用其他方法时不那么容易发现的模式。这也是统计图形领域的进展能够对数据分析产生重大影响的原因之一。数据分析师需要观察他们的数据,而R在该领域表现出众。
在本章中,我们将讨论处理图形的一般方法。我们首先探讨如何创建和保存图形,然后关注如何修改那些存在于所有图形中的特征,包括图形的标题、坐标轴、标签、颜色、线条、符号和文本标注。我们的焦点是那些可以应用于所有图形的通用方法。(在后续各章,我们将关注特定类型的图形。)最后,我们将研究组合多幅图形为单幅图形的各种方法。
3.1 使用图形
R是一个惊艳的图形构建平台。这里我特意使用了构建一词。在通常的交互式会话中,你可以通过逐条输入语句构建图形,逐渐完善图形特征,直至得到想要的效果。
考虑以下五行代码:
代码:
attach(mtcars) #绑定数据框mtcars
plot(wt, mpg)#打开了一个图形窗口并生成了一幅散点图,横轴表示车身重量,纵轴为每加仑汽油行驶的英里数。
abline(lm(mpg~wt))#添加了一条最优拟合曲线
title("Regression of MPG on Weight")#添加了标题
detach(mtcars)# 数据框解除了绑定
结果:
可以通过代码或图形用户界面来保存图形。要通过代码保存图形,将绘图语句夹在开启目标图形设备的语句和关闭目标图形设备的语句之间即可。例如,以下代码会将图形保存到当前工作目录中名为mygraph.pdf的PDF文件中:
代码:
pdf("mygraph.pdf")
attach(mtcars)
plot(wt, mpg)
abline(lm(mpg~wt))
title("Regression of MPG on Weight")
detach(mtcars)
dev.off()
除了pdf(),还可以使用函数:
**win.metafile()、
png()、jpeg()、
bmp()、
tiff()、
xfig()
postscript()**将图形保存为其他格式。
3.2 简单案例
让我们从表3-1中给出的假想数据集开始。它描述了病人对两种药物五个剂量水平上的响应情况。
代码:
dose <- c(20, 30, 40, 45, 60)
drugA <- c(16, 20, 27, 40, 60)
drugB <- c(15, 18, 25, 31, 40)
plot(dose, drugA, type="b")
结果:
plot(x, y, type="b")
x置于横轴
y置于纵轴,绘制点集(x, y),然后使用线段将其连接。
选项type="b"表示同时绘制点和线。使用help(plot)可以查看其他选项。
3.3 图形参数
我们可以通过修改称为图形参数的选项来自定义一幅图形的多个特征(字体、颜色、坐标轴、标签)。一种方法是通过函数par()来指定这些选项。以这种方式设定的参数值除非被再次修改,否则将在会话结束前一直有效。
参数设置格式:
par(optionname=value, optionname=name,...)
**不加参数地执行par()将生成一个含有当前图形参数设置的列表。添加参数no.readonly=TRUE可以生成一个可以修改的当前图形参数列表。**
代码:
dose <- c(20, 30, 40, 45, 60)
drugA <- c(16, 20, 27, 40, 60)
drugB <- c(15, 18, 25, 31, 40)
plot(dose, drugA, type="b")
opar <- par(no.readonly=TRUE) # 复制了一份当前的图形参数设置
par(lty=2, pch=17) #将默认的线条类型修改为虚线(lty=2)并将默认的点符号改为了实心三角(pch=17)
plot(dose, drugA, type="b")
par(opar) #还原原始设置
结果:
你可以随心所欲地多次使用par()函数,即**par(lty=2, pch=17)**也可以写成:
par(lty=2)
par(pch=17)
指定图形参数的第二种方法是为高级绘图函数直接提供optionname=value的键值对。这种情况下,指定的选项仅对这幅图形本身有效。你可以通过代码:
plot(dose, drugA, type="b", lty=2, pch=17)
来生成与上图相同的图形。
并不是所有的高级绘图函数都允许指定全部可能的图形参数。你需要参考每个特定绘图函数的帮助(如?plot、?hist或?boxplot)以确定哪些参数可以以这种方式设置。下面介绍可以设定的许多重要图形参数。
3.3.1 符号和线条
代码:
plot(dose, drugA, type="b", lty=3, lwd=3, pch=15, cex=2)
结果:
3.3.2 颜色
3.3.3 文本属性
3.3.4 图形尺寸与边界尺寸
par(pin=c(4,3), mai=c(1,.5, 1, .2))
**可生成一幅4英寸宽、3英寸高、上下边界为1英寸、左边界为0.5英寸、右边界为0.2英寸的图形。关于边界参数的完整指南,不妨参阅 Earl F. Glynn 编写的一份全面的在线教程
(http://research.stowers-institute.org/efg/R/Graphics/Basics/mar-oma/)。**
代码:
drugA <- c(16, 20, 27, 40, 60)
drugB <- c(15, 18, 25, 31, 40)
opar <- par(no.readonly=TRUE)
par(pin=c(2, 3))
par(lwd=2, cex=1.5)
par(cex.axis=.75, font.axis=3)
plot(dose, drugA, type="b", pch=19, lty=2, col="red")
plot(dose, drugB, type="b", pch=23, lty=6, col="blue", bg="green")
par(opar)
结果:
**代码解析:**首先,你以向量的形式输入了数据,然后保存了当前的图形参数设置(这样就可以在稍后恢复设置)。接着,你修改了默认的图形参数,得到的图形将为2英寸宽、3英寸高。除此之外,线条的宽度将为默认宽度的两倍,符号将为默认大小的1.5倍。坐标轴刻度文本被设置为斜体、缩小为默认大小的75%。之后,我们使用红色实心圆圈和虚线创建了第一幅图形,并使用绿色填充的绿色菱形加蓝色边框和蓝色虚线创建了第二幅图形。最后,我们还原了初始的图形参数设置。
值得注意的是,通过par()设定的参数对两幅图都有效,而在plot()函数中指定的参数仅对那个特定图形有效。
3.4 添加文本、自定义坐标轴和图例
除了图形参数,许多高级绘图函数(例如plot、hist、boxplot)也允许自行设定坐标轴和文本标注选项。举例来说,以下代码在图形上添加了标题(main)、副标题(sub)、坐标轴标签(xlab、ylab)并指定了坐标轴范围(xlim、ylim)。
代码:
plot(dose, drugA, type="b",
col="red", lty=2, pch=2, lwd=2,
main="Clinical Trials for Drug A",
sub="This is hypothetical data",
xlab="Dosage", ylab="Drug Response",
xlim=c(0, 60), ylim=c(0, 70))
结果:
3.4.1 标题
可以使用title()函数为图形添加标题和坐标轴标签。
格式:
title(main="main title", sub="subtitle",
xlab="x-axis label", ylab="y-axis label")
函数title()中亦可指定其他图形参数(如文本大小、字体、旋转角度和颜色)。举例来说,以下代码将生成红色的标题和蓝色的副标题,以及比默认大小小25%的绿色x轴、y轴标签:
title(main="My Title", col.main="red",
sub="My Subtitle", col.sub="blue",
xlab="My X label", ylab="My Y label",
col.lab="green", cex.lab=0.75)
函数title()一般来说被用于添加信息到一个默认标题和坐标轴标签被ann=FALSE选项移除的图形中。
3.4.2 坐标轴
你可以使用函数axis()来创建自定义的坐标轴,而非使用R中的默认坐标轴。
格式:
axis(side, at=, labels=, pos=, lty=, col=, las=, tck=, ...)
创建自定义坐标轴时,你应当禁用高级绘图函数自动生成的坐标轴。参数axes=FALSE将禁用全部坐标轴(包括坐标轴框架线,除非你添加了参数frame.plot=TRUE)。参数xaxt="n"和yaxt="n"将分别禁用X轴或Y轴(会留下框架线,只是去除了刻度)。
代码:
x <- c(1:10)
y <- x
z <- 10/x
opar <- par(no.readonly=TRUE)
par(mar=c(5, 4, 4, 8) + 0.1) #增加边界大小
plot(x, y, type="b",
pch=21, col="red",
yaxt="n", lty=3, ann=FALSE) #绘制x对y的图形
lines(x, z, type="b", pch=22, col="blue", lty=2) #添加x对1/x的直线
axis(2, at=x, labels=x, col.axis="red", las=2) #添加坐标轴
axis(4, at=z, labels=round(z, digits=2),
col.axis="blue", las=2, cex.axis=0.7, tck=-.01) # 添加坐标轴
mtext("y=1/x", side=4, line=3, cex.lab=1, las=2, col="blue") # 添加标题和文本
title("An Example of Creative Axes",
xlab="X values",
ylab="Y=X")
par(opar)
结果:
3.4.3 参考线
函数a**bline()**可以用来为图形添加参考线。
格式:
abline(h=yvalues, v=xvalues)
函数abline()中也可以指定其他图形参数(如线条类型、颜色和宽度)。举例来说:
abline(h=c(1,5,7))
在y为1、5、7的位置添加了水平实线,而代码:
abline(v=seq(1, 10, 2), lty=2, col="blue")
3.4.4 图例
当图形中包含的数据不止一组时,图例可以帮助你辨别出每个条形、扇形区域或折线各代表哪一类数据。我们可以使用函数legend()来添加图例(果然不出所料)。
格式;
legend(location, title, legend, ...)
其他常用的图例选项包括用于指定盒子样式的bty、指定背景色的bg、指定大小的cex,以及指定文本颜色的text.col。指定horiz=TRUE将会水平放置图例,而不是垂直放置。关于图例的更多细节,请参考help(legend)。这份帮助中给出的示例都特别有用。
代码:
drugA <- c(16, 20, 27, 40, 60)
drugB <- c(15, 18, 25, 31, 40)
opar <- par(no.readonly=TRUE)
par(lwd=2, cex=1.5, font.lab=2)
plot(dose, drugA, type="b",
pch=15, lty=1, col="red", ylim=c(0, 60),
main="Drug A vs. Drug B",
xlab="Drug Dosage", ylab="Drug Response")
lines(dose, drugB, type="b",
pch=17, lty=2, col="blue")
abline(h=c(30), lwd=1.5, lty=2, col="gray") #添加参考线
library(Hmisc)
minor.tick(nx=3, ny=3, tick.ratio=0.5) # 添加次要刻度
legend("topleft", inset=.05, title="Drug Type", c("A","B"),
lty=c(1, 2), pch=c(15, 17), col=c("red", "blue")) #添加图例
par(opar)
结果:
3.4.5 文本标注
我们可以通过函数text()和mtext()将文本添加到图形上。text()可向绘图区域内部添加文本,而mtext()则向图形的四个边界之一添加文本。
格式:
text(location, "text to place", pos, ...)
mtext("text to place", side, line=n, ...)
其他常用的选项有cex、col和font(分别用来调整字号、颜色和字体样式)。
除了用来添加文本标注以外,text()函数也通常用来标示图形中的点。我们只需指定一系列的x、y坐标作为位置参数,同时以向量的形式指定要放置的文本。x、y和文本标签向量的长度应当相同。
代码:
attach(mtcars)
plot(wt, mpg,
main="Mileage vs. Car Weight",
xlab="Weight", ylab="Mileage",
pch=18, col="blue")
text(wt, mpg,
row.names(mtcars),
cex=0.6, pos=4, col="red")
detach(mtcars)
结果:
这个例子中,我们针对数据框mtcars提供的32种车型的车重和每加仑汽油行驶英里数绘制了散点图。函数text()被用来在各个数据点右侧添加车辆型号。各点的标签大小被缩小了40%,颜色为红色。
代码:
opar <- par(no.readonly=TRUE)
par(cex=1.5)
plot(1:7,1:7,type="n")
text(3,3,"Example of default text")
text(4,4,family="mono","Example of mono-spaced text")
text(5,5,family="serif","Example of serif text")
par(opar)
结果:
3.4.6 数字标注
最后,你可以使用类似于 TeX 中的写法为图形添加数学符号和公式。请参阅
help(plotmath)以获得更多细节和示例。要即时看效果,可以尝试执行demo(plotmath)。部分运行结果如图3-13所示。函数plotmath()可以为图形主体或边界上的标题、坐标轴名称或文本标注添加数学符号。
3.5 图形的组合
在R中使用函数par()或layout()可以容易地组合多幅图形为一幅总括图形。此时请不要担心所要组合图形的具体类型,这里我们只关注组合它们的一般方法。后续各章将讨论每类图形的绘制和解读问题。
你可以在par()函数中使用图形参数mfrow=c(nrows, ncols)来创建按行填充的、行数为nrows、列数为ncols的图形矩阵。另外,可以使用mfcol=c(nrows, ncols)按列填充矩阵。
举例来说,以下代码创建了四幅图形并将其排布在两行两列中:
代码:
attach(mtcars)
opar <- par(no.readonly=TRUE)
par(mfrow=c(2,2))
plot(wt,mpg, main="Scatterplot of wt vs. mpg")
plot(wt,disp, main="Scatterplot of wt vs. disp")
hist(wt, main="Histogram of wt")
boxplot(wt, main="Boxplot of wt")
par(opar)
detach(mtcars)
结果:
代码:
attach(mtcars)
opar <- par(no.readonly=TRUE)
par(mfrow=c(3,1))
hist(wt)
hist(mpg)
hist(disp)
par(opar)
detach(mtcars)
结果:
函数layout()的调用形式为layout(mat),其中的mat是一个矩阵,它指定了所要组合的多个图形的所在位置。在以下代码中,一幅图被置于第1行,另两幅图则被置于第2行:
代码:
attach(mtcars)
layout(matrix(c(1,1,2,3), 2, 2, byrow = TRUE))
hist(wt)
hist(mpg)
hist(disp)
detach(mtcars)
结果:
代码:
attach(mtcars)
layout(matrix(c(1, 1, 2, 3), 2, 2, byrow = TRUE),
widths=c(3, 1), heights=c(1, 2))
hist(wt)
hist(mpg)
hist(disp)
detach(mtcars)
结果:
如你所见,layout()函数能够让我们轻松地控制最终图形中的子图数量和摆放方式,以及这些子图的相对大小。请参考help(layout)以了解更多细节。
图形布局的精细控制
可能有很多时候,你想通过排布或叠加若干图形来创建单幅的、有意义的图形,这需要有对图形布局的精细控制能力。你可以使用图形参数fig=完成这个任务。
代码:
opar <- par(no.readonly=TRUE)
par(fig=c(0, 0.8, 0, 0.8))
plot(mtcars$wt, mtcars$mpg,
xlab="Miles Per Gallon",
ylab="Car Weight")
par(fig=c(0, 0.8, 0.55, 1), new=TRUE)
boxplot(mtcars$wt, horizontal=TRUE, axes=FALSE)
par(fig=c(0.65, 1, 0, 0.8), new=TRUE)
boxplot(mtcars$mpg, axes=FALSE)
mtext("Enhanced Scatterplot", side=3, outer=TRUE, line=-3)
par(opar)
结果: