Table of Contents
引言
当你制作探索性图表时,甚至在查看之前,你就知道该图表会展示哪些变量。你制作每一张图表都有明确目的,能够快速看一眼,然后继续看下一张图表。在大多数分析过程中,你会生成几十甚至几百张图表,其中大多数会被立刻丢弃。
现在你已经理解了数据,接下来需要把你的理解传达给他人。你的受众很可能不会和你拥有相同的背景知识,也不会对这些数据投入很深的兴趣。为了帮助他人快速建立起对数据的良好认知,你需要花费相当多的精力,让你的图表尽可能做到自解释。在本章中,你将学习 ggplot2 提供的一些工具来实现这一点。
本章聚焦于创建优秀图形所需的工具。我们假设你已经知道自己想要什么,只是需要知道如何去实现。因此,我们非常建议将本章与一本优秀的通用可视化书籍搭配阅读。我们尤其喜欢 Albert Cairo 的《The Truthful Art》。这本书并不教授创建可视化的具体操作方法,而是侧重于如何思考,才能制作出有效的图形。
标签
将探索性图形转换为说明性图形时,最容易入手的地方就是添加良好的标签。你可以使用 labs() 函数来添加标签。
library(ggplot2)ggplot(mpg, aes(x = displ, y = hwy)) + geom_point(aes(color = class)) + geom_smooth(se = FALSE) + labs( x = "Engine displacement (L)", y = "Highway fuel economy (mpg)", color = "Car type", title = "Fuel efficiency generally decreases with engine size", subtitle = "Two seaters (sports cars) are an exception because of their light weight", caption = "Data from fueleconomy.gov" )
图表标题的目的是概括主要发现。应避免那种只描述图表内容的标题,例如“发动机排量与燃油经济性的散点图”。
如果你需要添加更多文本,还有另外两个有用的标签:subtitle 会在标题下方以较小的字体添加更多细节,而 caption 会在图表右下角添加文本,通常用于说明数据来源。你也可以使用 labs() 来替换坐标轴和图例标题。通常,最好把简短的变量名替换为更详细的描述,并注明单位。
也可以使用数学公式来代替文本字符串。只需把 "" 换成 quote(),并查看 ?plotmath 中可用的选项。
library(dplyr)df <- tibble( x = 1:10, y = cumsum(x^2))ggplot(df, aes(x, y)) + geom_point() + labs( x = quote(x[i]), y = quote(sum(x[i] ^ 2, i == 1, n)) )
注释
除了为图表的主要组成部分添加标签之外,为单个观测值或观测组添加标签也常常很有用。你手头的第一个工具是 geom_text()。geom_text() 与 geom_point() 类似,但它多了一个额外的美学映射:label。这使得你可以为图表添加文本标签。
标签有两个可能的来源。首先,你可能有一个提供标签的 tibble。在下面的图中,我们筛选出每种驱动类型中发动机排量最大的汽车,并将它们的信息保存为一个名为 label_info 的新数据框。
# 从 mpg 数据集中筛选出每种驱动类型(drv)里排量(displ)最大的汽车# 并为这些记录创建更容易理解的驱动类型标签label_info <- mpg |> # 按驱动类型分组,确保后续操作在每个驱动类型内部独立进行 group_by(drv) |> # 在每个组内按排量从大到小排序,让排量最大的记录排在最前面 arrange(desc(displ)) |> # 取每个组的第一条记录,也就是该驱动类型中排量最大的那辆车 slice_head(n = 1) |> # 新建一个更可读的变量 drive_type,把简写的 drv 转成完整英文描述 mutate( drive_type = case_when( drv == "f" ~ "front-wheel drive", # 前轮驱动 drv == "r" ~ "rear-wheel drive", # 后轮驱动 drv == "4" ~ "4-wheel drive" # 四轮驱动 ) ) |> # 只保留后续绘图或标注真正需要的变量 select(displ, hwy, drv, drive_type)# 打印查看筛选和整理后的结果label_info
# 使用 mpg 数据集创建一个散点图,查看发动机排量(displ)与高速油耗(hwy)的关系,# 并根据驱动类型(drv)着色ggplot(mpg, aes(x = displ, y = hwy, color = drv)) + # 绘制散点,alpha = 0.3 让点更透明,避免重叠过于拥挤 geom_point(alpha = 0.3) + # 添加平滑趋势线,不显示标准误差带 geom_smooth(se = FALSE) + # 添加文本标签,使用 label_info 数据框中的内容 # 这里单独指定数据源,是为了只给少数关键点加注释 geom_text( data = label_info, aes(x = displ, y = hwy, label = drive_type), # fontface = "bold" 让标签加粗,size 控制字号 # hjust 和 vjust 用来微调标签相对点的位置 fontface = "bold", size = 5, hjust = "right", vjust = "bottom" ) + # 隐藏图例,因为标签已经直接说明了驱动类型 theme(legend.position = "none")
你也可以使用相同的思路,通过 ggrepel 包中的 geom_text_repel() 来突出图中的某些点。再注意这里用到的另一个实用技巧:我们额外添加了第二层较大的空心点,以进一步突出这些被标注的点。
# install.packages("ggrepel")# 加载 ggrepel 包,用于绘制自动避让的文本标签,避免标签重叠library(ggrepel)# 筛选出潜在离群点:# 1) 高速油耗 hwy 大于 40 的车辆;# 2) 或者同时满足高速油耗大于 20 且发动机排量 displ 大于 5 的车辆potential_outliers <- mpg |> filter(hwy > 40 | (hwy > 20 & displ > 5))# 绘制散点图:横轴为发动机排量(displ),纵轴为高速油耗(hwy)ggplot(mpg, aes(x = displ, y = hwy)) + # 绘制所有车辆的散点 geom_point() + # 为潜在离群点添加文本标签,标签内容使用车型名称 model # geom_text_repel() 会自动调整标签位置,尽量避免与其他点和标签重叠 geom_text_repel(data = potential_outliers, aes(label = model)) + # 用红色点突出显示这些潜在离群点,便于观察 geom_point(data = potential_outliers, color = "red") + # 再次用更醒目的红色空心圆圈标出这些点 # size 控制圈的大小,shape = "circle open" 表示空心圆 geom_point( data = potential_outliers, color = "red", size = 3, shape = "circle open" )
请记住,除了 geom_text() 和 geom_label() 之外,ggplot2 中还有许多其他几何对象可用于帮助你为图表添加注释。下面是一些想法:
- 使用
geom_hline()和geom_vline()来添加参考线。我们通常会把它们画得较粗(linewidth = 2)并设为白色(color = "white"),同时将它们放在主要数据层的下方。这样可以让它们清晰可见,同时又不会把注意力从数据本身上移开。 - 使用
geom_rect()可以在感兴趣的点周围绘制一个矩形。矩形的边界由美学映射xmin、xmax、ymin、ymax来定义。或者,你也可以查看ggforce包,特别是geom_mark_hull(),它可以让你用凸包来标注点的子集。 - 使用带有
arrow参数的geom_segment()来通过箭头突出某个点。使用美学映射x和y来定义起始位置,使用xend和yend来定义结束位置。
另一个用于向图表添加注释的实用函数是 annotate()。一般来说,geom 通常适合突出显示数据的一个子集,而 annotate() 则适合向图表中添加一个或少数几个注释元素。
为了演示如何使用 annotate(),我们先创建一些要添加到图中的文本。由于这段文本有点长,我们会使用 stringr::str_wrap(),根据你希望每行显示的字符数自动给它添加换行:
library(stringr)# 将一段说明文字按指定宽度自动换行,便于在图表中显示trend_text <- "Larger engine sizes tend to have lower fuel economy." |> str_wrap(width = 30)# 输出处理后的文本trend_text然后,我们添加两层注释:一层使用标签几何对象,另一层使用线段几何对象。两者中的 x 和 y 美学映射定义注释应从何处开始,而线段注释中的 xend 和 yend 美学映射定义线段的结束位置。另请注意,这条线段被设置成了箭头样式。
# 绘制 mpg 数据集中发动机排量(displ)与高速油耗(hwy)的散点图ggplot(mpg, aes(x = displ, y = hwy)) + geom_point() + # 添加标签注释:在图中指定位置放置一段说明文字,用来解释总体趋势 annotate( geom = "label", # 使用标签几何对象,显示带边框的文本框 x = 3.5, # 标签的横坐标位置 y = 38, # 标签的纵坐标位置 label = trend_text, # 标签内容,使用前面整理好的趋势说明文字 hjust = "left", # 文本水平左对齐,使排版更自然 color = "red" # 文本颜色设置为红色,便于突出显示 ) + # 添加线段注释:用一条带箭头的线段,把文字和图中的目标位置联系起来 annotate( geom = "segment", # 使用线段几何对象,绘制一条连接线 x = 3, # 线段起点的横坐标 y = 35, # 线段起点的纵坐标 xend = 5, # 线段终点的横坐标 yend = 25, # 线段终点的纵坐标 color = "red" # 线段颜色设置为红色,与标签保持一致 arrow = arrow(type = "closed") # 在线段末端添加闭合箭头,增强指向性 )
注释是传达可视化主要结论和有趣特征的强大工具。唯一的限制只是你的想象力(以及你为了让注释排版更美观而调整位置时的耐心)!
比例尺
改进图表以便更好地传达信息的第三种方法是调整比例尺。比例尺控制美学映射在视觉上的呈现方式。
默认比例尺
通常情况下,ggplot2 会自动为你添加比例尺。例如,当你输入:
ggplot(mpg, aes(x = displ, y = hwy)) + geom_point(aes(color = class))ggplot2 会在后台自动添加默认比例尺:
ggplot(mpg, aes(x = displ, y = hwy)) + geom_point(aes(color = class)) + scale_x_continuous() + scale_y_continuous() + scale_color_discrete()请注意尺度(比例尺)的命名规则:scale_ 后接美学属性名称,然后是 _,最后是尺度名称。默认尺度的命名取决于它们所对应的变量类型:continuous、discrete、datetime 或 date。scale_x_continuous() 会把 displ 中的数值映射到 x 轴上的连续数轴上,scale_color_discrete() 会为每一类汽车选择颜色,等等。下面你将学习很多非默认尺度。
默认比例尺经过精心选择,能够很好地适用于各种输入。不过,出于两个原因,你可能希望覆盖这些默认设置:
- 你可能会想调整默认尺度的一些参数。这样你就可以更改坐标轴上的刻度,或者图例中的键标签。
- 你可能想完全替换该尺度,并使用一种完全不同的算法。通常,由于你对数据了解更多,因此可以做得比默认设置更好。
坐标轴刻度和图例键
统称起来,坐标轴和图例被称为导引。坐标轴用于 x 和 y 美学属性;图例用于其他所有美学属性。
有两个主要参数会影响坐标轴上刻度以及图例中的键的外观:breaks 和 labels。breaks 控制刻度的位置,或与键相关联的值。labels 控制与每个刻度/键对应的文本标签。breaks 最常见的用途是覆盖默认选择:
ggplot(mpg, aes(x = displ, y = hwy, color = drv)) + geom_point() + scale_y_continuous(breaks = seq(15, 40, by = 5)) 
你可以以同样的方式使用标签(一个长度与刻度相同的字符向量),但也可以将其设置为 NULL 以完全隐藏标签。这对于地图,或者用于发布那些你不能分享绝对数值的图表时会很有用。你也可以使用刻度和标签来控制图例的外观。对于分类变量的离散尺度,标签可以是一个命名列表,其中包含现有的级别名称及其对应的期望标签。
# 绘制 mpg 数据集中排量与高速油耗的散点图,并按驱动方式着色ggplot(mpg, aes(x = displ, y = hwy, color = drv)) + # 添加散点层 geom_point() + # 隐藏 x 轴刻度标签 scale_x_continuous(labels = NULL) + # 隐藏 y 轴刻度标签 scale_y_continuous(labels = NULL) + # 自定义图例标签:4=四驱,f=前驱,r=后驱 scale_color_discrete(labels = c("4" = "4-wheel", "f" = "front", "r" = "rear"))
labels 参数配合 scales 包中的标注函数,也可用于将数字格式化为货币、百分比等。左侧图展示了使用 label_dollar() 的默认标注方式,它会添加美元符号以及千位分隔符逗号。右侧图则通过将美元值除以 1,000,并添加后缀 “K”(表示“千”),再加上自定义的刻度,进一步实现了个性化设置。请注意,breaks 仍然采用数据的原始尺度。
library(scales)# Leftggplot(diamonds, aes(x = price, y = cut)) + geom_boxplot(alpha = 0.05) + scale_x_continuous(labels = label_dollar())# Rightggplot(diamonds, aes(x = price, y = cut)) + geom_boxplot(alpha = 0.05) + scale_x_continuous( labels = label_dollar(scale = 1/1000, suffix = "K"), breaks = seq(1000, 19000, by = 6000) )
另一个很实用的标签函数是 label_percent():
# 使用 diamonds 数据集,按 cut 作为 x 轴,并按 clarity 填充颜色ggplot(diamonds, aes(x = cut, fill = clarity)) + # 绘制堆叠比例柱状图,按总量归一化为 100% geom_bar(position = "fill") + # 设置 y 轴名称为 Percentage,并将刻度标签显示为百分比格式 scale_y_continuous(name = "Percentage", labels = label_percent())
breaks 的另一个用途是:当你只有相对较少的数据点,并希望准确突出观测值出现的位置时。比如,下面这张图展示了每位美国总统开始和结束任期的时间。
# 对 presidential 数据进行处理,生成按顺序递增的 idpresidential |> mutate(id = 33 + row_number()) |> # 绘制开始时间与 id 的点线图 ggplot(aes(x = start, y = id)) + # 添加每位总统任期开始时间的点 geom_point() + # 添加从 start 到 end 的线段,表示任期区间 geom_segment(aes(xend = end, yend = id)) + # 设置 x 轴为日期轴,去掉名称,并以总统任期开始日期作为刻度,标签显示为两位年份 scale_x_date(name = NULL, breaks = presidential$start, date_labels = "'%y")
请注意,对于 breaks 参数,我们使用 presidential$start 将 start 变量提取为一个向量,因为这个参数不能进行美学映射。另请注意,日期和日期时间尺度中 breaks 与 labels 的指定方式略有不同:
breaks不能像aes()里的变量那样“按数据行自动映射”,所以这里要直接传入一个向量:presidential$start。presidential$start的意思是把数据框里start这一列取出来,作为breaks的具体位置。date_labels用来控制刻度标签怎么显示,它后面接的是一种格式字符串,比如年份、月份、日期的显示格式。date_breaks用来控制刻度间隔,比如每 2 天、每 1 个月一个刻度,所以它接收像"2 days"、"1 month"这样的字符串。
图例布局
你最常会使用 breaks 和 labels 来调整坐标轴。虽然它们也可以用于图例,但还有一些其他技巧你更可能会用到。
要控制图例的整体位置,你需要使用 theme() 设置。我们会在本章结尾回到主题(theme)部分,但简单来说,它们用于控制图形中非数据的部分。theme 设置中的 legend.position 控制图例的绘制位置:
# 创建一个基础散点图:x 轴为发动机排量 displ,y 轴为高速油耗 hwy
# 并根据 class 给点上色
base <- ggplot(mpg, aes(x = displ, y = hwy)) +
geom_point(aes(color = class))
# 将图例放在右侧(默认位置)
base + theme(legend.position = "right") # the default
# 将图例放在左侧
base + theme(legend.position = "left")
# 将图例放在上方,并将颜色图例分成 3 行显示
base +
theme(legend.position = "top") +
guides(color = guide_legend(nrow = 3))
# 将图例放在下方,并将颜色图例分成 3 行显示
base +
theme(legend.position = "bottom") +
guides(color = guide_legend(nrow = 3))
如果你的绘图区域偏矮宽,就将图例放置在顶部或底部;若绘图区域偏高窄,则把图例放在左侧或右侧。你也可以通过设置 legend.position = “none” 来彻底隐藏图例。
若要单独控制各个图例的展示效果,可搭配guides()函数与guide_legend()函数或guide_colorbar()函数使用。下方示例展示了两项重要设置:通过nrow参数设置图例所占行数,以及修改某项美学参数来放大数据点。倘若你在绘图时设置了较低透明度以呈现大量数据点,该用法会格外实用。
# 创建一个散点图:x 轴为发动机排量 displ,y 轴为高速油耗 hwy# 点的颜色按 class 分类ggplot(mpg, aes(x = displ, y = hwy)) + geom_point(aes(color = class)) + # 添加平滑曲线,不显示置信区间 geom_smooth(se = FALSE) + # 将图例放在底部 theme(legend.position = "bottom") + # 调整颜色图例: # - 分成 2 行显示 # - override.aes 用于修改图例中的点样式 # 这里把图例点的大小设为 4 guides(color = guide_legend(nrow = 2, override.aes = list(size = 4)))
注意,guides() 中参数的名称与该美学的名称一致,就像在 labs() 中一样。
替换比例尺
与其只是稍微调整细节,不如直接完全替换比例尺。你最可能想要替换的比例尺主要有两类:连续位置比例尺和颜色比例尺。幸运的是,同样的原则也适用于其他所有美学,因此一旦你掌握了位置和颜色,就能很快学会其他比例尺的替换。
绘制变量的变换通常非常有用。例如,如果对克拉和价格进行对数变换,就更容易看清它们之间的精确关系:
# Leftggplot(diamonds, aes(x = carat, y = price)) + geom_bin2d()#> `stat_bin2d()` using `bins = 30`. Pick better value `binwidth`.# Rightggplot(diamonds, aes(x = log10(carat), y = log10(price))) + geom_bin2d()#> `stat_bin2d()` using `bins = 30`. Pick better value `binwidth`.
然而,这种转换的缺点在于,轴线现在是用转换后的值来标记的,这使得解读图表变得困难。我们可以在美学映射中不进行转换,而是用刻度来进行转换。这在视觉上是一样的,只是轴线是按照原始数据刻度来标记的。
ggplot(diamonds, aes(x = carat, y = price)) + geom_bin2d() + scale_x_log10() + scale_y_log10()
另一个经常需要自定义的比例尺是颜色。默认的分类比例尺会选择在色轮上均匀分布的颜色。一个有用的替代方案是 ColorBrewer 比例尺,它们经过人工调校,对常见色盲人群更友好。下面这两幅图看起来相似,但红色和绿色的色调差异足够明显,因此右图中的点即使对红绿色盲的人也能区分出来。
ggplot(mpg, aes(x = displ, y = hwy)) + geom_point(aes(color = drv))ggplot(mpg, aes(x = displ, y = hwy)) + geom_point(aes(color = drv)) + scale_color_brewer(palette = "Set1")
不要忘记一些更简单的可访问性改进技巧。如果颜色数量不多,你可以添加一个冗余的形状映射。这也有助于确保你的图在黑白情况下仍然易于解读。
ggplot(mpg, aes(x = displ, y = hwy)) + geom_point(aes(color = drv, shape = drv)) + scale_color_brewer(palette = "Set1")
ColorBrewer 比例尺的文档可在 https://colorbrewer2.org/ 在线查看,并可通过 Erich Neuwirth 开发的 RColorBrewer 包在 R 中使用。图 11.1 展示了所有调色板的完整列表。如果你的分类值是有序的,或者有一个“中间值”,那么顺序型(上方)和发散型(下方)调色板尤其有用。这种情况通常会在你使用 cut() 将连续变量转换为分类变量时出现。

当你已经预先定义了数值与颜色之间的映射时,可以使用 scale_color_manual()。例如,如果我们把总统所属政党映射为颜色,就希望采用标准对应关系:共和党用红色,民主党用蓝色。分配这些颜色的一种方法是使用十六进制颜色代码:
presidential |> mutate(id = 33 + row_number()) |> ggplot(aes(x = start, y = id, color = party)) + geom_point() + geom_segment(aes(xend = end, yend = id)) + scale_color_manual(values = c(Republican = "#E81B23", Democratic = "#00AEF3"))
对于连续颜色,可以使用内置的 scale_color_gradient() 或 scale_fill_gradient()。如果你使用的是发散型比例尺,可以使用 scale_color_gradient2()。这样可以让你为正值和负值指定不同的颜色。例如,当你想区分均值以上和均值以下的点时,这也很有用。
另一种选择是使用 viridis 颜色比例尺。设计者 Nathaniel Smith 和 Stéfan van der Walt 精心设计了连续色彩方案,使其不仅能被各种色盲人群感知,而且在颜色和黑白显示中都具有知觉上的一致性。这些比例尺在 ggplot2 中提供连续型(c)、离散型(d)和分箱型(b)调色板。
# 创建包含 10000 个随机正态分布样本的二维数据框df <- tibble( x = rnorm(10000), y = rnorm(10000))# 默认连续色标的六边形分箱图ggplot(df, aes(x, y)) + geom_hex() + coord_fixed() + labs(title = "Default, continuous", x = NULL, y = NULL)# 使用 Viridis 连续色标的六边形分箱图ggplot(df, aes(x, y)) + geom_hex() + coord_fixed() + scale_fill_viridis_c() + labs(title = "Viridis, continuous", x = NULL, y = NULL)# 使用 Viridis 分箱色标的六边形分箱图ggplot(df, aes(x, y)) + geom_hex() + coord_fixed() + scale_fill_viridis_b() + labs(title = "Viridis, binned", x = NULL, y = NULL)


请注意,所有颜色比例尺都有两种形式:分别对应 color 和 fill 美学映射的 scale_color_*() 和 scale_fill_*()(其中 color 比例尺同时支持英式和美式拼写)。
缩放
控制图形范围有三种方式:
- 调整要绘制的数据。
- 在每个比例尺中设置范围。
- 在
coord_cartesian()中设置xlim和ylim。
我们将通过一系列图形来演示这些选项。左侧的图展示了发动机排量与燃油效率之间的关系,并按驱动类型着色。右侧的图展示了相同的变量,但只绘制了数据的一个子集。对数据进行子集化不仅影响了 x 和 y 比例尺,也影响了平滑曲线。
# Leftggplot(mpg, aes(x = displ, y = hwy)) + geom_point(aes(color = drv)) + geom_smooth()# Rightmpg |> filter(displ >= 5 & displ <= 6 & hwy >= 10 & hwy <= 25) |> ggplot(aes(x = displ, y = hwy)) + geom_point(aes(color = drv)) + geom_smooth()
让我们把这些与下面的两个图进行比较:左图是在各个比例尺中设置范围,右图是在 coord_cartesian() 中设置范围。我们可以看到,缩小范围等同于对数据进行子集筛选。因此,如果要放大图中的某个区域,通常最好使用 coord_cartesian()。
# Leftggplot(mpg, aes(x = displ, y = hwy)) + geom_point(aes(color = drv)) + geom_smooth() + scale_x_continuous(limits = c(5, 6)) + scale_y_continuous(limits = c(10, 25))# Rightggplot(mpg, aes(x = displ, y = hwy)) + geom_point(aes(color = drv)) + geom_smooth() + coord_cartesian(xlim = c(5, 6), ylim = c(10, 25))
另一方面,如果你想扩展范围,例如使不同图形之间的比例尺一致,那么通常在单个比例尺上设置范围会更有用。比如,如果我们提取两类汽车并分别绘图,由于三个比例尺(x 轴、y 轴和颜色美学映射)都具有不同的范围,因此很难比较这些图形。
suv <- mpg |> filter(class == "suv")compact <- mpg |> filter(class == "compact")# Leftggplot(suv, aes(x = displ, y = hwy, color = drv)) + geom_point()# Rightggplot(compact, aes(x = displ, y = hwy, color = drv)) + geom_point()
克服这一问题的一种方法是在多个图形之间共享比例尺,并使用完整数据的范围来训练这些比例尺。
x_scale <- scale_x_continuous(limits = range(mpg$displ))y_scale <- scale_y_continuous(limits = range(mpg$hwy))col_scale <- scale_color_discrete(limits = unique(mpg$drv))# Leftggplot(suv, aes(x = displ, y = hwy, color = drv)) + geom_point() + x_scale + y_scale + col_scale# Rightggplot(compact, aes(x = displ, y = hwy, color = drv)) + geom_point() + x_scale + y_scale + col_scale
在这个特定情况下,你本可以直接使用分面,但这种技术在更一般的场景下也很有用,比如当你想把图形分布到报告的多页中时。
主题
最后,你可以使用主题来自定义图形中的非数据元素:
# 使用 mpg 数据集创建散点图,x 轴为发动机排量 displ,y 轴为高速油耗 hwyggplot(mpg, aes(x = displ, y = hwy)) + # 绘制散点图,并按车辆类别 class 着色 geom_point(aes(color = class)) + # 添加平滑曲线,不显示标准误差带 geom_smooth(se = FALSE) + # 使用黑白主题 theme_bw()
ggplot2 包含图 11.2 所示的八种主题,默认主题是 theme_gray()。

也可以控制每个主题的单独组成部分,比如 y 轴所用字体的大小和颜色。我们已经看到,legend.position 控制图例的绘制位置。使用 theme() 还可以自定义图例的许多其他方面。例如,在下面的图中,我们改变了图例的方向,并为其添加了黑色边框。注意,图例框和图形标题元素的自定义是通过 element_*() 函数完成的。这些函数用于指定非数据组件的样式,例如,标题文本的粗体是在 element_text() 的 face 参数中设置的,而图例边框的颜色则在 element_rect() 的 color 参数中定义。控制标题和副标题位置的主题元素分别是 plot.title.position 和 plot.caption.position。在下面的图中,这些值被设置为 "plot",表示这些元素与整个绘图区对齐,而不是与图面板对齐(默认设置)。另外还使用了几个有用的 theme() 组件来调整标题和副标题文本的排列和格式。
# 使用 mpg 数据集绘制散点图# x 轴表示发动机排量 displ,y 轴表示高速油耗 hwy# 并根据驱动方式 drv 对点进行着色ggplot(mpg, aes(x = displ, y = hwy, color = drv)) + # 绘制每辆车对应的散点 geom_point() + # 添加标题和数据来源说明 labs( title = "发动机越大,燃油经济性通常越低", caption = "数据来源:https://fueleconomy.gov。" ) + # 使用 theme() 自定义图形外观: # - 调整图例位置 # - 设置图例横向排列 # - 给图例外框加黑色边框 # - 将标题设为加粗 # - 让标题和注释相对于整个图形区域对齐 # - 将注释左对齐 theme( legend.position = c(0.6, 0.7), legend.direction = "horizontal", legend.box.background = element_rect(color = "black"), plot.title = element_text(face = "bold"), plot.title.position = "plot", plot.caption.position = "plot", plot.caption = element_text(hjust = 0) )
要查看所有 theme() 组件的概览,请参阅 ?theme 的帮助文档。ggplot2 书籍也是了解主题设置完整细节的绝佳去处。
层
到目前为止,我们讨论的是如何创建和修改单个图形。那么,如果你有多个图形,并且想以某种方式将它们排列在一起,该怎么办呢?patchwork 包允许你把多个独立的图形组合到同一张图中。我们在本章前面已经加载过这个包。
要将两个图并排放置,你只需把它们相加即可。注意,你首先需要创建这些图并将它们保存为对象(在下面的示例中,它们被命名为 p1 和 p2)。然后,使用 + 将它们并排放置。
install.packages("patchwork")library(patchwork)p1 <- ggplot(mpg, aes(x = displ, y = hwy)) + geom_point() + labs(title = "Plot 1")p2 <- ggplot(mpg, aes(x = drv, y = hwy)) + geom_boxplot() + labs(title = "Plot 2")p1 + p2
需要注意的是,在上面的代码块中,我们并没有使用 patchwork 包中的新函数。相反,这个包为 + 运算符添加了新的功能。
你也可以使用 patchwork 创建复杂的图布局。下面,| 将 p1 和 p3 并排放置,/ 则将 p2 移到下一行。
p3 <- ggplot(mpg, aes(x = cty, y = hwy)) + geom_point() + labs(title = "Plot 3")(p1 | p3) / p2
此外,patchwork 还允许你将多个图中的图例收集到一个公共图例中,自定义图例的位置以及各个图的尺寸,并为你的图添加共同的标题、副标题、说明等。下面我们创建 5 个图。我们已经关闭了箱线图和散点图的图例,并使用 & theme(legend.position = "top") 将密度图的图例收集到图的顶部。请注意这里使用的是 & 运算符,而不是通常的 +。这是因为我们修改的是 patchwork 图的主题,而不是单个 ggplot。图例被放置在顶部的 guide_area() 内。最后,我们还自定义了 patchwork 各个组成部分的高度——guide 的高度为 1,箱线图为 3,密度图为 2,分面散点图为 4。Patchwork 会根据这个比例分配你为图预留的区域,并相应地放置各个组件。
p1 <- ggplot(mpg, aes(x = drv, y = cty, color = drv)) + geom_boxplot(show.legend = FALSE) + labs(title = "Plot 1")p2 <- ggplot(mpg, aes(x = drv, y = hwy, color = drv)) + geom_boxplot(show.legend = FALSE) + labs(title = "Plot 2")p3 <- ggplot(mpg, aes(x = cty, color = drv, fill = drv)) + geom_density(alpha = 0.5) + labs(title = "Plot 3")p4 <- ggplot(mpg, aes(x = hwy, color = drv, fill = drv)) + geom_density(alpha = 0.5) + labs(title = "Plot 4")p5 <- ggplot(mpg, aes(x = cty, y = hwy, color = drv)) + geom_point(show.legend = FALSE) + facet_wrap(~drv) + labs(title = "Plot 5")(guide_area() / (p1 + p2) / (p3 + p4) / p5) + plot_annotation( title = "City and highway mileage for cars with different drive trains", caption = "Source: https://fueleconomy.gov." ) + plot_layout( guides = "collect", heights = c(1, 3, 2, 4) ) & theme(legend.position = "top")
如果你想进一步了解如何使用 patchwork 组合和排布多个图,我们建议查看该包官网上的指南:https://patchwork.data-imaginist.com。
发表评论