首先介绍 R 语言绘图的基本方法,随后讲解如何定制图表的各个元素(标题、轴、标签、刻度线、辅助线、图例等),最后关注如何定制一组图和混合图。

本章包含了几乎所有 R 绘图所需的一般知识,但不包含对 ggplot2 (一个专业于绘图的 R 包)绘图的介绍。

使用的 R 版本是 3.5.2

1 案例:评估药效

研究病人对两种药物 A and B 的不同剂量的反应,首先生成数据:

# 药物剂量
dose <- c(20, 30, 40, 50, 60)
drugA <- c(16, 20, 27, 40, 60)
drugB <- c(15, 18, 25, 31, 40)
dose_table <- data.frame(dose, drugA, drugB)
dose drugA drugB
20 16 15
30 20 18
40 27 25
50 40 31
60 60 40

2 基本方法

2.1 生成图表

绘制散点图并连线:

  • 使用 plot() 方法
  • 第一个参数是 x,第二个参数是 y。参数 type="b" 意思是同时绘制点和线。
plot(dose, drugA, type="b")

Use command help(plot) to learn more about plot()

2.2 保存图表

可以将图表保存为 PNG, JPEG, PDF, PSF 等格式。

png(filename = "my_plot.png")
jpeg(filename = "my_plot.jpg")
pdf(file="my_plot.pdf")
postscript(file = "my_plot.ps")

3 配置图表

3.1 方法一: 使用 par() 函数

使用 par() 查看和配置图表参数。

  • 用参数 no.readonly = TRUE 查看可编辑的图标参数
  • 用变量 opar: Original params 来备份初始配置
opar <- par(no.readonly = TRUE)
opar[1:2]
## $xlog
## [1] FALSE
## 
## $ylog
## [1] FALSE
# lty: change line type to dashed (=2), pch: change symbol to solid triangle (=17)
par(lty=2, pch=17)
plot(dose, drugA, type="b")

# 恢复初始配置
par(opar)

3.2 方法二: 直接传参给绘图函数

plot(dose, drugA, type = "b", lty=2, pch=17)

3.3 配置点和线

Parameter Description
pch 点的样式
cex 点的大小倍数
lty 线的样式
lwd 线的宽度倍数

3.4 配置颜色

3.4.1 与颜色有关的参数

  • col 用于线图或饼图,可以传入向量,R 将循环使用其中的颜色
  • col.axis 轴文本
  • col.lab 轴标签
  • col.main 主标题
  • col.sub 副标题
  • fg 前景色
  • bg 背景色

支持各种颜色格式:col=1, col="white", col="#FFFFFF", col=rgb(1,1,1) or col=hsv(0,0,1) is OK.

3.4.2 运用 R 语言生成颜色

  • colors() 返回所有可用颜色名
  • rainbow(), heat.colors(), terrain.colors, topo.colors() and cm.colors() 可以生成系列色
  • RColorBrewer 包可以生成颜色

3.4.2.1 例 1: raindow() 函数

n <- 10
mycolors <- rainbow(10)
pie(rep(1, n), labels = mycolors, col = mycolors)

3.4.2.2 例 2: gray() 函数

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

3.4.2.3 例 3: RColorBrewer 包

library(RColorBrewer)
display.brewer.all()

n <- 7
mycolors <- brewer.pal(n, "RdYlGn")
barplot(rep(1,n), col=mycolors)

3.5 配置图中的文字

图表中的文字样式也可以进行类似配置。

3.5.1 字号

par() 中传入以下参数和值,或直接传入绘图方法中。

  • cex 基础文字大小倍数
  • cex.axis 轴文字字号倍数,相对 cex,下同
  • cex.lab 轴标签字号倍数
  • cex.main 主标题字号倍数
  • cex.sub 副标题字号倍数

3.5.2 粗细

  • font 基础文字粗细,=1 正常,=2 加粗,=3 意大利斜体,=4 粗斜体,=5点阵字体 (Adobe)
  • font.axis 轴文字
  • font.lab 轴标签
  • font.main 主标题
  • font.sub 副标题
  • ps Font point size

3.5.3 字体

  • family 字体

Windows 中字体名和具体名称之间有一个 map 的对应关系。

使用已知对应字体,可以直接将 family 值设置为字体名:

par(family="serif")

使用新字体,需要用 windowsFont() 方法构建 map 关系:

windowsFonts(
  A=windowsFont("Arial Black"),
  B=windowsFont("Bookman Old Style"),
  C=windowsFont("Comic Sans MS")
)
# 将图表字体设置为 Arial Black
par(family = "A")

可以使用 names() 方法查看可用字体。

# PDF 字体
names(pdfFonts())
# 生成 pdf
pdf(file="myplot.pdf", family = "fontname")
# PostScript Format 字体
names(postscriptFonts())
# 生成 PSF
postscript(file = "myplot.ps", family = "fontname")

3.6 配置长、高与边距

  • pin 传入向量,分别为宽和高
  • mai margin,图表的外边距,单位是 inches,顺序是 c(bottom, left, top, right)
  • mar margin,图表的外边距,单位是行,顺序同上
# 4 inch 宽,3 inch 高,上下边距为 1 inch,0.2 inch 右边距,0.5 inch 左边距
par(pin = c(4,3), mai = c(1, .5, 1, .2))

3.7 综合例子 1:分别展示两种药的药效

配置:

  1. 宽 =2 和高 =3
  2. 线粗2倍,点变大1.5倍
  3. 轴文字 0.75 倍,斜体
  4. 点线样式在绘图函数中直接配置
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)

# 1
par(pin=c(2,3))
# 2
par(lwd=2, cex=1.5)
# 3
par(cex.axis=.75, font.axis=3)
# 4-1
plot(dose, drugA, type="b", pch=19, lty=2, col="red")

# 4-2
plot(dose, drugB, type="b", pch=23, lty=6, col="blue", bg="green")

par(opar)

3.8 配置图表(进阶)

plot() 中传入更多参数,可以更好地定制图表。

绘制药物 A 的剂量与病人反应关系的图像:

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

  • 参数 frame.plot=FALSE:不绘制全部坐标轴,= TRUE 则绘制所有轴,形成箱状如上图。
  • 参数 ann=FALSE :使用默认标题和轴标签样式。
  • 参数 axes=FALSE :绘制默认轴。
  • yaxt="n"xaxt="n" 表示不显示轴刻度,但是显示轴。

并不是所有绘图函数都支持这些参数,需要参考 help()

3.8.1 配置标题

使用 title() 定制标题,基本语法是:

plot.new()
title(main="main title", sub="subtitle",
      xlab="x-axis label", ylab="y-axis label")

它也可被传入图表的配置参数.

plot.new()
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)

3.8.2 配置坐标轴

使用 axis() 方法定制坐标轴,基本语法是:

axis(side, at=, labels=, pos=, lty=, col=, las=, tck=, ...)
  • side 指定配置的目标轴(根据轴在图表的哪一侧),1 底部,2 左侧,3 顶部,4 右侧
  • at 在哪里画刻度线,传入数字型向量
  • labels 刻度线处的标记文字,传入字符型向量。如果是 NULL,将使用刻度线处的数值(也即 at= 的值)。
  • pos 与另一条轴的哪个值处相交
  • lty 线样式
  • col 线和刻度线颜色
  • las label和轴平行(=0)或和轴垂直(=2)
  • tck 刻度线的长度比例,为负在轴外,为正在轴内,=0 隐藏,=1 即为网格线。

繁琐但全面 cover 的示例:

  • 使用了 lines() 方法绘制线图。
  • 使用了 mtext() 方法添加文字。
# Specifies data
x <- c(1:10)
y <- x
z <- 10/x
opar <- par(no.readonly=TRUE)
# Increases margins
par(mar=c(5, 4, 4, 8) + 0.1)
# Plots x vs. y, suppressing annotations
plot(x, y, type="b",
     pch=21, col="red",
     yaxt="n", lty=3, ann=FALSE) 
# Adds an x versus 1/x line
lines(x, z, type="b", pch=22, col="blue", lty=2)
# Draws the axes
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)
# Adds titles and text
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() 方法,例子见下一小节的示例图。

3.8.3 作辅助线

使用 abline(h=yvalues, v=xvalues) 添加辅助线.

例如如下代码:

  1. 在 y=1,5,7 处绘制辅助线
  2. 在 x=1,3,5,7,9 处绘制辅助线
# 1
abline(h=c(1,5,7))
# 2
abline(v=seq(1, 10, 2), lty=2, col="blue")

3.8.4 配置图例

使用 legend() 方法配置图例和其样式。

legend(location, title, legend, ...)
  • location 图例位置,语法可以是以下任意一种
    • x,y 上左侧为原点的距离坐标系
    • locator(1) 鼠标位置
    • bottom, bottomleft, left, topleft, top, topright, right, bottomright, center 在图内的某个方位
      • inset= 参数在方位基础上位移一段距离
  • title 图例标题,字符串 (optional)
  • legend 图例标签,字符串向量
  • ... 其他图表参数,比如col=颜色,pch=点样式,lwd=,lty=线样式,fill=图例盒子的颜色,都传入向量
  • horiz=TRUE 将图例设置成水平排列而不是垂直排列
  • bty 图例盒子类型,bg背景色,cex字号,text.color文字颜色

See help(legend) for more details.

3.8.5 综合例子 2:比较药效

对比药物 A 和药物 B 的药效。

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)
# Increases line, text, symbol, and label font size
par(lwd=2, cex=1.5, font.lab=2)
# Generates the graph
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")
# Adds minor tick marks
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.8.6 添加文字注解

使用 text()mtext() 为图表添加文字。

  • text()图中添加文字,尤其用于标识某个特殊点
    • 语法是 text(location, "text to place", pos, ...)
  • mtext()外边距的空白添加文字
    • 语法是 mtext("text to place", side, line=n, ...)

二者共同的参数:

  • location 位置。值为 x,y 表示坐标位置,或 locator(1) 表示鼠标位置
  • pos 相对于 location 参数值的位置. 1 = below, 2 = left, 3 = above, 4 = right.
  • side 放置在外边距的哪一侧,1 = bottom, 2 = left, 3 = top, 4 = right。
    • line 参数还可以配置文字与坐标轴的距离
    • adj=0 左下角对齐,adj=1右上角对齐
  • 其他参数,比如字号cex,颜色col和粗细font

例子:

attach(datasets::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(datasets::mtcars)
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.8.7 添加数学表达式注解

可以往图表中添加数学表达式,规则见 help(plotmath).

使用 plotmath() 方法可以向图表的组件或在图中添加数学公式。

4 生成组图

4.1 混合多张图表

使用 par()layout() 方法可以混合图表。

  • par() 里的参数
    • mfrow=c(nrows, ncols) 按行排列,生成 nrow 行 nrow 列的图表阵列
    • mfcol=c(nrows, ncols) 按列排列

4.1.1 使用 par() 函数

例子:

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)

注意上述代码中 hist() 方法,即使不传入 main 参数,图的默认标题也会是 “Histagram of xxx”.

4.1.2 使用 layout() 函数

layout 接收一个矩阵 (matrix),根据矩阵规模和矩阵内的数字来排列图表。

可选参数: * widths 列宽,传入向量 * heights 行高,传入向量 * 相对长度使用数字型向量,绝对长度使用 lcm() 值向量

例子 1:不设置列宽和行高的基本图组

attach(mtcars)
layout(
  matrix(c(1,1,2,3), 2, 2, byrow=TRUE)
  )
hist(wt)
hist(mpg)
hist(disp)

detach(mtcars)

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

See help(layout) for more details.

4.2 定制一张高级混合图

使用图表配置参数 fig 将多个图合并到同一张图里,定制更高级的图表。

fig 的参数:

* 规定一个坐标系 `(x,y)`,左下角 `(0,0)`,右上角 `(1,1)`
* 第一个参数是向量 `c(x1, x2, y1, y2)`,意为图形的横坐标从 x1 到 x2,纵坐标从 y1 到 y2
* 参数 `new=TRUE` ,表示在旧图上应用一个新图。

例子:在点阵图的上方和右侧增加箱线图

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")
# Adds a box plot above
par(fig=c(0, 0.8, 0.55, 1), new=TRUE)
boxplot(mtcars$wt, horizontal=TRUE, axes=FALSE)
# Adds a box plot to the right
par(fig=c(0.6, 1, 0, 0.8), new=TRUE)
boxplot(mtcars$mpg, axes=FALSE)
mtext("Enhanced Scatterplot", side=3, outer=TRUE, line=-3)

par(opar)

为什么箱线图和点阵图fig参数里向量的横纵坐标参数加起来超过了 1 ?

因为向量参数 c(x1,x2,y1,y2) 是指示一个绘图范围,所以允许重合。

报错 Error in plot.new(): figure margins too large 的解决办法也是调整向量参数。

示例代码的向量参数可以让箱线图更靠近点阵图,需要经过不断尝试,找到最好的配置。