Table of Contents
引言
鳍更长的企鹅比鳍更短的企鹅更重还是更轻?你可能已经有了答案,但试着让你的回答更精确一些。鳍长与体重之间的关系是什么样的?是正相关还是负相关?是线性的还是非线性的?这种关系是否会因企鹅的物种不同而变化?又是否会因企鹅所生活的岛屿不同而变化?让我们创建一些可视化图表来回答这些问题。
你可以使用palmerpenguins中的penguins数据框(也称为palmerpenguins::penguins)来检验你对这些问题的回答。数据框是一种矩形结构的数据集合,由变量(列)和观测(行)组成。penguins数据集包含了由Kristen Gorman博士和南极帕尔默站LTER项目收集并发布的344条观测数据。
为了让讨论更清晰,我们先定义一些术语:
变量是你可以测量的数量、特征或属性。
数值是变量在被测量时所呈现的状态。变量的数值可能会随着每次测量而变化。
观测是在相似条件下进行的一组测量(通常是在同一时间、针对同一对象完成所有测量)。一次观测会包含多个数值,每个数值对应一个不同的变量。我们有时也将观测称为数据点。
表格数据是一组数值的集合,每个数值都与一个变量和一个观测相关联。当每个数值都位于独立的“单元格”中、每个变量位于各自的列中、每个观测位于各自的行中时,这种表格数据就是整洁的(tidy)。
在此语境中,变量指的是所有企鹅的某一属性,而观测指的是单只企鹅的所有属性。
在控制台中输入数据框的名称,R会打印其内容的预览。请注意,在该预览的顶部显示为tibble。在tidyverse中,我们使用一种称为tibble的特殊数据框,你很快就会进一步了解它。
> penguins# A tibble: 344 × 8 species island bill_length_mm bill_depth_mm flipper_length_mm body_mass_g sex year <fct> <fct> <dbl> <dbl> <int> <int> <fct> <int> 1 Adelie Torgersen 39.1 18.7 181 3750 male 2007 2 Adelie Torgersen 39.5 17.4 186 3800 female 2007 3 Adelie Torgersen 40.3 18 195 3250 female 2007 4 Adelie Torgersen NA NA NA NA NA 2007 5 Adelie Torgersen 36.7 19.3 193 3450 female 2007 6 Adelie Torgersen 39.3 20.6 190 3650 male 2007 7 Adelie Torgersen 38.9 17.8 181 3625 female 2007 8 Adelie Torgersen 39.2 19.6 195 4675 male 2007 9 Adelie Torgersen 34.1 18.1 193 3475 NA 200710 Adelie Torgersen 42 20.2 190 4250 NA 2007# ℹ 334 more rows# ℹ Use `print(n = ...)` to see more rows该数据框包含8列。若想以另一种方式查看数据,从而看到所有变量以及每个变量的前几条观测,可以使用glimpse()。或者,如果你使用的是RStudio,可以运行View(penguins)来打开一个交互式数据查看器。
该数据框包含8列。若想以另一种方式查看数据,从而看到所有变量以及每个变量的前几条观测,可以运行View(penguins)来打开一个交互式数据查看器。
penguins中的变量包括:
species:企鹅的物种(Adelie、Chinstrap或Gentoo)。
flipper_length_mm:企鹅鳍的长度(单位:毫米)。
body_mass_g:企鹅的体重(单位:克)。
要了解更多关于penguins的数据,可以运行 ?penguins 打开其帮助页面。

最终目标是重现如下可视化图表,用于展示这些企鹅的鳍长与体重之间的关系,并将企鹅的物种纳入考虑。

创建一个ggplot
让我们一步一步地重现这个图。
使用ggplot2时,你从函数ggplot()开始绘图,先定义一个绘图对象,然后在其上添加各个图层。ggplot()的第一个参数是用于绘图的数据集,因此ggplot(data = penguins)会创建一个空的图形,该图已准备好展示penguins数据,但由于我们尚未指定如何进行可视化,目前它仍然是空的。这个图并不十分有趣,但你可以把它看作是一块空白画布,接下来你将在其上绘制图形的各个图层。
# install.packages("ggplot2")library(ggplot2)ggplot(data = penguins)接下来,我们需要告诉ggplot()如何将数据中的信息以可视化形式呈现。ggplot()函数的mapping参数用于定义数据集中变量如何映射到图形的视觉属性(美学属性)。mapping参数始终在aes()函数中进行定义,而aes()中的x和y参数用于指定映射到x轴和y轴的变量。现在,我们只将鳍长映射到x美学属性,将体重映射到y美学属性。ggplot2会在data参数中查找这些被映射的变量,在这里即为penguins数据。
下面的图展示了添加这些映射后的结果。
ggplot( data = penguins, mapping = aes(x = flipper_length_mm, y = body_mass_g))我们的空白画布现在有了更多结构——可以清楚地看到鳍长将显示在何处(x轴),体重将显示在何处(y轴)。但企鹅本身尚未出现在图中。这是因为我们还没有在代码中说明如何将数据框中的观测值呈现在图上。
为此,我们需要定义一个几何对象(geom):也就是图中用来表示数据的几何元素。这些几何对象在ggplot2中通过以geom_开头的函数提供。人们通常会根据图中使用的geom类型来描述图表。例如,柱状图使用柱形几何对象(geom_bar()),折线图使用线形几何对象(geom_line()),箱线图使用箱线几何对象(geom_boxplot()),散点图使用点几何对象(geom_point())等等。
geom_point()函数会为图添加一个点图层,从而创建一个散点图。ggplot2提供了许多geom函数,每个函数都会向图中添加不同类型的图层。
ggplot( data = penguins, mapping = aes(x = flipper_length_mm, y = body_mass_g)) + geom_point()#> Warning: Removed 2 rows containing missing values or values outside the scale range#> (`geom_point()`).
现在我们得到了一个看起来像“散点图”的图形。它还没有完全达到我们的“最终目标”图,但通过这个图,我们已经可以开始回答推动本次探索的问题:“鳍长与体重之间的关系是什么样的?”这种关系似乎是正相关的(鳍越长,体重也越大),相当线性(点大致分布在一条直线附近而不是曲线附近),并且强度适中(围绕这条线的离散程度不大)。通常来说,鳍更长的企鹅体重也更大。
在为该图添加更多图层之前,让我们先停下来回顾一下我们收到的警告信息:
Removed 2 rows containing missing values (geom_point()).
我们之所以会看到这条消息,是因为数据集中有两只企鹅缺少体重和/或鳍长的数值,而在缺少这两个值的情况下,ggplot2无法在图中表示它们。与R一样,ggplot2遵循这样一种理念:缺失值不应被悄然忽略。这类警告可能是你在处理真实数据时最常见的警告之一——缺失值是一个非常普遍的问题,在后续的图中,我们将抑制此警告,以避免它在我们生成的每一个图旁边都被打印出来。
散点图对于展示两个数值变量之间的关系非常有用,但对于任何看起来存在的变量关系,都应保持一定的怀疑,并思考是否存在其他变量能够解释或改变这种表面关系的性质。例如,鳍长与体重之间的关系是否会因物种不同而有所差异?让我们将物种信息纳入图中,看看这是否能为这些变量之间的关系带来更多见解。我们将通过使用不同颜色的点来表示不同的物种。
要实现这一点,我们需要修改美学映射还是几何对象呢?如果你猜的是“在美学映射中,也就是在aes()里面”,那你已经开始掌握使用ggplot2创建数据可视化的要领了!如果没有,也不用担心。你会绘制更多的ggplot图,并在实践中不断检验和提升你的直觉。
ggplot( data = penguins, mapping = aes(x = flipper_length_mm, y = body_mass_g, color = species)) + geom_point()
当将一个分类变量映射到某个美学属性时,ggplot2会自动为该变量的每一个唯一水平(这里是三个物种)分配一个独特的美学取值(此处为不同颜色),这一过程称为缩放(scaling)。ggplot2还会添加一个图例,用于说明这些取值分别对应哪些水平。
现在让我们再添加一个图层:一条平滑曲线,用于展示体重与鳍长之间的关系。在继续之前,请回顾上面的代码,并思考我们如何将其添加到现有的图中。
由于这是一个用于表示数据的新几何对象,我们将添加一个新的geom作为点几何对象之上的一层:geom_smooth()。并且我们将指定使用线性模型绘制最佳拟合直线,即设置method = “lm”。
ggplot( data = penguins, mapping = aes(x = flipper_length_mm, y = body_mass_g, color = species)) + geom_point() + geom_smooth(method = "lm")
我们已经成功添加了线条,但这个图看起来并不像前面的完成图,后者只有一条表示整个数据集的线,而不是为每个企鹅物种分别绘制多条线。
当美学映射在ggplot()中(即全局层面)定义时,它们会被传递到图中的每一个后续geom图层中。不过,ggplot2中的每个geom函数也可以接受一个mapping参数,从而允许在局部层面定义美学映射,并将其与从全局继承的映射叠加。由于我们希望点根据物种进行着色,但不希望线条按物种分开绘制,因此应只在geom_point()中指定color = species。
ggplot( data = penguins, mapping = aes(x = flipper_length_mm, y = body_mass_g)) + geom_point(mapping = aes(color = species)) + geom_smooth(method = "lm")好了!我们现在得到的图已经非常接近我们的最终目标了,虽然还不够完美。我们仍然需要为每种企鹅使用不同的形状,并改进标签。
通常来说,仅使用颜色来表示图中的信息并不是一个好主意,因为由于色盲或其他色觉差异,人们对颜色的感知可能不同。因此,除了颜色之外,我们还可以将物种映射到形状这一美学属性上。
ggplot( data = penguins, mapping = aes(x = flipper_length_mm, y = body_mass_g)) + geom_point(mapping = aes(color = species, shape = species)) + geom_smooth(method = "lm")
请注意,图例也会自动更新,以反映点形状的不同。
最后,我们可以通过在新图层中使用labs()函数来改进图的标签。labs()中的一些参数可能不言自明:title用于添加标题,subtitle用于添加副标题。其他参数与美学映射相对应,x表示x轴标签,y表示y轴标签,而color和shape则定义图例的标签。此外,我们还可以使用ggthemes包中的scale_color_colorblind()函数来优化配色,使其对色盲更友好。
library(ggthemes)ggplot( data = penguins, mapping = aes(x = flipper_length_mm, y = body_mass_g)) + geom_point(aes(color = species, shape = species)) + geom_smooth(method = "lm") + labs( title = "Body mass and flipper length", subtitle = "Dimensions for Adelie, Chinstrap, and Gentoo Penguins", x = "Flipper length (mm)", y = "Body mass (g)", color = "Species", shape = "Species" ) + scale_color_colorblind()
我们终于有了一个与我们的“终极目标”完美匹配的图表!
练习
- penguins 有多少行?有多少列?
> dim(penguins)[1] 344 8- penguins 数据框中的 bill_depth_mm 变量描述的是什么?请查看 ?penguins 的帮助文档来找出答案。
bill_depth_mm 表示企鹅喙的深度,单位是毫米。
- 绘制 bill_depth_mm 与 bill_length_mm 的散点图。也就是说,绘制一个以 bill_depth_mm 为纵轴、bill_length_mm 为横轴的散点图。描述这两个变量之间的关系。
ggplot(penguins, aes(x = bill_length_mm, y = bill_depth_mm)) + geom_point()- 如果你绘制 species 与 bill_depth_mm 的散点图,会发生什么?
ggplot(penguins, aes(x = species, y = bill_depth_mm)) + geom_plot()- 为什么下面的代码会报错?你会如何修复它?
ggplot(data = penguins) +geom_point()这段代码会报错是因为在 ggplot() 中没有指定美学映射(aes),而 geom_point() 需要明确的 x 和 y 变量才能绘制散点图。
- geom_point() 中的 na.rm 参数有什么作用?该参数的默认值是什么?创建一个散点图,在其中成功地将该参数设置为 TRUE。
na.rm 参数用于控制是否在绘图时自动移除包含缺失值(NA)的观测。如果设置为 TRUE,会忽略这些缺失值而不报错或警告;如果为 FALSE,则可能会出现警告信息。该参数的默认值是 FALSE。
ggplot(penguins, aes(x = bill_length_mm, y = bill_depth_mm)) +geom_point(na.rm = TRUE)- 将以下说明添加到你在上一个练习中制作的图中:“Data come from the palmerpenguins package.” 提示:查看 labs() 的文档。
ggplot(penguins, aes(x = bill_length_mm, y = bill_depth_mm)) + geom_point(na.rm = TRUE) + labs(caption = "Data come from the palmerpenguins package.")- 重现以下可视化。bill_depth_mm 应该映射到哪个美学属性?它应该在全局层级映射,还是在 geom 层级映射?
ggplot(penguins, aes(x = flipper_length_mm, y = body_mass_g, color = bill_depth_mm)) + geom_point() + geom_smooth(se = TRUE)- 在脑海中运行这段代码并预测输出结果会是什么样子。然后在 R 中运行代码,检查你的预测。
ggplot( data = penguins, mapping = aes(x = flipper_length_mm, y = body_mass_g, color = island)) + geom_point() + geom_smooth(se = FALSE)- 这两个图看起来会不同吗?为什么/为什么不?
ggplot( data = penguins, mapping = aes(x = flipper_length_mm, y = body_mass_g)) + geom_point() + geom_smooth()ggplot() + geom_point( data = penguins, mapping = aes(x = flipper_length_mm, y = body_mass_g) ) + geom_smooth( data = penguins, mapping = aes(x = flipper_length_mm, y = body_mass_g) )这两段代码会生成相同的图:一个以 flipper_length_mm 为 x 轴、body_mass_g 为 y 轴的散点图,并叠加一条带置信区间的平滑曲线。
区别在于映射和数据的指定位置:
第一段在 ggplot() 中全局定义了 data 和 aes,因此后续所有几何对象都会继承。
第二段在每个 geom 中分别定义 data 和 aes,但由于设置相同,最终效果一致。
随着我们从这些入门部分继续前进,我们将过渡到更简洁地表达 ggplot2 代码的方式。到目前为止,我们一直非常明确,这在你学习时是有帮助的:
ggplot( data = penguins, mapping = aes(x = flipper_length_mm, y = body_mass_g)) + geom_point()通常,一个函数的前一两个参数非常重要,你应该把它们牢记于心。ggplot() 的前两个参数是 data 和 mapping,其余部分,我们将不再显式写出这些参数名。这样可以减少输入量,并通过减少多余文本,使你更容易看出不同图之间的差异。
将前一个图用更简洁的方式重写如下:
ggplot(penguins, aes(x = flipper_length_mm, y = body_mass_g)) + geom_point()可视化分布
如何对一个变量的分布进行可视化取决于该变量的类型:分类变量或数值变量。
分类变量
如果一个变量只能取一小部分可能值中的一个,那么它就是分类变量。要查看分类变量的分布,可以使用条形图。条形的高度表示每个 x 值对应的观测数量。
ggplot(penguins, aes(x = species)) + geom_bar()
对于具有无序水平的分类变量(例如上面的企鹅物种)的条形图,通常更倾向于根据频数对条形进行重新排序。为此,需要将该变量转换为因子(R 处理分类数据的方式),然后对该因子的水平进行重新排序。
library(forcats)ggplot(penguins, aes(x = fct_infreq(species))) + geom_bar()数值变量
如果一个变量可以取广泛范围的数值,并且对这些数值进行加减或求平均是有意义的,那么它就是数值型变量(或定量变量)。数值型变量可以是连续的,也可以是离散的。
连续变量分布的一种常用可视化方式是直方图。
ggplot(penguins, aes(x = body_mass_g)) + geom_histogram(binwidth = 200)
直方图将 x 轴划分为等宽的区间(bins),并用柱子的高度表示每个区间中的观测数量。在上面的图中,最高的柱子表示有 39 个观测的 body_mass_g 值位于 3500 到 3700 克之间,这两个数值分别是该柱子的左右边界。
你可以使用 binwidth 参数来设置直方图中区间的宽度,该宽度以 x 变量的单位来衡量。在使用直方图时,你应该始终尝试多种不同的 binwidth,因为不同的区间宽度可能会揭示不同的模式。在下面的图中,binwidth 为 20 时过于狭窄,导致柱子过多,使得难以判断分布的形状。同样,binwidth 为 2,000 时又过大,导致所有数据只被分成三根柱子,也难以判断分布的形状。而 binwidth 为 200 则提供了一个较为合理的平衡。


数值变量分布的另一种可视化方式是密度图。密度图是直方图的平滑版本,是一种实用的替代方案,特别适用于来自潜在平滑分布的连续数据。我们不会详细介绍 geom_density() 是如何估计密度的(你可以在函数文档中了解更多),但可以通过一个类比来解释密度曲线是如何绘制的。想象一个由木块组成的直方图,然后再想象把一根煮熟的意大利面条从上面放下。面条覆盖在这些木块上的形状,可以看作是密度曲线的形状。它比直方图展示的细节更少,但可以更容易快速看出分布的形状,特别是在峰值和偏度方面。
ggplot(penguins, aes(x = body_mass_g)) + geom_density()
练习
- 绘制一个企鹅物种的条形图,并将 species 映射到 y 美学属性。这个图有什么不同?
ggplot(penguins, aes(y = body_mass_g)) + geom_histogram(binwidth = 200)将 species 映射到 y 美学属性后,条形图会变为水平条形图,而不是默认的垂直条形图。本质上显示的信息没有变化,仍然是各个企鹅物种的计数,但方向发生了改变,这通常可以让类别名称更容易阅读。
- 以下两个图有什么不同?在改变柱子的颜色时,哪种美学属性(color 还是 fill)更有用?
ggplot(penguins, aes(x = species)) + geom_bar(color = "red")ggplot(penguins, aes(x = species)) + geom_bar(fill = "red")color = “red” 只会改变柱子的边框颜色为红色,柱子内部仍是默认填充(通常是灰色)。
fill = “red” 会将柱子的内部填充为红色,而边框颜色保持默认。
- geom_histogram() 中的 bins 参数有什么作用?
bins 参数用于指定直方图中分箱(柱子)的数量。它会将数据范围划分为指定数量的区间,每个区间对应一根柱子。调整 bins 的值会影响图形的细致程度:值越大,柱子越多、越细;值越小,柱子越少、越宽。
- 在加载 ggplot2包后,使用 diamonds 数据集中的 carat 变量绘制直方图。尝试不同的 binwidth。哪个 binwidth 能揭示最有趣的模式?
ggplot(diamonds,aes(x=carat)) + geom_histogram(binwidth = 0.1)可视化关系
为了可视化变量之间的关系,我们需要将至少两个变量映射到图形的美学属性上。接下来,你将学习用于可视化两个或多个变量关系的常用图形,以及用于创建这些图形的几何对象(geoms)。
一个数值变量和一个分类变量
为了可视化一个数值变量与一个分类变量之间的关系,我们可以使用并排的箱线图。箱线图是一种用于表示位置度量(百分位数)的可视化简写形式,用来描述数据分布。同时,它也有助于识别潜在的异常值。如图 1.1 所示,每个箱线图由以下部分组成:
一个盒子表示数据中间一半范围,这个距离称为四分位距(IQR),从分布的第25百分位延伸到第75百分位。盒子中间有一条线表示中位数,即分布的第50百分位。这三条线可以让你了解分布的离散程度,以及分布是围绕中位数对称还是向一侧偏斜。
用可视化的点表示那些距离盒子边缘超过1.5倍IQR的观测值。这些离群点较为异常,因此会被单独绘制。
一条线(或称“须”)从盒子的两端延伸,直到分布中最远的非离群点。

让我们使用 geom_boxplot() 来查看不同物种的体重分布:
ggplot(penguins, aes(x = species, y = body_mass_g)) + geom_boxplot()
或者,我们可以使用 geom_density() 绘制密度图。
ggplot(penguins, aes(x = body_mass_g, color = species)) + geom_density(linewidth = 0.75)
我们还通过使用 linewidth 参数自定义了线条的粗细,以便它们在背景中更加突出。
此外,我们可以将物种映射到颜色和填充美学属性上,并使用 alpha 美学属性为填充的密度曲线添加透明度。该属性的取值范围在 0(完全透明)到 1(完全不透明)之间。在下面的图中,它被设置为 0.5。
ggplot(penguins, aes(x = body_mass_g, color = species, fill = species)) + geom_density(alpha = 0.5)
请注意我们在这里使用的术语:
如果我们希望某个美学属性所表示的视觉特征根据变量的取值而变化,就将变量映射到该美学属性上。
否则,我们是为该美学属性设置一个固定值。
两个分类变量
我们可以使用堆叠条形图来可视化两个分类变量之间的关系。例如,下面的两个堆叠条形图都展示了岛屿与物种之间的关系,具体来说,是可视化每个岛屿内物种的分布情况。
第一个图显示了每个岛屿上各种企鹅物种的频数。该频数图表明每个岛屿上的阿德利企鹅数量是相同的,但我们无法很好地了解各个岛屿内部的百分比分布情况。

第二个图是通过在几何对象中设置 position = “fill” 创建的相对频率图,对于比较不同岛屿上的物种分布更为有用,因为它不受各岛屿企鹅数量不均的影响。通过该图我们可以看到,Gentoo 企鹅全部生活在 Biscoe 岛,并且约占该岛企鹅数量的 75%;Chinstrap 企鹅全部生活在 Dream 岛,并且约占该岛企鹅数量的 50%;而 Adelie 企鹅生活在三个岛屿上,并且在 Torgersen 岛上占全部企鹅数量。

在创建这些条形图时,我们将用于分隔成不同条形的变量映射到 x 美学属性,将用于改变条形内部颜色的变量映射到 fill 美学属性。不幸的是,ggplot2 默认将 y 轴标记为 “count”,但我们可以通过添加一个 labs() 图层并将 y 轴标签指定为 “proportion” 来覆盖这一设置。
ggplot(penguins, aes(x = island, fill = species)) + geom_bar(position = "fill") + labs(y = "proportion")两个数值变量
到目前为止,你已经学习了如何使用散点图(通过 geom_point() 创建)和光滑曲线(通过 geom_smooth() 创建)来可视化两个数值变量之间的关系。散点图可能是用于展示两个数值变量关系的最常用图形。
ggplot(penguins, aes(x = flipper_length_mm, y = body_mass_g)) + geom_point()
三个或更多变量
正如我们所看到的,我们可以通过将变量映射到其他美学属性上,在图形中加入更多变量。例如,在下面的散点图中,点的颜色代表物种,点的形状代表岛屿。
ggplot(penguins, aes(x = flipper_length_mm, y = body_mass_g)) + geom_point(aes(color = species, shape = island))
然而,在图形中添加过多的美学映射会使其显得杂乱且难以理解。另一种对分类变量特别有用的方法是将图形拆分为分面,即每个子图分别展示数据的一个子集。
要按单个变量对图形进行分面,可以使用 facet_wrap()。facet_wrap() 的第一个参数是一个公式,你可以通过在变量名前加上 ~ 来创建。传递给 facet_wrap() 的变量应当是分类变量。
ggplot(penguins, aes(x = flipper_length_mm, y = body_mass_g)) + geom_point(aes(color = species, shape = species)) + facet_wrap(~island)
练习
- ggplot2 包中附带的 mpg 数据框包含了美国环境保护署针对 38 款汽车模型收集的 234 条观测数据。mpg 中哪些变量是分类变量?哪些变量是数值变量?(提示:输入 ?mpg 查看该数据集的文档。)当你运行 mpg 时,如何查看这些信息?
str(mpg)- 使用 mpg 数据框绘制 hwy 与 displ 的散点图。接下来,将第三个数值变量分别映射到颜色、大小、同时映射到颜色和大小,以及形状。对于分类变量和数值变量,这些美学属性的表现有何不同?
ggplot(mpg, aes(x = displ, y = hwy)) + geom_point(aes(color = cty, size = cty, shape = class))- 在 hwy 与 displ 的散点图中,如果将第三个变量映射到 linewidth,会发生什么?
如果在 hwy 与 displ 的散点图中将第三个变量映射到 linewidth,基本不会产生预期效果。
原因是 linewidth 主要用于线状几何对象(例如 geom_line()、geom_path()),而不是用于点(geom_point())。对于点图,控制大小的美学属性是 size,而不是 linewidth。
因此:
在 geom_point() 中使用 linewidth 通常会被忽略或不起作用
有时可能会出现警告,提示该美学属性未被使用
如果想根据变量改变点的大小,应使用 size 而不是 linewidth
- 如果将同一个变量映射到多个美学属性,会发生什么?
如果将同一个变量映射到多个美学属性(例如同时映射到颜色和大小),这些属性会同时根据该变量发生变化,从而增强数据模式的表达。
具体来说:
对于数值变量:颜色会呈现渐变,大小也会连续变化,两者共同反映数值大小规律
对于分类变量:不同类别会对应不同的颜色和大小组合,更容易区分各组
不过需要注意:
映射过多美学属性可能会让图形显得杂乱,降低可读性
有些组合(如形状 + 大量类别)可能不易区分
- 绘制 bill_depth_mm 与 bill_length_mm 的散点图,并根据 species 为点着色。按物种着色揭示了这两个变量之间的关系的什么信息?那么按物种分面又会如何?
# 按物种着色ggplot(penguins, aes(x = bill_length_mm, y = bill_depth_mm, color = species)) + geom_point()# 按物种分面ggplot(penguins, aes(x = bill_length_mm, y = bill_depth_mm)) + geom_point() + facet_wrap(~ species)按物种着色可以在同一坐标系中比较不同物种的分布情况,显示各物种之间的差异和可能的分组趋势,但可能会有重叠。
按物种分面则将每个物种分开展示,使每一类内部的关系更清晰,更容易观察各自的模式,但不如着色方式直观对比不同物种之间的相对位置。
- 为什么下面的代码会产生两个独立的图例?如何修复以将这两个图例合并?
ggplot( data = penguins, mapping = aes( x = bill_length_mm, y = bill_depth_mm, color = species, shape = species )) + geom_point() + labs(color = "Species") # 注意这里大写了,会被判别为另一个变量,删掉这句即可。- 创建以下两个堆叠柱状图。第一个可以回答什么问题?第二个可以回答什么问题?
ggplot(penguins, aes(x = island, fill = species)) + geom_bar(position = "fill")ggplot(penguins, aes(x = species, fill = island)) + geom_bar(position = "fill")第一个图(x = island, fill = species)可以回答:在每个岛上,不同物种的相对比例是多少?
第二个图(x = species, fill = island)可以回答:对于每个物种,它们分布在不同岛上的相对比例是多少?
保存你的图形
一旦你绘制好了图形,可能希望将其从 R 中导出并保存为图像,以便在其他地方使用。这正是 ggsave() 的作用,它会将最近创建的图形保存到磁盘:
ggplot(penguins, aes(x = flipper_length_mm, y = body_mass_g)) + geom_point()ggsave(filename = "penguin-plot.png")这会将你的图形保存到你的工作目录中,这个概念你将在后面进一步学习。
如果你没有指定宽度和高度,它们将取自当前绘图设备的尺寸。为了实现可复现的代码,建议你明确指定这些参数。你可以在文档中了解更多关于 ggsave() 的内容。
不过,一般来说,我们建议你使用 Quarto 来整合最终报告。Quarto 是一个可复现的写作系统,允许你将代码与文字交织在一起,并自动将图形包含在报告中。
练习
- 运行以下代码行。两个图中哪个被保存为 mpg-plot.png?为什么?
ggplot(mpg, aes(x = class)) + geom_bar()ggplot(mpg, aes(x = cty, y = hwy)) + geom_point()ggsave("mpg-plot.png") # 只保存当前图片- 在上面的代码中,你需要做哪些修改才能将图形保存为 PDF 而不是 PNG?你可以如何查找 ggsave() 支持哪些图像文件类型?
常见问题
当你开始运行 R 代码时,很可能会遇到问题。别担心——这对每个人来说都会发生。我们都已经写了多年的 R 代码,但每天仍然会写出第一次运行就出错的代码!
首先,仔细对比你正在运行的代码和书中的代码。R 对细节非常敏感,一个放错位置的字符就可能带来完全不同的结果。确保每个 ( 都有对应的 ),每个 ” 都成对出现。有时你运行代码却没有任何反应,这时请查看控制台左侧:如果显示的是 +,说明 R 认为你还没有输入完整的表达式,它正在等待你继续输入。在这种情况下,通常可以按下 ESCAPE 键中止当前命令,然后从头重新开始。
在创建 ggplot2 图形时,一个常见的问题是把 + 放在了错误的位置:它必须位于行尾,而不是行首。换句话说,确保你没有不小心写出像下面这样的代码:
ggplot(data = mpg) + geom_point(mapping = aes(x = displ, y = hwy))如果你仍然卡住了,可以尝试查看帮助。你可以在控制台运行 ?function_name 来获取任何 R 函数的帮助,或者在 RStudio 中选中函数名并按下 F1。即使帮助看起来不太有用也没关系——可以直接跳到示例部分,寻找与你想要实现的内容相匹配的代码。
如果这仍然没有帮助,请仔细阅读错误信息。有时候答案就隐藏在其中!不过当你刚接触 R 时,即使答案就在错误信息里,你也可能还不知道如何理解它。另一个很有用的工具是 Google:尝试搜索错误信息,很可能已经有人遇到过相同的问题,并在网上获得了解答。
总结
在本章中,你已经学习了使用 ggplot2 进行数据可视化的基础知识。我们从 ggplot2 的基本理念开始:可视化是将数据中的变量映射到位置、颜色、大小和形状等美学属性上。随后,你学习了如何通过逐层添加来增加图形的复杂性并提升其表现效果。你还了解了如何通过使用额外的美学映射以及使用分面(faceting)将图形拆分为多个小图,从而可视化单个变量的分布,以及两个或多个变量之间的关系等常见图形。
在后续内容中,我们会反复使用可视化,并在需要时介绍新的技术。
在掌握了可视化的基础之后,下面我们将稍微转换一下重点,为你提供一些实用的工作流程建议。
发表评论