数据可视化

1 数据可视化

1.1 引言

“简单的图表给数据分析师带来的信息比任何其他工具都要多。” —— 约翰·图基

R 有几种制作图表的系统,但 ggplot2 是其中最优雅、最多才多艺的之一。ggplot2 实现了图形语法,这是一种描述和构建图表的连贯系统。有了 ggplot2,可以通过学习一个系统并将其应用于许多地方,使得你可以更快地做更多事情。

这一部分内容将教你如何使用 ggplot2 对数据进行可视化。我们将从创建一个简单的散点图开始,然后介绍坐标轴映射(aesthetic mappings)和几何对象(geometric objects)——这是 ggplot2 的基本构建模块。接着,我们将带你逐步了解如何可视化单个变量的分布以及可视化两个或多个变量之间的关系。最后,我们将结束于保存你的图表和故障排除提示。

1.1.1 先决条件

这一部分专注于 ggplot2,这是 tidyverse 中的核心包之一。要访问本章中使用的数据集、帮助页面和函数,请通过运行以下代码加载 tidyverse:

library(tidyverse)
Warning: package 'ggplot2' was built under R version 4.4.2
── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
✔ dplyr     1.1.4     ✔ readr     2.1.5
✔ forcats   1.0.0     ✔ stringr   1.5.1
✔ ggplot2   3.5.1     ✔ tibble    3.2.1
✔ lubridate 1.9.3     ✔ tidyr     1.3.1
✔ purrr     1.0.2     
── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag()    masks stats::lag()
ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors

这一行代码加载了核心 tidyverse;这些包几乎在每个数据分析中都会使用。它还告诉你 tidyverse 中的哪些函数与基本 R 中的函数(或你可能已加载的其他包中的函数)发生了冲突。

如果运行此代码时收到错误消息 there is no package called 'tidyverse',则需要首先安装它,然后再次运行 library()

install.packages("tidyverse")
library(tidyverse)

一个包只需要安装一次,但每次启动新会话时都需要加载它。

除了 tidyverse,我们还将使用 palmerpenguins 包,其中包含了 Palmer 群岛三个岛上企鹅的体量测量数据集,以及 ggthemes 包,该包提供了适用于色盲的安全颜色调色板

library(palmerpenguins)
Warning: package 'palmerpenguins' was built under R version 4.4.2
library(ggthemes)
Warning: package 'ggthemes' was built under R version 4.4.2

1.2 初步步骤

长脚蹼的企鹅比短脚蹼的企鹅体重更多还是更少?你可能已经有了答案,但尝试让你的答案更加精确。脚蹼长度和体重之间的关系是什么样的?是正相关的还是负相关的?是线性的还是非线性的?这种关系是否因企鹅的物种而异?岛屿是否会影响企鹅的这种关系?让我们创建可视化图表来回答这些问题。

1.2.1 企鹅数据框(data.frame)

你可以使用 palmerpenguins 包中的 penguins 数据框(又名 palmerpenguins::penguins)来测试对这些问题的答案。数据框是变量(在列中)和观察值(在行中)的矩形集合。penguins 包含了由 Kristen Gorman 博士和 Palmer Station、南极洲 LTER 收集并提供的 344 个观察值。

为了使讨论更容易理解,让我们定义一些术语:

  • 变量是你可以测量的数量、品质或属性。

  • 是你测量变量时的状态。变量的值可能会随着每次测量而变化。

  • 观察是在类似条件下进行的一组测量(通常你会同时在同一对象上的同一时间进行所有测量)。一个观察将包含几个值,每个值与不同的变量相关联。我们有时将观察称为数据点。

  • 表格数据是一组值,每个值与一个变量和一个观察相关联。如果每个值都放置在自己的“单元格”中,每个变量都放置在自己的列中,每个观察都放置在自己的行中,则表格数据是整洁的。

在这个语境中,变量指的是所有企鹅的属性,观察指的是单个企鹅的所有属性。

在控制台中键入数据框的名称,R 将打印其内容的预览。请注意,预览顶部显示为 tibble。在 tidyverse 中,我们使用称为 tibble 的特殊数据框,你很快将学到更多相关内容。

penguins
# A tibble: 344 × 8
   species island    bill_length_mm bill_depth_mm flipper_length_mm body_mass_g
   <fct>   <fct>              <dbl>         <dbl>             <int>       <int>
 1 Adelie  Torgersen           39.1          18.7               181        3750
 2 Adelie  Torgersen           39.5          17.4               186        3800
 3 Adelie  Torgersen           40.3          18                 195        3250
 4 Adelie  Torgersen           NA            NA                  NA          NA
 5 Adelie  Torgersen           36.7          19.3               193        3450
 6 Adelie  Torgersen           39.3          20.6               190        3650
 7 Adelie  Torgersen           38.9          17.8               181        3625
 8 Adelie  Torgersen           39.2          19.6               195        4675
 9 Adelie  Torgersen           34.1          18.1               193        3475
10 Adelie  Torgersen           42            20.2               190        4250
# ℹ 334 more rows
# ℹ 2 more variables: sex <fct>, year <int>

这个数据框包含 8 列。如果你想要查看所有变量以及每个变量的前几个观察值的替代视图,请使用 glimpse()。或者,如果你在 RStudio 中,可以运行 View(penguins) 来打开一个交互式数据查看器。

glimpse(penguins)
Rows: 344
Columns: 8
$ species           <fct> Adelie, Adelie, Adelie, Adelie, Adelie, Adelie, Adel…
$ island            <fct> Torgersen, Torgersen, Torgersen, Torgersen, Torgerse…
$ bill_length_mm    <dbl> 39.1, 39.5, 40.3, NA, 36.7, 39.3, 38.9, 39.2, 34.1, …
$ bill_depth_mm     <dbl> 18.7, 17.4, 18.0, NA, 19.3, 20.6, 17.8, 19.6, 18.1, …
$ flipper_length_mm <int> 181, 186, 195, NA, 193, 190, 181, 195, 193, 190, 186…
$ body_mass_g       <int> 3750, 3800, 3250, NA, 3450, 3650, 3625, 4675, 3475, …
$ sex               <fct> male, female, female, NA, female, male, female, male…
$ year              <int> 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007, 2007…

在企鹅数据框中包含以下变量:

  1. 物种(species):企鹅的物种(Adelie、Chinstrap 或 Gentoo)。

  2. 脚蹼长度(flipper_length_mm):企鹅的脚蹼长度,以毫米为单位。

  3. 体重(body_mass_g):企鹅的体重,以克为单位。

要了解更多关于企鹅的信息,请运行 ?penguins 打开其帮助页面。

1.2.2 终极目标

我们的最终目标是重新创建以下可视化图表,显示这些企鹅脚蹼长度和体重之间的关系,同时考虑到企鹅的物种。

1.2.3 创建一个 ggplot

让我们逐步重新创建这个图表。

使用 ggplot2,你可以使用 ggplot() 函数开始绘图,定义一个绘图对象,然后向其添加图层。ggplot() 的第一个参数是要在图中使用的数据集,因此 ggplot(data = penguins) 创建了一个空图,准备显示企鹅数据,但由于我们尚未告诉它如何可视化数据,所以目前为空。这不是一个非常令人兴奋的图,但你可以将其想象成一个空画布,你将在其上绘制图表的其他图层。

ggplot(data = penguins)

接下来,我们需要告诉 ggplot() 如何将我们的数据信息在图表中进行可视化呈现。ggplot() 函数的映射参数定义了数据集中的变量如何映射到图表的视觉属性(坐标轴/aesthetic)。映射参数始终在 aes( ) 函数中定义,而 aes() 函数的 x 和 y 参数指定要映射到 x 和 y 轴的变量。目前,我们只将脚蹼长度映射到 x 坐标轴,将体重映射到 y 坐标轴。ggplot2 在数据参数中查找映射的变量,这里是企鹅数据。

下面的图表展示了添加了这些映射的结果。

ggplot(
  data = penguins,
  mapping = aes(x = flipper_length_mm, y = body_mass_g)
)

我们的空画布现在有了更多的结构——清楚地显示了脚蹼长度将显示在哪里(在 x 轴上),体重将显示在哪里(在 y 轴上)。但是企鹅本身还没有出现在图表上。这是因为我们还没有在我们的代码中说明如何在图表上表示来自数据框的观察值。

要做到这一点,我们需要定义一个几何对象(geom):图表用于表示数据的几何对象。这些几何对象在 ggplot2 中通过以 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 认同这样一种哲学:缺失值不应该悄悄地消失。这种警告消息可能是你在处理真实数据时看到的最常见的警告之一 - 缺失值是一个非常普遍的问题,你将在此后中学到更多关于它们的知识。在本章剩余的图表中,我们将抑制此警告,以便它不会与我们制作的每个图表一起打印。

1.2.4 添加坐标轴和图层

散点图对于显示两个数值变量之间的关系非常有用,但是始终对两个变量之间的任何表面关系保持怀疑态度,并询问是否可能存在其他变量来解释或改变这种表面关系的性质,是一个好主意。例如,脚蹼长度和体重之间的关系是否因物种而异?让我们将物种纳入我们的图表,并查看这是否揭示了这些变量之间表面关系的任何额外见解。我们将通过用不同颜色的点表示物种来实现这一点。

要实现这一点,我们需要修改坐标轴还是几何图形?如果你猜到“在坐标轴映射内,aes() 内部”,那么你已经开始逐步掌握使用 ggplot2 创建数据可视化!如果没有,不要担心。在整本书中,你将制作更多的 ggplot,并有更多的机会在制作它们时检查你的直觉。

ggplot(
  data = penguins,
  mapping = aes(x = flipper_length_mm, y = body_mass_g, color = species)
) +
  geom_point()
Warning: Removed 2 rows containing missing values or values outside the scale range
(`geom_point()`).

现在让我们再添加一层:显示体重和脚蹼长度之间关系的平滑曲线。在继续之前,请参考上面的代码,并思考我们如何将其添加到我们现有的图表中。

由于这是一个表示我们数据的新几何对象,我们将在我们的点几何对象的顶部添加一个新的几何图层: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")
`geom_smooth()` using formula = 'y ~ x'
Warning: Removed 2 rows containing non-finite outside the scale range
(`stat_smooth()`).
Warning: Removed 2 rows containing missing values or values outside the scale range
(`geom_point()`).

我们已成功添加了线条,但是这个图表看起来与第 1.2.2 节的图表不同,后者只有一条线表示整个数据集,而不是每个企鹅物种分别显示一条线。

当在 ggplot() 中定义坐标轴映射时,在全局级别,它们被传递给图表的每个后续几何图层。然而,ggplot2 中的每个几何函数也可以接受一个映射参数,这允许在局部级别进行坐标轴映射,这些映射被添加到从全局级别继承的映射中。由于我们希望根据物种对点进行着色,但不希望将线条分开显示,我们应该仅在 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")
`geom_smooth()` using formula = 'y ~ x'
Warning: Removed 2 rows containing non-finite outside the scale range
(`stat_smooth()`).
Warning: Removed 2 rows containing missing values or values outside the scale range
(`geom_point()`).

瞧!我们得到了非常接近我们最终目标的东西,尽管它还不完美。我们仍然需要为每个企鹅物种使用不同的形状,并改进标签。

通常不建议仅在图表上使用颜色来表示信息,因为人们由于色盲或其他色觉差异而对颜色有不同的感知。因此,除了颜色之外,我们还可以将物种映射到形状坐标轴上。

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")
`geom_smooth()` using formula = 'y ~ x'
Warning: Removed 2 rows containing non-finite outside the scale range
(`stat_smooth()`).
Warning: Removed 2 rows containing missing values or values outside the scale range
(`geom_point()`).

请注意,图例会自动更新以反映点的不同形状。

最后,我们可以使用 labs() 函数在新的图层中改进我们图表的标签。labs() 的一些参数可能是不言自明的:title 添加标题,subtitle 添加副标题到图表中。其他参数与坐标轴映射相匹配,x 是 x 轴标签,y 是 y 轴标签,color 和 shape 定义图例的标签。此外,我们可以使用 ggthemes 包中的 scale_color_colorblind() 函数将颜色调色板改进为色盲安全的。

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()
`geom_smooth()` using formula = 'y ~ x'
Warning: Removed 2 rows containing non-finite outside the scale range
(`stat_smooth()`).
Warning: Removed 2 rows containing missing values or values outside the scale range
(`geom_point()`).

我们终于得到了一个与我们的“终极目标”完全匹配的图表!

1.2.5 练习

  1. penguins 中有多少行?有多少列?

  2. penguins 数据框中的 bill_depth_mm 变量描述了什么?阅读 ?penguins 的帮助来找出答案。

  3. 制作 bill_depth_mm vs. bill_length_mm 的散点图。也就是说,制作一个以 bill_depth_mm 为 y 轴,bill_length_mm 为 x 轴的散点图。描述这两个变量之间的关系。

  4. 如果你制作一个物种 vs. bill_depth_mm 的散点图会发生什么?可能更好的几何对象选择是什么?

  5. 为什么以下代码会报错,你该如何修复它?

ggplot(data = penguins) + geom_point()

  1. 在 geom_point() 中,na.rm 参数是什么作用?该参数的默认值是什么?创建一个散点图,在这个参数成功设置为 TRUE。

  2. 在你之前做的散点图中添加以下说明:“数据来自 palmerpenguins 包”。提示:查看 labs() 的文档。

  3. 重新创建下面的可视化。bill_depth_mm 应该映射到哪个坐标轴属性?应该在全局级别还是几何图层级别映射?

  1. 请你在脑海中运行以下代码,并预测输出会是什么样子。然后,在 R 中运行代码,检查你的预测。
ggplot(
  data = penguins,
  mapping = aes(x = flipper_length_mm, y = body_mass_g, color = island)
) +
  geom_point() +
  geom_smooth(se = FALSE)
  1. 这两个图表会有区别吗?为什么?
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)
  )

1.3 ggplot2调用

随着我们从这些介绍性部分过渡到更简洁的 ggplot2 代码,我们将采用更简洁的表达方式。到目前为止,我们一直非常明确,这对于学习很有帮助:

# | eval: false
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()`).

通常,函数的前一个或两个参数非常重要,你应该牢记它们。ggplot()的前两个参数是data和mapping,在本书的其余部分,我们不会提供这些名称。这样做可以节省打字,减少额外的文本量,使得更容易看到图表之间的不同之处。

将上一个图表更简洁地重写为:

# | eval: false
ggplot(penguins, 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()`).

在未来,你还将学习到管道操作符,|>,它允许你使用以下方式创建该图表:

# | eval: false
penguins |> 
  ggplot(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()`).

1.4 可视化分布

如何可视化变量的分布取决于变量的类型:分类变量或数值变量。

1.4.1 分类变量

如果变量只能取一组小的值之一,则该变量是分类变量。要检查分类变量的分布,可以使用条形图。条形的高度显示了每个 x 值发生了多少次观察。

ggplot(penguins, aes(x = species)) +
  geom_bar()

在具有非有序级别的分类变量的条形图中,例如上面的企鹅物种,通常最好根据它们的频率重新排序条形。要这样做,需要将变量转换为因子(R 处理分类数据的方式),然后重新排序该因子的级别。

ggplot(penguins, aes(x = fct_infreq(species))) +
  geom_bar()

你将在后续的学习中更多地了解有关因子和处理因子的函数(例如上面显示的 fct_infreq())。

1.4.2 数值变量

如果一个变量可以取一系列广泛的数值,并且对这些值进行加法、减法或求平均值是合理的,那么这个变量就是数值(或定量)的。数值变量可以是连续的或离散的。

连续变量的分布经常使用直方图进行可视化。

ggplot(penguins, aes(x = body_mass_g)) +
  geom_histogram(binwidth = 200)
Warning: Removed 2 rows containing non-finite outside the scale range
(`stat_bin()`).

直方图将 x 轴分成等间距的箱子,然后使用每个箱子的高度来显示落入该箱子中的观测数量。在上面的图中,最高的柱子显示了39个观测值的 body_mass_g 值落在3,500到3,700克之间,这个范围是柱子的左右边缘。

你可以使用 binwidth 参数设置直方图中间隔的宽度,它以 x 变量的单位度量。在使用直方图时,应该尝试不同的 binwidths,因为不同的 binwidths 可以揭示不同的模式。在下面的图中,binwidth 为20太窄,导致了太多的柱子,使得难以确定分布的形状。同样,binwidth 为2,000太高,导致所有数据都被分为了只有三个柱子,同样难以确定分布的形状。binwidth 为200提供了一个合理的平衡。

ggplot(penguins, aes(x = body_mass_g)) +
  geom_histogram(binwidth = 20)
Warning: Removed 2 rows containing non-finite outside the scale range
(`stat_bin()`).

ggplot(penguins, aes(x = body_mass_g)) +
  geom_histogram(binwidth = 2000)
Warning: Removed 2 rows containing non-finite outside the scale range
(`stat_bin()`).

一个用于显示数值变量分布的替代可视化方法是密度图。密度图是直方图的平滑版本,特别适用于来自于连续平滑分布的数据。我们不会详细介绍 geom_density() 如何估计密度(你可以在函数文档中了解更多),但让我们通过一个类比来解释密度曲线是如何绘制的。想象一下,你用木块制作了一个直方图。然后,想象一下你在上面放了一根煮熟的意大利面条。意大利面条垂下的形状可以被视为密度曲线的形状。它显示的细节比直方图要少,但可以更容易地快速了解分布的形状,特别是关于模式和偏斜度方面。

ggplot(penguins, aes(x = body_mass_g)) +
  geom_density()
Warning: Removed 2 rows containing non-finite outside the scale range
(`stat_density()`).

1.4.3 练习

  1. 创建一个图,其中将企鹅的物种分配给 y 轴。这个图和之前的图有什么不同?
  2. 以下两个图有什么不同?哪个坐标轴,color 还是 fill,更适合改变柱子的颜色?
# | eval: false
ggplot(penguins, aes(x = species)) + geom_bar(color = "red")

ggplot(penguins, aes(x = species)) + geom_bar(fill = "red")

  1. 在 geom_histogram() 中的 bins 参数是做什么用的?
  2. 在 diamonds 数据集中创建一个关于 carat 变量的直方图,这个数据集在加载 tidyverse 包时就可以使用。尝试不同的 binwidth。哪个 binwidth 揭示了最有趣的模式?

1.5 可视化关系

要可视化一个关系,我们至少需要将两个变量映射到图的坐标轴上。在接下来的章节中,你将学习用于可视化两个或更多变量之间关系的常用图表和创建它们的几何对象。

1.5.1 一个数值变量和一个分类变量

要可视化数值和分类变量之间的关系,我们可以使用并列箱线图。箱线图是一种对描述分布的位置(百分位数)的视觉简写。它还有助于识别潜在的异常值。如下图所示,每个箱线图包括:

  • 一个箱子,指示数据的中间一半的范围,这个距离称为四分位距(IQR),从分布的第25百分位数到第75百分位数的距离。在箱子的中间是一条显示分布中位数的线,即第50百分位数。这三条线让你了解分布的展开程度,以及分布是否对称于中位数或偏向一侧。

  • 显示距离箱子两端超过1.5倍 IQR 的观测值的视觉点。这些异常点很不寻常,所以被单独绘制出来。

  • 从箱子的每一端延伸出的线(或叫须),直到分布中最远的非异常点。

让我们使用 geom_boxplot() 来查看不同物种的体重分布:

ggplot(penguins, aes(x = species, y = body_mass_g)) +
  geom_boxplot()
Warning: Removed 2 rows containing non-finite outside the scale range
(`stat_boxplot()`).

或者,我们可以使用 geom_density() 创建密度图。

ggplot(penguins, aes(x = body_mass_g, color = species)) +
  geom_density(linewidth = 0.75)
Warning: Removed 2 rows containing non-finite outside the scale range
(`stat_density()`).

我们使用了linewidth参数自定义了线条的粗细,以使其在背景中更加突出。

此外,我们可以将物种映射到颜色和填充美学,并使用alpha美学为填充的密度曲线添加透明度。该美学取值介于0(完全透明)和1(完全不透明)之间。在下面的图中,它被设置为0.5。

ggplot(penguins, aes(x = body_mass_g, color = species, fill = species)) +
  geom_density(alpha = 0.5)
Warning: Removed 2 rows containing non-finite outside the scale range
(`stat_density()`).

注意我们在这里使用的术语:

  • 如果我们希望表示该美学的视觉属性基于该变量的值而变化,则将变量映射到美学。

  • 否则,我们设置美学的值。

1.5.2 两个分类变量

我们可以使用堆叠条形图来可视化两个分类变量之间的关系。例如,以下两个堆叠条形图都显示了岛屿和物种之间的关系,或者具体来说,可视化了每个岛屿中物种的分布。

第一个图显示了每个岛屿上企鹅物种的频率。频率图显示了每个岛屿上阿德利企鹅的数量相等。但我们无法很好地了解每个岛屿内的百分比平衡。

ggplot(penguins, aes(x = island, fill = species)) +
  geom_bar()

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

ggplot(penguins, aes(x = island, fill = species)) +
  geom_bar(position = "fill")

在创建这些条形图时,我们可以把分隔成条形的变量映射到 x 美学,而将在条形内部更改颜色的变量映射到 fill 美学。

1.5.3 两个数值变量

到目前为止,你已经学习了用于可视化两个数值变量之间关系的散点图(使用 geom_point() 创建)和平滑曲线(使用 geom_smooth() 创建)。散点图可能是最常用于可视化两个数值变量之间关系的图表。

ggplot(penguins, 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()`).

1.5.4 三个或更多变量

正如我们在第 1.2.4 节中所看到的,我们可以通过将它们映射到额外的美学来将更多变量合并到图表中。例如,在以下散点图中,点的颜色代表物种,点的形状代表岛屿。

ggplot(penguins, aes(x = flipper_length_mm, y = body_mass_g)) +
  geom_point(aes(color = species, shape = island))
Warning: Removed 2 rows containing missing values or values outside the scale range
(`geom_point()`).

然而,向图表添加太多的美学映射会使其变得混乱,难以理解。另一种方法,特别适用于分类变量,是将图表分成不同的面板,即显示数据子集的子图。

要将图表分面成单个变量,请使用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)
Warning: Removed 2 rows containing missing values or values outside the scale range
(`geom_point()`).

1.5.5 练习

  1. mpg 数据框捆绑在 ggplot2 软件包中,包含美国环境保护局对 38 种汽车型号进行的 234 次观测。mpg中的哪些变量是分类变量?哪些变量是数值变量?(提示:键入 ?mpg 以阅读数据集的文档。)当你运行 mpg 时,你如何查看这些信息?

  2. 使用 mpg 数据框制作 hwy vs. displ 的散点图。接下来,将第三个数值变量映射到颜色,然后大小,然后颜色和大小,然后形状。这些美学在分类变量和数值变量方面的行为有何不同?

  3. 在 hwy vs. displ 的散点图中,如果将第三个变量映射到linewidth会发生什么?

  4. 如果将同一变量映射到多个美学,会发生什么?

  5. 制作 bill_depth_mm vs. bill_length_mm 的散点图,并将点的颜色按物种着色。通过按物种分色,这种着色揭示了这两个变量之间的关系?再按物种分面会发生什么?

  6. 为什么以下代码会产生两个单独的图例?如何修复它以合并这两个图例?

ggplot(
  data = penguins,
  mapping = aes(
    x = bill_length_mm, y = bill_depth_mm, 
    color = species, shape = species
  )
) +
  geom_point() +
  labs(color = "Species")
  1. 创建以下两个堆叠条形图。你可以用第一个图形回答什么问题?你可以用第二个图形回答什么问题?
ggplot(penguins, aes(x = island, fill = species)) +
  geom_bar(position = "fill")
ggplot(penguins, aes(x = species, fill = island)) +
  geom_bar(position = "fill")

1.6 保存你的图像

一旦你制作了图形,你可能希望通过将其保存为图像来将其从R中移出,以便你可以在其他地方使用。这就是ggsave()的工作,它将最近创建的图形保存到磁盘上:

ggplot(penguins, 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()`).

ggsave(filename = "penguin-plot.png")
Saving 7 x 5 in image
Warning: Removed 2 rows containing missing values or values outside the scale range
(`geom_point()`).

这将你的图形保存到你的工作目录中,关于工作目录,我们会在后续章节介绍。

如果你不指定宽度和高度,它们将从当前绘图设备的尺寸中获取。对于可重复使用的代码,你需要指定它们。你可以在文档中了解有关 ggsave() 的更多信息。

然而,总的来说,我们建议你使用 Quarto 来组织你的最终报告,Quarto 是一种可重现的撰写系统,允许你在你的代码和你的散文之间交织,并在你的写作中自动包含你的图形。你将在后续章节中学到更多关于 Quarto 的内容。