《R for Data Science (2e)》免费中文翻译 (第1章) --- Data visualization(1)

发布于:2025-07-20 ⋅ 阅读:(13) ⋅ 点赞:(0)

写在前面

本系列推文为《R for Data Science (2)》的中文翻译版本。所有内容都通过开源免费的方式上传至Github,欢迎大家参与贡献,详细信息见:
Books-zh-cn 项目介绍:
Books-zh-cn:开源免费的中文书籍社区
r4ds-zh-cn Github 地址:
https://github.com/Books-zh-cn/r4ds-zh-cn
r4ds-zh-cn 网站地址:
https://books-zh-cn.github.io/r4ds-zh-cn/


目录

  • Whole game

  • 1.1 介绍

  • 1.2 第一步

  • 1.3 ggplot2 调用

Whole game

在本书的这一部分,我们的目标是快速为您概述数据科学的主要工具:importing, tidying, transforming, visualizing data,如 Figure 1 所示。我们希望向您展示数据科学的”整个游戏”,只提供足够的主要要素,让您能够处理真实的、简单的数据集。本书的后续部分将更深入地涵盖每个主题,扩大您能够处理的数据科学挑战的范围。

Figure 1: 在本书的这一部分,您将学习如何导入(import)、整理(tidy)、转换(transform)、可视化(visualize)数据。

Figure 1: 在本书的这一部分,您将学习如何导入(import)、整理(tidy)、转换(transform)、可视化(visualize)数据。

四个章节着重介绍数据科学的工具:

  • 可视化(Visualization)是使用 R 编程的一个很好的起点,因为其收益非常明显:您可以创建优雅且信息丰富的图形,帮助您理解数据。在1 Data visualization中,您将深入学习可视化,了解 ggplot2 plot 的基本结构,以及将数据转化为图形的强大技术。

  • 仅仅进行可视化通常是不够的,因此在3 Data transformation,中,您将学习关键的动词(verbs),这些动词可以帮助您选择重要的变量、过滤关键观察结果、创建新变量和计算总结。

  • 5 Data tidying中,您将学习关于整洁数据(tidy data)的知识,这是一种一致的存储数据的方式,使转换、可视化和建模变得更加容易。您将学习其基本原则,以及如何将数据整理成整洁的形式。

  • 在您进行数据转换和可视化之前,首先需要将数据导入 R 中。在7 Data import中,您将学习如何将.csv文件导入 R 中的基础知识。

除了这些章节之外,还有四个章节着重介绍您的 R 工作流程。在 2 Workflow: basics, 4 Workflow: code style, 6 Workflow: scripts and projects 中,您将学习编写和组织 R 代码的良好工作流程实践。这些将为您的长远成功奠定基础,因为它们将为您在处理实际项目时保持组织提供工具。最后,8 Workflow: getting help 将教您如何获取帮助和持续学习。

1.1 介绍

"The simple graph has brought more information to the data analyst's mind than any other device.” — John Tukey

R 有几种用于制作图形的系统,但 ggplot2 是最优雅和最多功能的之一。ggplot2 实现了图形语法(grammar of graphics),这是一套一致的描述和构建图形的系统。通过 ggplot2,你可以更多更快地学习一个系统,并在许多地方应用。

本章将教您如何使用 ggplot2 可视化数据。我们将从创建一个简单的散点图(scatterplot)开始,并使用它来介绍美学映射(aesthetic mappings)和几何对象(geometric objects)– ggplot2 的基本构建块。然后,我们将向您展示如何可视化单个变量的分布以及可视化两个或多个变量之间的关系。最后,我们将介绍如何保存您的图形并提供故障排除提示。

1.1.1 先决条件

本章重点介绍 ggplot2,它是 tidyverse 中的核心包之一。要访问本章中使用的数据集、帮助页面和函数,请运行以下命令加载 tidyverse:

library(tidyverse)
#> ── 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.2     ✔ tibble    3.3.0
#> ✔ lubridate 1.9.4     ✔ tidyr     1.3.1
#> ✔ purrr     1.0.4     
#> ── 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 中的哪些函数与 base R(或其他已加载的包)中的函数存在冲突。

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

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

您只需要安装一个包一次,但每次开始新会话时都需要加载(load)它。

除了 tidyverse 之外,我们还将使用 palmerpenguins 包,其中包含了 penguins 数据集,该数据集包含了帕尔默群岛上三个岛屿上企鹅的身体测量数据。还有 ggthemes 包,它提供了一个适用于色盲的安全调色板。

library(palmerpenguins)
#> 
#> Attaching package: 'palmerpenguins'
#> The following objects are masked from 'package:datasets':
#> 
#>     penguins, penguins_raw
library(ggthemes)

1.2 第一步

企鹅的翼展较长的是否比翼展较短的体重更重还是更轻?您可能已经有了答案,但请尽量给出精确的回答。翼展长度和体重之间的关系是怎样的?是正相关的吗?还是负相关的?是线性的吗?还是非线性的?这种关系是否因企鹅的物种而异?岛屿的差异是否会对关系产生影响?让我们创建可视化图表来回答这些问题。

1.2.1 penguins 数据框

您可以使用 palmerpenguins 包中的 penguins 数据框(data frame)来测试您对这些问题的回答(即 palmerpenguins::penguins)。一个 data frame 是一种由变量(列)和观测(行)组成的矩形集合。penguins 包含 344 个观测值,由 Dr. Kristen Gorman 和 Palmer Station, Antarctica LTER 搜集提供。

为了方便讨论,让我们定义一些术语:

  • variable 是可以进行测量的数量、特性或属性。

  • value 是在测量时 variable 所处的状态。variable 的 value 可能会在每次测量时发生变化。

  • observation 是在类似条件下进行的一组测量(通常在同一时间和同一对象上进行所有测量)。一个 observation 会包含多个 values,每个 value 与不同的 variable 相关联。我们有时将一个 observation 称为一个数据点。

  • Tabular data 是一组 values,每个 value 与一个 variable 和一个 observation 相关联。如果每个 value 都放置在自己的”单元格”中,每个 variable 都在自己的列中,每个 observation 都在自己的行中,那么 Tabular data 就是整洁(tidy)的。

在这个案例中,variable 指的是所有企鹅的属性,observation 指的是单个企鹅的所有属性。

在控制台中键入 data frame 的名称,R 将打印出其内容的预览。请注意,预览的顶部显示着 tibble。在 tidyverse 中,我们使用特殊的 data frames 称为 tibbles,您很快将学到更多关于它的知识。

penguins
#> # A tibble: 344 × 8
#>   species island    bill_length_mm bill_depth_mm flipper_length_mm
#>   <fct>   <fct>              <dbl>         <dbl>             <int>
#> 1 Adelie  Torgersen           39.1          18.7               181
#> 2 Adelie  Torgersen           39.5          17.4               186
#> 3 Adelie  Torgersen           40.3          18                 195
#> 4 Adelie  Torgersen           NA            NA                  NA
#> 5 Adelie  Torgersen           36.7          19.3               193
#> 6 Adelie  Torgersen           39.3          20.6               190
#> # ℹ 338 more rows
#> # ℹ 3 more variables: body_mass_g <int>, sex <fct>, year <int>

该 data frame 包含 8 列。如果想要以另一种视图查看所有变量和每个变量的前几个观察值,请使用 glimpse() 函数。或者,如果您在 RStudio 中运行,请使用 View(penguins) 打开一个交互式数据查看器。

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

penguins 中的变量包括:

  1. species: 一只企鹅的物种 (Adelie, Chinstrap, or Gentoo)。

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

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

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

1.2.2 最终目标

在本章中,我们的最终目标(ultimate goal)是重新创建以下可视化图表,显示企鹅的翼展长度(flipper lengths)和体重(body masses)之间的关系,并考虑企鹅的物种(species)差异。

1.2.3 创建一个 ggplot

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

在 ggplot2 中,你可以使用函数 ggplot() 开始一个绘图过程,定义一个绘图对象,然后向其添加图层(layers)。ggplot() 的第一个参数是要在图表中使用的数据集(dataset),因此 ggplot(data = penguins) 创建了一个空图表(empty graph),准备展示 penguins 数据集,但由于我们尚未告诉它如何进行可视化,所以目前它是空的。这并不是一个非常令人兴奋的图表,但你可以将其看作是一个空白的画布,你将在其上绘制剩下的图层。

ggplot(data = penguins)

接下来,我们需要告诉 ggplot() 如何将数据的信息进行可视化表示。ggplot() 函数的 mapping 参数定义了数据集中的变量如何映射到图表的视觉属性(aesthetics)。mapping 参数总是在 aes() 函数中定义,aes() 函数的 xy 参数指定要映射到 x 轴和 y 轴的变量。目前,我们仅将翼展长度(flipper length)映射到 x aesthetic,将体重(body mass)映射到 y aesthetic。ggplot2 会在 data 参数中寻找映射的变量,此处为 penguins 数据集。

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

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

我们的空白画布现在具有了更多的结构 – 可以清楚地看到翼展长度(flipper lengths)将显示在 x-axis 上,体重(body masses)将显示在 y-axis 上。但是企鹅的观测值还没有显示在图表上。这是因为我们在代码中尚未明确指定如何在图表上表示数据框中的观测值。

为了实现这一点,我们需要定义一个几何对象(geom):用于表示数据的图表中的几何对象。在 ggplot2 中,可以使用以 geom_ 开头的函数来获得这些几何对象。人们通常通过图表使用的几何对象类型来描述图表。例如,柱状图使用柱状几何对象(geom_bar()),折线图使用线条几何对象(geom_line()),箱线图使用箱线几何对象(geom_boxplot()),散点图使用点几何对象(geom_point()),等等。

函数 geom_point() 将一层点添加到您的图表中,从而创建一个散点图。ggplot2 提供了许多不同类型的几何函数,每个函数都可以向图表添加不同类型的图层(layer)。在本书中,您将学习到许多不同的几何函数,特别是在 Chapter 9 中。

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

现在我们有了一个看起来像是”散点图(scatterplot)”的图表。它还不完全符合我们的”最终目标”图表,但使用这个图表,我们可以开始回答我们探索的问题:“翼展长度(flipper length)和体重(body mass)之间的关系是什么样的?” 这个关系似乎是正向的(随着翼展长度(flipper length)的增加,体重(body mass)也增加),相当线性(点围绕在一条线附近而不是曲线上),并且中等强度(这条线附近没有太多的散点)。翼展较长的企鹅通常在体重上也较大。

在我们为这个图表添加更多图层之前,让我们暂停一下并回顾一下我们收到的警告信息(warning message):

Removed 2 rows containing missing values (geom_point()).

我们看到这个警告信息是因为我们的数据集中有两个企鹅的体重和/或翼展长度值缺失,而 ggplot2 没有办法在图表上表示它们,因为需要同时具备这两个值。与 R 一样,ggplot2 遵循这样的理念:缺失值永远不应该悄悄地丢失。这种警告通常是您在处理实际数据时最常见的警告之一 – 缺失值是一个非常常见的问题,在本书中您将在 Chapter 18 中学到更多相关知识。在本章的其余图表中,我们将禁止显示这个警告信息,以免在每个图表旁边都打印出来。

1.2.4 添加美学和图层

散点图对于显示两个数值变量之间的关系非常有用,但是对于两个变量之间的任何明显关系,保持怀疑态度并询问是否存在其他变量来解释或改变这种明显关系的性质总是一个好主意。例如,翼展长度(flipper length)和体重(body mass)之间的关系是否因物种(species)而异?让我们将物种(species)信息加入到我们的图表中,看看这是否揭示了这些变量之间明显关系的其他洞察。我们将使用不同颜色的点来表示不同的物种(species)。

为了实现这一点,我们需要修改 aesthetic 或 geom 部分吗?如果你猜到了”in the aesthetic mapping, inside of aes()“,那么你已经开始掌握使用 ggplot2 创建数据可视化的方法了!如果没有,不用担心。在本书中,你将制作更多的 ggplots,并有更多的机会在制作图表时检验你的直觉。

ggplot(
  data = penguins,
  mapping = aes(x = flipper_length_mm, y = body_mass_g, color = species)
) +
  geom_point()

当将一个分类变量映射到一个 aesthetic 时,ggplot2 会自动为每个唯一的变量水平(每个物种)分配一个唯一的 aesthetic 值(这里是唯一的 color),这个过程被称为缩放(scaling)。ggplot2 还会添加一个图例(legend),解释哪些值对应于哪些水平。

现在让我们添加一个额外的图层:一个显示体重(body mass)和翼展长度(flipper length)之间关系的平滑曲线。在继续之前,请回顾上面的代码,并思考如何将其添加到我们现有的图表中。

由于这是一个表示数据的新几何对象,我们将在我们的 point geom 之上添加一个新的几何层:geom_smooth()。我们将通过 method = "lm" 指定使用线性模型(linear model)来绘制最佳拟合线。

ggplot(
  data = penguins,
  mapping = aes(x = flipper_length_mm, y = body_mass_g, color = species)
) +
  geom_point() +
  geom_smooth(method = "lm")

我们成功地添加了线条,但是这个图形看起来与 Section 2.2.2 提供的图形不同,Section 2.2.2 的图形只有一条线表示整个数据集,而不是每个企鹅物种都有独立的线条。

当在 ggplot() 中定义 aesthetic mappings 时,在全局级别(global level)上,它们会传递给绘图的每个后续几何层(geom layers)。然而,ggplot2 中的每个几何函数也可以接受一个 mapping 参数,该参数允许在局部级别(local level)上进行 aesthetic mappings,并将其添加到从全局级别继承的映射中。由于我们希望点的颜色根据物种进行着色,但不希望将线条分开显示,所以我们应该仅对 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")

太棒了!我们已经接近我们的最终目标,尽管还不完美。我们仍然需要为每个企鹅物种使用不同的形状,并改进标签。

在绘图中仅使用颜色来表示信息通常不是一个好主意,因为由于色盲或其他色觉差异,人们对颜色的感知有所不同。因此,除了颜色之外,我们还可以将 species 映射到 shape aesthetic 上。

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

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

最后,我们可以使用 labs() 函数在新的图层中改进我们绘图的标签。labs() 的一些参数可能是不言自明的:title 添加标题,subtitle 添加副标题到绘图中。其他参数与美学映射相匹配,x 是 x 轴标签,y 是 y 轴标签,colorshape 定义图例的标签。此外,我们可以使用 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()

我们最终有了一个完全符合我们”最终目标”的图!

1.2.5 练习

  1. penguins 有多少行(rows)?有多少列(columns)?

  2. penguins 数据框中的 bill_depth_mm 变量描述了什么?请阅读 ?penguins 的帮助文档来获取答案。

  3. 创建一个 bill_depth_mm vs. bill_length_mm 的散点图。也就是说,在 y-axis 上绘制 bill_depth_mm,在 x-axis 上绘制 bill_length_mm。描述这两个变量之间的关系。

  4. 如果你绘制 species vs. bill_depth_mm 的散点图,会发生什么?选择什么样的几何图形可能更好?

  5. 为什么下面的代码会出错,如何修复它?

ggplot(data = penguins) + 
geom_point()
  1. geom_point() 中,na.rm 参数的作用是什么?这个参数的默认值是什么?创建一个散点图,并成功地将该参数设置为 TRUE

  2. 在前面绘制的图中添加以下说明:“Data come from the palmerpenguins package.” 提示:查看 labs() 的文档。

  3. 重新创建以下可视化图形。bill_depth_mm 应该映射到哪个美学属性?这个映射是应该在全局级别(global level)还是几何级别(geom level)上完成?

  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 代码的更简洁的表达。到目前为止,我们一直非常明确,这在学习过程中是很有帮助的:

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

通常,函数的前一个或两个参数非常重要,你应该熟记于心。ggplot() 函数的前两个参数是 datamapping,在本书的其余部分,我们不再提供这些参数的名称。这样做可以节省输入的工作量,并通过减少额外文本的数量,更容易看出绘图之间的区别。这是一个非常重要的编程问题,在 Chapter 25 中我们会再次涉及到这个问题。

对先前的绘图进行更简洁的重写可以得到:

ggplot(penguins, aes(x = flipper_length_mm, y = body_mass_g)) + 
  geom_point()

在将来,您还将学习使用管道操作符 |> 来创建该绘图,如下所示:

penguins |> 
  ggplot(aes(x = flipper_length_mm, y = body_mass_g)) + 
  geom_point()

--------------- 未完待续 ---------------

本期翻译贡献:

  • @TigerZ生信宝库


网站公告

今日签到

点亮在社区的每一天
去签到