R语言图形入门

Published on 2017 - 04 - 12

使用图形

R是一个惊艳的图形构建平台。这里我特意使用了“构建”一词。在通常的交互式会话中,你可以通过逐条输入语句构建图形,逐渐完善图形特征,直至得到想要的效果。

考虑以下五行代码:

attach(mtcars)
plot(wt, mpg)
abline(lm(mpg~wt))
title("Regression of MPG on Weight")
detach(mtcars)

首句绑定了数据框mtcars。第二条语句打开了一个图形窗口并生成了一幅散点图,横轴表示车身重量,纵轴为每加仑汽油行驶的英里数。第三句向图形添加了一条最优拟合曲线。第四句添加了标题。最后一句为数据框解除了绑定。在R中,图形通常都是以这种交互式的风格绘制的(参见图1)。

可以通过代码或图形用户界面来保存图形。要通过代码保存图形,将绘图语句夹在开启目标图形设备的语句和关闭目标图形设备的语句之间即可。例如,以下代码会将图形保存到当前工作目录中名为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()将图形保存为其他格式。(注意,Windows图元文件格式仅在Windows系统中可用。)

通过图形用户界面保存图形的方法因系统而异。对于Windows,在图形窗口中选择“文件”→ “另存为”,然后在弹出的对话框中选择想要的格式和保存位置即可。在Mac上,当Quartz图形窗口处于高亮状态时,点选菜单栏中的“文件”→“另存为”即可。其提供的输出格式仅有PDF。在UNIX系统中,图形必须使用代码来保存。在附录A中,我们将考虑每个系统中可用的备选图形用户界面,这将给予你更多选择。

通过执行如plot()、hist()(绘制直方图)或boxplot()这样的高级绘图命令来创建一幅新图形时,通常会覆盖掉先前的图形。如何才能创建多个图形并随时查看每一个呢?方法有若干。

第一种方法,你可以在创建一幅新图形之前打开一个新的图形窗口:

dev.new()
 statements to create graph 1
dev.new()
 statements to create a graph 2
etc.

每一幅新图形将出现在最近一次打开的窗口中。

第二种方法,你可以通过图形用户界面来查看多个图形。在Mac上,你可以使用Quartz菜单中的“后退”(Back)和“前进”(Forward)来逐个浏览图形。在Windows上,这个过程分为两步。在打开第一个图形窗口以后,勾选“历史”(History)→“记录”(Recording)。然后使用菜单中的“上一个”(Previous)和“下一个”(Next)来逐个查看已经绘制的图形。

第三种也是最后一种方法,你可以使用函数dev.new()、dev.next()、dev.prev()、dev.set()和dev.off()同时打开多个图形窗口,并选择将哪个输出发送到哪个窗口中。这种方法全平台适用。关于这种方法的更多细节,请参考help(dev.cur)。

R将在保证用户输入最小化的前提下创建尽可能美观的图形。不过你依然可以使用图形参数来指定字体、颜色、线条类型、坐标轴、参考线和标注。其灵活度足以让我们实现对图形的高度定制。

一个简单的例子

让我们从表1中给出的假想数据集开始。它描述了病人对两种药物五个剂量水平上的响应情况。

剂  量 对药物A的响应 对药物B的响应
20 16 15
30 20 18
40 27 25
45 40 31
60 60 40

可以使用以下代码输入数据:

dose  <- c(20, 30, 40, 45, 60)
drugA <- c(16, 20, 27, 40, 60)
drugB <- c(15, 18, 25, 31, 40)

使用以下代码可以创建一幅描述药物A的剂量和响应关系的图形:

plot(dose, drugA, type="b")

plot()是R中为对象作图的一个泛型函数(它的输出将根据所绘制对象类型的不同而变化)。本例中,plot(x, y, type="b")将x置于横轴,将y置于纵轴,绘制点集(x, y),然后使用线段将其连接。选项type="b"表示同时绘制点和线。使用help(plot)可以查看其他选项。结果如图2所示。

图形参数

我们可以通过修改称为图形参数的选项来自定义一幅图形的多个特征(字体、颜色、坐标轴、标题)。

(修改图形参数的)一种方法是通过函数par()来指定这些选项。以这种方式设定的参数值除非被再次修改,否则将在会话结束前一直有效。其调用格式为par(optionname=value, optionname=name,…)。不加参数地执行par()将生成一个含有当前图形参数设置的列表。添加参数no.readonly=TRUE可以生成一个可以修改的当前图形参数列表。

继续我们的例子,假设你想使用实心三角而不是空心圆圈作为点的符号,并且想用虚线代替实线连接这些点。你可以使用以下代码完成修改:

opar <- par(no.readonly=TRUE)
par(lty=2, pch=17)
plot(dose, drugA, type="b")
par(opar)

结果如图3所示。

首个语句复制了一份当前的图形参数设置。第二句将默认的线条类型修改为虚线(lty=2)并将默认的点符号改为了实心三角(pch=17)。然后我们绘制了图形并还原了原始设置。

你可以随心所欲地多次使用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)以确定哪些参数可以以这种方式设置。下面介绍可以设定的许多重要图形参数。

符号和线条

如你所见,可以使用图形参数来指定绘图时使用的符号和线条类型。相关参数如表2所示。

参  数 描  述
pch 指定绘制点时使用的符号(见图4)
cex 指定符号的大小。cex是一个数值,表示绘图符号相对于默认大小的缩放倍数。默认大小为1,1.5表示放大为默认值的1.5倍,0.5表示缩小为默认值的50%,等等
lty 指定线条类型(参见图5)
lwd 指定线条宽度。lwd是以默认值的相对大小来表示的(默认值为1)。例如,lwd=2将生成一条两倍于默认宽度的线条

选项pch=用于指定绘制点时使用的符号。可能的值如图4所示。

对于符号21~25,你还可以指定边界颜色(col=)和填充色(bg=)。

选项lty=用于指定想要的线条类型。可用的值如图5所示。

综合以上选项,以下代码:

plot(dose, drugA, type="b", lty=3, lwd=3, pch=15, cex=2)

将绘制一幅图形,其线条类型为点线,宽度为默认宽度的3倍,点的符号为实心正方形,大小为默认符号大小的2倍。结果如图6所示。

[图6 药物A剂量和响应的折线图。修改了线条类型、线条宽度、点的符号和符号大小]

接下来我们将讨论颜色的指定方法。

颜色

R中有若干和颜色相关的参数。表3列出了一些常用参数。

参  数 描  述
col 默认的绘图颜色。某些函数(如lines和pie)可以接受一个含有颜色值的向量并自动循环使用。例如,如果设定col=c("red", "blue")并需要绘制三条线,则第一条线将为红色,第二条线为蓝色,第三条线又将为红色
col.axis 坐标轴刻度文字的颜色
col.lab 坐标轴标签(名称)的颜色
col.main 标题颜色
col.sub 副标题颜色
fg 图形的前景色
bg 图形的背景色

在R中,可以通过颜色下标、颜色名称、十六进制的颜色值、RGB值或HSV值来指定颜色。举例来说,col=1、col="white"、col="#FFFFFF"、col=rgb(1,1,1)和col=hsv(0,0,1)都是表示白色的等价方式。函数rgb()可基于红—绿—蓝三色值生成颜色,而hsv()则基于色相—饱和度—亮度值来生成颜色。请参考这些函数的帮助以了解更多细节。

函数colors()可以返回所有可用颜色的名称。Earl F. Glynn为R中的色彩创建了一个优秀的在线图表,参见http://research.stowers-institute.org/efg/R/Color/Chart。R中也有多种用于创建连续型颜色向量的函数,包括rainbow()、heat.colors()、terrain.colors()、topo.colors()以及cm.colors()。举例来说,rainbow(10)可以生成10种连续的“彩虹型”颜色。多阶灰度色可使用gray()函数生成。这时要通过一个元素值为0和1之间的向量来指定各颜色的灰度。gray(0:10/10)将生成10阶灰度色。试着使用以下代码:

n <- 10
mycolors <- rainbow(n)
pie(rep(1, n), labels=mycolors, col=mycolors)
mygrays <- gray(0:n/n)
pie(rep(1, n), labels=mygrays, col=mygrays)

文本属性

图形参数同样可以用来指定字号、字体和字样。表4阐释了用于控制文本大小的参数。

参  数 描  述
cex 表示相对于默认大小缩放倍数的数值。默认大小为1,1.5表示放大为默认值的1.5倍,0.5表示缩小为默认值的50%,等等
cex.axis 坐标轴刻度文字的缩放倍数。类似于cex
cex.lab 坐标轴标签(名称)的缩放倍数。类似于cex
cex.main 标题的缩放倍数。类似于cex
cex.sub 副标题的缩放倍数。类似于cex

字体族和字样可以通过字体选项进行控制(见表5)。

参  数 描  述
font 整数。用于指定绘图使用的字体样式。1=常规,2=粗体,3=斜体,4=粗斜体,5=符号字体(以Adobe符号编码表示)
font.axis 坐标轴刻度文字的字体样式
font.lab 坐标轴标签(名称)的字体样式
font.main 标题的字体样式
font.sub 副标题的字体样式
ps 字体磅值(1磅约为1/72英寸)。文本的最终大小为 ps*cex
family 绘制文本时使用的字体族。标准的取值为serif(衬线)、sans(无衬线)和mono(等宽)

举例来说,在执行语句:

par(font.lab=3, cex.lab=1.5, font.main=4, cex.main=2)

之后创建的所有图形都将拥有斜体、1.5倍于默认文本大小的坐标轴标签(名称),以及粗斜体、2倍于默认文本大小的标题。

我们可以轻松设置字号和字体样式,然而字体族的设置却稍显复杂。这是因为衬线、无衬线和等宽字体的具体映射是与图形设备相关的。举例来说,在Windows系统中,等宽字体映射为TT Courier New,衬线字体映射为TT Times New Roman,无衬线字体则映射为TT Arial(TT代表True Type)。如果你对以上映射表示满意,就可以使用类似于family="serif"这样的参数获得想要的结果。如果不满意,则需要创建新的映射。在Windows中,可以通过函数windowsFont()来创建这类映射。例如,在执行语句:

windowsFonts(
  A=windowsFont("Arial Black"),
  B=windowsFont("Bookman Old Style"),
  C=windowsFont("Comic Sans MS")
)

之后,即可使用A、B和C作为family的取值。在本例的情境下,par(family="A")将指定Arial Black作为绘图字体。请注意,函数windowsFont()仅在Windows中有效。在Mac上,请改用quartzFonts()。

如果以PDF或PostScript格式输出图形,则修改字体族会相对简单一些。对于PDF格式,可以使用names(pdfFonts())找出你的系统中有哪些字体是可用的,然后使用pdf(file="myplot.pdf", family="fontname")来生成图形。对于以PostScript格式输出的图形,则可以对应地使用names(postscriptFonts())和postscript(file="myplot.ps", family= "fontname")。请参阅在线帮助以了解更多信息。

图形尺寸与边界尺寸

最后,可以使用表6列出的参数来控制图形尺寸和边界大小。

参  数 描  述
pin 以英寸表示的图形尺寸(宽和高)
mai 以数值向量表示的边界大小,顺序为“下、左、上、右”,单位为英寸
mar 以数值向量表示的边界大小,顺序为“下、左、上、右”,单位为英分。默认值为c(5, 4, 4, 2) + 0.1

代码:

par(pin=c(4,3), mai=c(1,.5, 1, .2))

可生成一幅4英寸宽、3英寸高、上下边界为1英寸、左边界为0.5英寸、右边界为0.2英寸的图形。关于边界参数的完整指南,不妨参阅Earl F. Glynn编写的一份全面的在线教程

让我们使用最近学到的选项来强化之前的简单图形示例。代码清单1中的代码生成的图形如图7所示。

dose  <- c(20, 30, 40, 45, 60)
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()设定的参数对两幅图都有效,而在绘图函数中指定的参数仅对那个特定图形有效。观察图7可以发现,图形的呈现上还有一定缺陷。这两幅图都缺少标题,并且纵轴的刻度单位不同,这无疑限制了我们直接比较两种药物的能力。同时,坐标轴的标签(名称)也应当提供更多的信息。

添加文本、自定义坐标轴和图例

除了图形参数,许多高级绘图函数(例如plot、hist、boxplot)也允许自行设定坐标轴和文本标注选项。举例来说,以下代码在图形上添加了标题(main)、副标题(sub)、坐标轴标签(xlab、ylab)并指定了坐标轴范围(xlim、ylim)。结果如图8所示。

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

再次提醒,并非所有函数都支持这些选项。请参考相应函数的帮助以了解其可以接受哪些选项。从更精细的控制和模块化的角度考虑,你可以使用本节余下部分描述的函数来控制标题、坐标轴、图例和文本标注的外观。

注意 某些高级绘图函数已经包含了默认的标题和标签。你可以通过在plot()语句或单独的par()语句中添加ann=FALSE来移除它们。

标题

可以使用title()函数为图形添加标题和坐标轴标签。调用格式为:

title(main="main title", sub="sub-title",
      xlab="x-axis label", ylab="y-axis label")

函数title()中亦可指定其他图形参数(如文本大小、字体、旋转角度和颜色)。举例来说,以下代码将生成红色的标题和蓝色的副标题,以及较默认大小小25%的绿色x轴、y轴标签:

title(main="My Title", col.main="red",
      sub="My Sub-title", col.sub="blue",
      xlab="My X label", ylab="My Y label",
      col.lab="green", cex.lab=0.75)

坐标轴

你可以使用函数axis()来创建自定义的坐标轴,而非使用R中的默认坐标轴。其格式为:

axis(side, at=, labels=, pos=, lty=, col=, las=, tck=, ...)

各参数已详述于表7中。

选  项 描  述
side 一个整数,表示在图形的哪边绘制坐标轴(1=下,2=左,3=上,4=右)
at 一个数值型向量,表示需要绘制刻度线的位置
labels 一个字符型向量,表示置于刻度线旁边的文字标签(如果为NULL,则将直接使用at中的值)
pos 坐标轴线绘制位置的坐标(即与另一条坐标轴相交位置的值)
lty 线条类型
col 线条和刻度线颜色
las 标签是否平行于(=0)或垂直于(=2)坐标轴
tck 刻度线的长度,以相对于绘图区域大小的分数表示(负值表示在图形外侧,正值表示在图形内侧,0表示禁用刻度,1表示绘制网格线);默认值为-0.01
(…) 其他图形参数

创建自定义坐标轴时,你应当禁用高级绘图函数自动生成的坐标轴。参数axes=FALSE将禁用全部坐标轴(包括坐标轴框架线,除非你添加了参数frame.plot=TRUE)。参数xaxt="n"和yaxt="n"将分别禁用X轴或Y轴(会留下框架线,只是去除了刻度)。代码清单2中是一个稍显笨拙和夸张的例子,它演示了我们到目前为止讨论过的各种图形特征。结果如图9所示。

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",       # 绘制x对y的图形
     pch=21, col="red",
     yaxt="n", lty=3, ann=FALSE)

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)

次要刻度线

注意,我们最近创建的图形都只拥有主刻度线,却没有次要刻度线。要创建次要刻度线,你需要使用Hmisc包中的minor.tick()函数。如果你尚未安装Hmisc包,请先安装它。你可以使用代码:

library(Hmisc)
minor.tick(nx=n, ny=n, tick.ratio=n)

来添加次要刻度线。其中nx和ny分别指定了X轴和Y轴每两条主刻度线之间通过次要刻度线划分得到的区间个数。tick.ratio表示次要刻度线相对于主刻度线的大小比例。当前的主刻度线长度可以使用par("tck")获取。举例来说,下列语句将在X轴的每两条主刻度线之间添加1条次要刻度线,并在Y轴的每两条主刻度线之间添加2条次要刻度线:

minor.tick(nx=2, ny=3, tick.ratio=0.5)

次要刻度线的长度将是主刻度线的一半。

参考线

函数abline()可以用来为图形添加参考线。其使用格式为:

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

则在x为1、3、5、7、9的位置添加了垂直的蓝色虚线。

图例

当图形中包含的数据不止一组时,图例可以帮助你辨别出每个条形、扇形区域或折线各代表哪一类数据。我们可以使用函数legend()来添加图例(果然不出所料)。其使用格式为:

legend(location, title, legend, ...)

常用选项详述于表8中。

选  项 描  述
location 有许多方式可以指定图例的位置。你可以直接给定图例左上角的x、y坐标,也可以执行locator(1),然后通过鼠标单击给出图例的位置,还可以使用关键字bottom、bottomleft、left、topleft、top、topright、right、bottomright或center放置图例。如果你使用了以上某个关键字,那么可以同时使用参数inset=指定图例向图形内侧移动的大小(以绘图区域大小的分数表示)
title 图例标题的字符串(可选)
legend 图例标签组成的字符型向量
其他选项。如果图例标示的是颜色不同的线条,需要指定col=加上颜色值组成的向量。如果图例标示的是符号不同的点,则需指定pch=加上符号的代码组成的向量。如果图例标示的是不同的线条宽度或线条类型,请使用lwd=或lty=加上宽度值或类型值组成的向量。要为图例创建颜色填充的盒形(常见于条形图、箱线图或饼图),需要使用参数fill=加上颜色值组成的向量

其他常用的图例选项包括用于指定盒子样式的bty、指定背景色的bg、指定大小的cex,以及指定文本颜色的text.col。指定horiz=TRUE将会水平放置图例,而不是垂直放置。关于图例的更多细节,请参考help(legend)。这份帮助中给出的示例都特别有用。

让我们看看对药物数据作图的一个例子(代码清单3)。你将再次使用我们目前为止讲到的许多图形功能。结果如图10所示。

dose  <- c(20, 30, 40, 45, 60)
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)

文本标注

我们可以通过函数text()和mtext()将文本添加到图形上。text()可向绘图区域内部添加文本,而mtext()则向图形的四个边界之一添加文本。使用格式分别为:

text(location, "text to place", pos, ...)
mtext("text to place", side, line=n, ...)

常用选项列于表9中。

选  项 描  述
location 文本的位置参数。可为一对x,y坐标,也可通过指定location为locator(1)使用鼠标交互式地确定摆放位置
pos 文本相对于位置参数的方位。1=下,2=左,3=上,4=右。如果指定了pos,就可以同时指定参数offset=作为偏移量,以相对于单个字符宽度的比例表示
side 指定用来放置文本的边。1=下,2=左,3=上,4=右。你可以指定参数line=来内移或外移文本,随着值的增加,文本将外移。也可使用adj=0将文本向左下对齐,或使用adj=1右上对齐

其他常用的选项有cex、col和font(分别用来调整字号、颜色和字体样式)。

除了用来添加文本标注以外,text()函数也通常用来标示图形中的点。我们只需指定一系列的x, y坐标作为位置参数,同时以向量的形式指定要放置的文本。x、y和文本标签向量的长度应当相同。下面给出了一个示例,结果如图11所示。

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)

在Windows系统中输出的结果如图12所示。这里为了获得更好的显示效果,我们使用par()函数增大了字号。

本例所得结果因平台而异,因为不同系统中映射的常规字体、等宽字体和有衬线字体有所不同。在你的系统上,结果看起来如何呢?

数学标注

最后,你可以使用类似于TeX中的写法为图形添加数学符号和公式。请参阅help(plotmath)以获得更多细节和示例。要即时看效果,可以尝试执行demo(plotmath)。部分运行结果如图13所示。函数plotmath()可以为图形主体或边界上的标题、坐标轴名称或文本标注添加数学符号。

图形的组合

在R中使用函数par()或layout()可以容易地组合多幅图形为一幅总括图形。此时请不要担心所要组合图形的具体类型,这里我们只关注组合它们的一般方法。后续各章将讨论每类图形的绘制和解读问题。

你可以在par()函数中使用图形参数mfrow=c(nrows, ncols)来创建按行填充的、行数为nrows、列数为ncols的图形矩阵。另外,可以使用nfcol=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)

结果如图14所示。

作为第二个示例,让我们依3行1列排布3幅图形。代码如下:

attach(mtcars)
opar <- par(no.readonly=TRUE)
par(mfrow=c(3,1))
hist(wt)
hist(mpg)
hist(disp)
par(opar)
detach(mtcars)

所得图形如图15所示。请注意,高级绘图函数hist()包含了一个默认的标题(使用main=""可以禁用它,抑或使用ann=FALSE来禁用所有标题和标签)。

函数layout()的调用形式为layout(mat),其中的mat是一个矩阵,它指定了所要组合的多个图形的所在位置。在以下代码中,一幅图被置于第1行,另两幅图则被置于第2行:

attach(mtcars)
layout(matrix(c(1,1,2,3), 2, 2, byrow = TRUE)) # c(1,1,2,3)指定图形的位置;2,2指定图形的布局2*2
hist(wt)
hist(mpg)
hist(disp)
detach(mtcars)

结果如图16所示。

为了更精确地控制每幅图形的大小,可以有选择地在layout()函数中使用widths=和heights=两个参数。其形式为:

  • widths = 各列宽度值组成的一个向量
  • heights = 各行高度值组成的一个向量

相对宽度可以直接通过数值指定,绝对宽度(以厘米为单位)可以通过函数lcm()来指定。

在以下代码中,我们再次将一幅图形置于第1行,两幅图形置于第2行。但第1行中图形的高度是第2行中图形高度的三分之一。除此之外,右下角图形的宽度是左下角图形宽度的四分之一:

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)

所得图形如图17所示。

如你所见,layout()函数能够让我们轻松地控制最终图形中的子图数量和摆放方式,以及这些子图的相对大小。请参考help(layout)以了解更多细节。

图形布局的精细控制

可能有很多时候,你想通过排布或叠加若干图形来创建单幅的、有意义的图形,这需要有对图形布局的精细控制能力。你可以使用图形参数fig=完成这个任务。代码清单4通过在散点图上添加两幅箱线图,创建了单幅的增强型图形。结果如图18所示。

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)

要理解这幅图的绘制原理,请试想完整的绘图区域:左下角坐标为(0,0),而右上角坐标为(1,1)。图19是一幅示意图。参数fig=的取值是一个形如c(x1, x2, y1, y2)的数值向量。

第一个fig=将散点图设定为占据横向范围0~0.8,纵向范围0~0.8。上方的箱线图横向占据0~0.8,纵向0.55~1。右侧的箱线图横向占据0.65~1,纵向0~0.8。fig=默认会新建一幅图形,所以在添加一幅图到一幅现有图形上时,请设定参数new=TRUE。

我将参数选择为0.55而不是0.8,这样上方的图形就不会和散点图拉得太远。类似地,我选择了参数0.65以拉近右侧箱线图和散点图的距离。你需要不断尝试找到合适的位置参数。

注意 各独立子图所需空间的大小可能与设备相关。如果你遇到了“Error in plot.new(): figure margins too large”这样的错误,请尝试在整个图形的范围内修改各个子图占据的区域位置和大小。

你可以使用图形参数fig=将若干图形以任意排布方式组合到单幅图形中。稍加练习,你就可以通过这种方法极其灵活地创建复杂的视觉呈现。

参考文档