3.5 图形的组合

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

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

举例来说,以下代码创建了四幅图形并将其排布在两行两列中:

  1. attach(mtcars)
  2. opar <- par(no.readonly=TRUE)
  3. par(mfrow=c(2,2))
  4. plot(wt,mpg, main="Scatterplot of wt vs. mpg")
  5. plot(wt,disp, main="Scatterplot of wt vs disp")
  6. hist(wt, main="Histogram of wt")
  7. boxplot(wt, main="Boxplot of wt")
  8. par(opar)
  9. detach(mtcars)

结果如图3-14所示。

3.5 图形的组合 - 图1

图3-14 通过par(mfrow=c(2,2))组合的四幅图形

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

  1. attach(mtcars)
  2. opar <- par(no.readonly=TRUE)
  3. par(mfrow=c(3,1))
  4. hist(wt)
  5. hist(mpg)
  6. hist(disp)
  7. par(opar)
  8. detach(mtcars)

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

3.5 图形的组合 - 图2

图3-15 通过par(mfrow=c(3,1))组合的三幅图形

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

  1. attach(mtcars)
  2. layout(matrix(c(1,1,2,3), 2, 2, byrow = TRUE))
  3. hist(wt)
  4. hist(mpg)
  5. hist(disp)
  6. detach(mtcars)

结果如图3-16所示。

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

widths = 各列宽度值组成的一个向量

heights = 各行高度值组成的一个向量

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

3.5 图形的组合 - 图3

图3-16 使用函数layout()组合的三幅图形,各列宽度为默认值

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

  1. attach(mtcars)
  2. layout(matrix(c(1, 1, 2, 3), 2, 2, byrow = TRUE),
  3. widths=c(3, 1), heights=c(1, 2))
  4. hist(wt)
  5. hist(mpg)
  6. hist(disp)
  7. detach(mtcars)

所得图形如图3-17所示。

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

3.5 图形的组合 - 图4

图3-17 使用函数layout()组合的三幅图形,各列宽度为指定值

图形布局的精细控制

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

代码清单3-4 多幅图形布局的精细控制

  1. opar <- par(no.readonly=TRUE)
  2. par(fig=c(0, 0.8, 0, 0.8)) # 设置散点图
  3. plot(mtcars$wt, mtcars$mpg,
  4. xlab="Miles Per Gallon",
  5. ylab="Car Weight")
  6. par(fig=c(0, 0.8, 0.55, 1), new=TRUE) # 在上方添加箱线图
  7. boxplot(mtcars$wt, horizontal=TRUE, axes=FALSE)
  8. par(fig=c(0.65, 1, 0, 0.8), new=TRUE) # 在右侧添加箱线图
  9. boxplot(mtcars$mpg, axes=FALSE)
  10. mtext("Enhanced Scatterplot", side=3, outer=TRUE, line=-3)
  11. par(opar)

3.5 图形的组合 - 图5

图3-18 边界上添加了两幅箱线图的散点图

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

3.5 图形的组合 - 图6

图3-19 使用图形参数fig=指定位置

第一个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=将若干图形以任意排布方式组合到单幅图形中。稍加练习,你就可以通过这种方法极其灵活地创建复杂的视觉呈现。