使用图层的三个主要目的:
- 展示数据:绘制原始数据时唯一的一层(数据层)
- 展示数据的统计摘要:在数据背景下展示模型的统计预测效果,模型层通常绘制在数据层之上
- 添加额外的元数据(metadata)、上下文信息和注释:也称背景层,了解数据的背景信息或强调数据中的某些特征,一般在最后绘制。
1. 基本图形类型
几何对象是ggplot2的基本组成部分,可以独立构建图形。他们都是二维的,主要函数有 geom_area() , geom_bar() , geom_line , geom_point() , geom_polygon() , geom_tile() 等等。这些函数包括x,y两个主要属性,另外也可以接受 color 和 size 两个图形属性,他们构成了基本的数据层。
我们可以通过使用 + 来 添加图层 。
df <- data.frame(
x = c(3, 1, 5),
y = c(2, 4, 6),
label = c("a","b","c")
)
df
p <- ggplot(df, aes(x, y, label = label)) +
labs(x = NULL, y = NULL) + # Hide axis label
theme(plot.title = element_text(size = 12)) # Shrink plot title
p + geom_point() + ggtitle("point")
p + geom_text() + ggtitle("text")
p + geom_bar(stat = "identity") + ggtitle("bar")
p + geom_tile() + ggtitle("raster")
p + geom_line() + ggtitle("line")
p + geom_area() + ggtitle("area")
p + geom_path() + ggtitle("path")
p + geom_polygon() + ggtitle("polygon")
以上命令运行后可依次生成散点图、含标签的散点图、条形图、色深图、线条图、面积图、路径图和多边形图等八个图,其中 ggtitle() 是给各个图片添加注释/命名

2. 添加标签(label)
主要函数 geom_text() :和散点类似,就是将point换成了文字。它包括以下几种参数:
- size :设置字体大小
- angle :设置倾斜角度
- family :可设置字体,下面代码中的 "sans" , "serif" , "mono" 代表三种字体, "sans" 是默认字体。
df <- data.frame(x = 1, y = 3:1, family = c("sans", "serif", "mono"))
ggplot(df, aes(x, y)) +
geom_text(aes(label = family, family = family))

此外还有两个字体安装包
– showtext, https://github.com/yixuan/showtext, by Yixuan Qiu, makes GD-independent plots by rendering all text as polygons.
– extrafont, https://github.com/wch/extrafont, by Winston Chang, converts fonts to a standard format that all devices can use.
- fontface :可设置粗体或斜体, “plain” 默认普通值, “bold” 粗体、 “italic” 斜体。
df <- data.frame(x = 1, y = 3:1, face = c("plain", "bold", "italic"))
ggplot(df, aes(x, y)) +
geom_text(aes(label = face, fontface = face))

- vjust 和 hjust 可以设置字体对齐方式。 vjust (“bottom”, “middle”, “top”, “inward”, “outward”); hjust (“left”, “center”, “right”, “inward”, “outward”)。最常用的路线之一是 “inward” :它将文本对齐到主画面的中间:
df <- data.frame(
x = c(1, 1, 2, 2, 1.5),
y = c(1, 2, 1, 2, 1.5),
text = c(
"bottom-left", "bottom-right",
"top-left", "top-right", "center"
)
)
ggplot(df, aes(x, y)) +
geom_text(aes(label = text)) # 字母不能全部在画面中
ggplot(df, aes(x, y)) +
geom_text(aes(label = text), vjust = "inward", hjust = "inward") # 字母全部在画面中

- nudge() 参数可以设置文字距原坐标点的距离,在散点和文字同时存在时很有必要,这是文字是一个注释的作用,如果不添加该参数,点和文字就会重合。
df <- data.frame(trt = c("a", "b", "c"), resp = c(1.2, 3.4, 2.5))
ggplot(df, aes(resp, trt)) +
geom_point() +
geom_text(aes(label = paste0("(", resp, ")")), nudge_y = -0.25) + # y轴负方向下移0.25
xlim(1, 3.6) # 设定x轴取值区间

- check_overlap :查找重复值。当注释中有大量重复时,设置 check_overlap=TRUE 可以自动删除重复标签。
以耗油量数据为例:
library(ggplot2)
mpg
ggplot(mpg, aes(displ, hwy)) +
geom_text(aes(label = model)) +
xlim(1, 8)
ggplot(mpg, aes(displ, hwy)) +
geom_text(aes(label = model), check_overlap = TRUE) +
xlim(1, 8)

另外,与 geom_text() 类似的是 geom_label() ,它与geom_text的区别自动在文字后方绘制一个圆角矩形标签,当需要在复杂的背景上标注文字时可以使用。
label <- data.frame(
waiting = c(55, 80),
eruptions = c(2, 4.3),
label = c("peak one", "peak two")
)
ggplot(faithfuld, aes(waiting, eruptions)) +
geom_tile(aes(fill = density)) +
geom_label(data = label, aes(label = label))

学会了在图中添加标签,可以替代图例
3. 注释(Annotations)
注释可以在你的图上添加一些额外的元数据,可以使用以下函数:
- geom_text() :在指定点添加标签(见上文)
- geom_rect() :可强调图形中感兴趣的矩形区域。包括 xmin , xmax , ymin , ymax
- geom_line() ; geom_path() ; geom_segment() :在图形中添加线条; arrow() 可以用来添加箭头
- geom_vline() ; geom_hline() :向图形添加垂直线或水平线
- geom_abline() :向图形添加任意斜率和截距地直线
然后我们用“失业率数据集”来举例:
ggplot(economics, aes(date, unemploy)) +
geom_line()

下面我们想在图中展现一下不同政*党**执政时期失业率的高低情况:
presidential <- subset(presidential, start > economics$date[1]) ## 设置数据集,economics$date[1]是指定了数据集中data一列的第一行数据
ggplot(economics) +
geom_rect(
aes(xmin = start, xmax = end, fill = party),
ymin = -Inf, ymax = Inf, alpha = 0.2,
data = presidential
) +
geom_vline(
aes(xintercept = as.numeric(start)),
data = presidential,
colour = "grey50", alpha = 0.5
) +
geom_text(
aes(x = start, y = 2500, label = name),data = presidential,
size = 3, vjust = 0, hjust = 0, nudge_x = 50
) +
geom_line(aes(date, unemploy)) + ## 数据集中的两列数据来源
scale_fill_manual(values = c("blue", "red")) ## scale是标度函数
(疑问: party 这个对象的数据不知道从哪里来的)
生成图片:

如果我们想使用同样的方法在图片中添加一个单独的注释,方法有点复杂,因为首先要创建一个数据框:
library(ggplot2)
economics
yrng <- range(economics$unemploy)
xrng <- range(economics$date) ## range 是取提取范围的子集,最大值和最小值
caption <- paste(strwrap("Unemployment rates in the US have
+ varied a lot over the years", 40), collapse = "\n") ## 设置题目内容
ggplot(economics, aes(date, unemploy)) +
geom_line() +
geom_text(
aes(x, y, label = caption),
data = data.frame(x = xrng[1], y = yrng[2], caption = caption),
hjust = 0, vjust = 1, size = 4
)
上述代码可以用 annotate() 来简化 geom_text() 部分:
ggplot(economics, aes(date, unemploy)) +
geom_line() +
annotate("text", x = xrng[1], y = yrng[2], label = caption,
hjust = 0, vjust = 1, size = 4)
生成图片:

注释,在分面状态下的组间比较的时也非常有用。下面要做一组关于钻石质量(carat)和价格(price)比较的2d热图,按照切割品质(cut)进行分面:
ggplot(diamonds, aes(log10(carat), log10(price))) +
geom_bin2d() +
facet_wrap(~cut, nrow = 1)

4. 群组几何对象(Collective Geoms)
在ggplot2中,几何对象大致可以分为个体(individual)几何对象和群组(collective)两种类型。群组几何对象可以多角度的展现数据结果。
group (分组)这种图形属性可以用来设置:哪些观测值控制哪种图形元素
举例:纵向数据集Oxboys(nlme包)记录了26个男生(subject)在9个不同时期(occasion)中测定的身高(height)和中心化年龄(age)。
data(Oxboys, package = "nlme")
> head(Oxboys)
#> Subject age height Occasion ## 以下是输出结果
#> 1 1 -1.0000 140 1
#> 2 1 -0.7479 143 2
#> 3 1 -0.4630 145 3
#> 4 1 -0.1643 147 4
#> 5 1 -0.0027 148 5
#> 6 1 0.2466 150 6
多个分组+单个图形属性
当我们想从总体上查看数据,将数据中的每个个体区分开,然后用同样的方式映射。这在含有多个个体的纵向数据中很常见,叫“细面图”(spaghetti plot)。
例如,我们想在整体上观察26个男生的成长轨迹(并不区分哪条线是那个男生),就可以在映射 aes() 中添加参数 group=Subject :
ggplot(Oxboys, aes(age, height, group = Subject)) +
geom_point() +
geom_line()

如果不加 group 这个参数,我们只会得到一条将所有点连起来的奇怪的折线,这就毫无疑义了
在不同图层进行分组
如果我们想要将不同水平下的数据加以整合得到一个汇总信息,这时,基本的数据图层上是每个个体的数据,我们可以在第二个数据层上展示整体组群的信息。
例如在上面的例子中,我们想根据所有男孩的年龄和身高在图中添加一个平滑线条,如果按照上面的方式,在第一层函数中直接添加 group=Subject 就会给每个男孩的线条上加一个平滑曲线,这不能得到我们想要的结果:
ggplot(Oxboys, aes(age, height, group = Subject)) +
geom_line() +
geom_smooth(method = "lm", se = FALSE)
这时我们应该把分组参数放到另一个图层中,正确的代码如下:
ggplot(Oxboys, aes(age, height)) +
geom_line(aes(group = Subject)) +
geom_smooth(method = "lm", size = 2, se = FALSE)

修改默认分组
在这里首先补充说明一下变量的分类:变量按其数值表现是否连续,分为连续变量和离散变量。
离散变量 :指变量值可以按一定顺序一一列举,通常以 整数位 取值的变量。如职工人数、工厂数、机器台数等。
连续变量 :在一定区间内可以任意取值的变量叫连续变量,其数值是连续不断的,相邻两个数值可作无限分割,即可 取无限个数值 。
当离散变量存在时,一般就会将其认为是默认分组变量。
如果图像中含有离散型变量,而你却想绘制连接所有分组的线条。这时就要修改默认分组。或者在新图层中设定一个新的分组,就能将二者结合起来。
在上个例子中,绘制各个时期(Occasion)和身高(height)的箱线图,离散型变量Occasion默认为分组变量:
ggplot(Oxboys, aes(Occasion, height)) +
geom_boxplot()

然后要在此基础上添加个体轨迹,这时只添加 geom line() 函数不能达到预期效果,因为它还会默认Occasion为分组变量,这时应该将 aes(group=Subject) 添加在 geom line() 函数中,另外添加颜色属性 colour 将第二个图层区分开:
ggplot(Oxboys, aes(Occasion, height)) +
geom_boxplot() +
geom_line(aes(group = Subject), colour = "#3366FF", alpha = 0.5)

匹配图形属性和图形对象
如何将个体的图形属性映射给整体图形属性。
- 线段和路径类
例如建立一个数据框,包括三个数据点,两条连接三点的线段。数据点比线段条数多一,线段的图形属性是由起始点的图形属性决定的,所以第一条线段使用第一个数据点的图形属性,第二条线段使用第二个数据点的图形属性,而第三个点的图形属性将不会被用到。下面代码生成图片的颜色是随着数据点,呈现离散状态。
library(ggplot2)
df <- data.frame(x = 1:3, y = 1:3, colourname = c(1,3,5))
ggplot(df, aes(x, y, colour = factor(colourname))) +
geom_line(aes(group = 1), size = 2) + ## group = 1 默认分组设置
geom_point(size = 5)

下面修改可以将颜色属性设置为渐变状态,但是数据点还是突出的:
ggplot(df, aes(x, y, colour = colourname)) +
geom_line(aes(group = 1), size = 2) +
geom_point(size = 5)

在复杂一些,利用“线性插值法”可以将线段平稳地在图形属性间变化:
啥是“线性插值法”:已知坐标 (x0, y0) 与 (x1, y1),要得到 [x0, x1] 区间内某一位置 x 在直线上的值。
library(ggplot2)
df <- data.frame(x = 1:3, y = 1:3, colour = c(1,3,5))
xgrid <- with(df, seq(min(x), max(x), length = 50)) ## seq()是生成一组数字
interp <- data.frame(
x = xgrid,
y = approx(df$x, df$y, xout = xgrid)$y, ##approx()是排序
colour = approx(df$x, df$colour, xout = xgrid)$y
)
ggplot(interp, aes(x, y, colour = colour)) +
geom_line(size = 2) +
geom_point(data = df, size = 5)

- 多边形等
对于多边形这类几何图像,当所有个体的图形属性都相同时,可以使用 fill() 参数。
当图形映射对象是离散型对象时,默认将群组几何对象分解成更小的部分。
如下例,耗油量数据集(mpg)中,用柱状图表示不同类别的汽车数量:
library(ggplot2)
mpg
ggplot(mpg, aes(class)) +
geom_bar()

进一步,通过 fill() 参数,用不同颜色表示drv(动力传动系统:前轮f,后轮r,四轮4),填充每个class:
library(ggplot2)
mpg
ggplot(mpg, aes(class, fill = drv)) +
geom_bar()

drv是离散型变量,所以可以用不同的颜色区分
如果根据连续型变量hwy(高速公路行驶记录每加仑行驶的英里数)填充class,如果不添加分组信息,则不会有不同颜色填充。
ggplot(mpg, aes(class, fill = hwy)) +
geom_bar()
如果在 fill() 后面添加 group() 参数,设置按 hwy 分组:
ggplot(mpg, aes(class, fill = hwy, group = hwy)) +
geom_bar()

就可以形成按照hwy数量大小,渐变填充的状态。
5. 曲面图(Surface Plots)
ggplot2包不能绘制3D曲面图像,但是可绘制能表示3D图的2D图像,比如等高线图、着色瓦片图、气泡图等
geom_contour() 等高线图:
library(ggplot2)
faithfuld
# A tibble: 5,625 x 3
eruptions waiting density
<dbl> <dbl> <dbl>
1 1.60 43.0 0.00322
2 1.65 43.0 0.00384
3 1.69 43.0 0.00444
4 1.74 43.0 0.00498
5 1.79 43.0 0.00542
6 1.84 43.0 0.00574
7 1.88 43.0 0.00592
8 1.93 43.0 0.00594
9 1.98 43.0 0.00581
10 2.03 43.0 0.00554
# ... with 5,615 more rows
ggplot(faithfuld, aes(eruptions, waiting)) +
geom_contour(aes(z = density, colour = ..level..))

geom_raster() 着色瓦片图:
ggplot(faithfuld, aes(eruptions, waiting)) +
geom_raster(aes(fill = density))

气泡图:
library(ggplot2)
faithfuld
# Bubble plots work better with fewer observations
small <- faithfuld[seq(1, nrow(faithfuld), by = 10), ]
ggplot(small, aes(eruptions, waiting)) +
geom_point(aes(size = density), alpha = 1/3) +
scale_size_area()

参考资料:
Hadley Wickham(2016). ggplot2. Springer International Publishing. doi: 10.1007/978-3-319-24277-4
《R语言应用系列丛书·ggplot2:数据分析与图形艺术》