R for data science笔记1.1 Data visualization(上)

1 介绍

“The simple graph has brought more information to the data analyst’s mind than any other device.” — John Tukey
“简单的图标比任何其他工具都能带来更多的信息。”——John Tukey

R有多种图形生成系统,但ggplot2是最优雅、最通用的系统之一。ggplot2实现了图形的语法,是一个用于描述和构建图形的连贯系统。你可以通过使用ggplot2在多个地方应用它来做提高生产力。

本章将教你如何使用ggplot2对数据进行可视化。我们将从创建一个简单的散点图开始,并使用它来引入美学映射和几何对象(aesthetic mappings and geometric objects )——这是ggplot2的基本构建模块。然后,我们将介绍单变量分布的可视化,以及两个或多个变量之间的关系的可视化。最后,我们将保存您的绘图和给一些故障排除技巧。

1.1 事前准备

ggplot2是tidyverse的一个核心包,首先,加载tidyver包。

1
2
3
4
5
6
7
8
9
10
11
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.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中的基本函数或你可能加载的其他包中的函数冲突。

如果你运行这段代码并得到错误消息,没有名为tidyverse的包,你需要首先安装它,然后再次运行library()

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

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

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

1
2
library(palmerpenguins)
library(ggthemes)

2 第一步

脚蹼较长的企鹅比脚蹼较短的企鹅重还是轻?脚蹼长度和身体质量之间的关系是什么?它是正相关的?负相关的?线性吗?非线性吗?这种关系是否因企鹅的种类而异?企鹅住的那个岛旁边的那个岛呢?让我们创建可视化来回答这些问题。

2.1 penguins 数据格式

 你可以使用在palmerpenguins,又名palmerpenguins::penguins中找到的penguins数据框架来检验你对这些问题的答案。数据框架是变量(列)和观测值(行)的矩形集合。企鹅包含了由克里斯汀·戈尔曼博士和南极帕尔默站收集和提供的344份观察报告。

  • 变量variable是可以测量的数量、质量或属性。
  • 值value是测量变量时的变量的状态。变量的值可能在不同测量中改变。
  • 一次观测observation是在类似条件下进行的一组测量,通常在一个观测中同时对同一个对象进行所有测量。一个观测值将包含多个值,每个值与一个不同的变量相关联。我们有时将观测值称为数据点
  • 表格数据Tabular data是一组值,每个值与一个变量和一次观测相关联。如果每个值都放在自己的“单元格”中,每个变量都放在自己的列中,每次观测都放在自己的行中,那么表格数据就是整洁的。

在控制台中输入数据框架的名称,R将打印其内容的预览。请注意,在这个预览的顶部显示的是tibble。在tidyverse中,我们使用名为tibbles的特殊数据框,您将很快了解更多有关它的信息。

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

1
2
3
4
5
6
7
8
9
10
11
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:企鹅的体重,单位是g。
  4. 等等。。。共8个

2.2 终极目标

考虑到企鹅的种类,本章的最终目标是重建如下的可视化图,展示这些企鹅的鳍肢长度和身体质量之间的关系。

image.png

2.3 创造一个ggplot

让我们慢慢来创建一个图。

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

1
ggplot(data = penguins)

接下来,我们需要告诉ggplot()如何以视觉方式表示数据中的信息。ggplot()函数的mapping参数定义了如何将数据集中的变量映射到绘图的视觉属性。mapping参数总是在aes()函数中定义,aes()xy参数指定要映射到x和y轴的变量。现在,我们只将脚蹼长度映射到x,身体质量映射到y。ggplot2在data参数中查找映射的变量,在本例中是penguins

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

1
2
3
4
ggplot(
data = penguins,
mapping = aes(x = flipper_length_mm, y = body_mass_g)
)

image.png

我们的空画布现在有了更多的结构——可以清楚地看到脚蹼长度将显示在哪里(在x轴上)以及身体质量将显示在哪里(在y轴上)。但企鹅本身还没有参与其中。这是因为在我们的代码中,我们还没有明确地说明如何在绘图中表示来自数据框的观察结果。

为此,我们需要定义一个几何对象(geom):它可以用图形来展现数据。这些几何对象在ggplot2中以geom_开头的函数提供。人们经常用图形所使用的几何类型来描述图形。例如,条形图使用条形几何geom_bar(),折线图使用直线几何geom_line(),箱线图使用boxplot几何geom_boxplot(),散点图使用点几何geom_point(),等等。

函数geom_point()在图形中添加了一哥图层的点,这自然就创建了一个散点图。ggplot2带有许多几何函数,每个函数都为图形添加不同类型的图层。本书会介绍很多这样的几何函数。

1
2
3
4
5
6
7
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()`).

image.png

现在我们有了一些看起来像散点图的东西。它还不符合我们的最终目标,但通过这个图,我们可以回答开始的问题:“脚蹼长度和体重之间的关系是什么?”它们之间的关系似乎是正相关的,而且是相当线性的,以及中等强度的关系,在这条直线上没有太多散点。结论:鳍肢较长的企鹅通常体型较大。

在我们向图中添加更多图层之前,让我们暂停一下,回顾一下我们得到的警告消息:

Removed 2 rows containing missing values geom_point()

我们看到这个消息是因为在我们的数据集中有两个企鹅的身体质量和/或鳍肢长度值缺失,ggplot2无法绘制。和R一样,ggplot2遵循的理念是,缺失的值永远不应该悄无声息地丢失。在处理真实数据时,这种类型的警告可能是最常见的一种——缺失值是一个非常常见的问题,但在本章余下的绘图中,我们将取消这个警告,以免它出现在我们绘制的每个绘图旁边。

2.4 添加美观元素和图层

散点图在显示两个定量变量之间的关系时很有用,但最好对这两个变量之间的任何明显关系保持怀疑,并询问是否有其他变量可以解释或改变这种明显关系的性质。例如,脚蹼长度和身体质量之间的关系是否因物种而异?让我们将物种纳入我们的图中,看看这是否揭示了这些变量之间的明显关系的其他见解。我们用不同颜色的点来表示物种。

为了实现这一点,我们需要修改aesthetic 还是 geom?如果你猜到了,“在aes()中更改,即在aesthetic中,你已经掌握了使用ggplot2创建数据可视化的窍门!

1
2
3
4
5
ggplot(
data = penguins,
mapping = aes(x = flipper_length_mm, y = body_mass_g, color = species)
) +
geom_point()

image.png

当一个分类变量被映射到aesthetic时,ggplot2将自动为变量的每个(即三个物种中的每一个)分配一个唯一的aesthetic值(这里是唯一的颜色),这个过程称为scaling(缩放)。ggplot2还会添加一个图例来说明哪个值对应哪个水平。

现在让我们再添加一个图层:一条显示身体质量和脚蹼长度之间关系的平滑曲线。在你继续之前,回头看看上面的代码,并考虑一下我们如何将其添加到我们现有的绘图中。

由于这是一个表示我们数据的新几何对象,我们将在我们的点几何上添加一个新的几何层:geom_smooth()。我们将指定基于method = "lm"的线性模型绘制最佳拟合直线。

1
2
3
4
5
6
ggplot(
data = penguins,
mapping = aes(x = flipper_length_mm, y = body_mass_g, color = species)
) +
geom_point() +
geom_smooth(method = "lm")

image.png

我们成功地添加了线条,但这个图形看起来不太对,它应该只有一条线表示整个数据集,而不是现在这样,每个企鹅物种都有单独的线。

当aesthetic映射在ggplot()中定义时,在全局级别上,它们被传递到绘图的每个后续几何层。然而,ggplot2中的每个几何函数也可以接受一个映射参数,这允许在局部层次上的aesthetic映射添加到那些从全局层次继承的映射上。因为我们希望点的颜色是基于物种的,但又不希望线条将它们分开,所以我们应该只为geom_point()指定color = species

1
2
3
4
5
6
ggplot(
data = penguins,
mapping = aes(x = flipper_length_mm, y = body_mass_g)
) +
geom_point(mapping = aes(color = species)) +
geom_smooth(method = "lm")

image.png

现在我们仍然需要为每种企鹅使用不同的形状、改进标签。

在图中仅使用颜色来表示信息通常不是一个好主意,因为由于色盲或其他色觉差异,人们对颜色的感知不同。因此,除了色彩,我们还可以将物种映射到标签形状上。

1
2
3
4
5
6
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")

image.png

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
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()

image.png

2.5 练习

  1. penguins中有多少行?有多少列?
  2. bill_depth_mm变量描述的是什么?阅读penguins的帮助来找出答案。
  3. 绘制bill_depth_mmbill_length_mm的散点图。也就是说,绘制一个散点图,将bill_depth_mm设为y轴,将bill_length_mm设为x轴。描述这两个变量之间的关系。
  4. 如果绘制speciesbill_depth_mm的散点图会发生什么?什么是更好的geom选择?
  5. 为什么下面的代码会报错?如何修复?
1
2
ggplot(data = penguins) + 
geom_point()
  1. na.rm参数在geom_point()中做什么?它的默认值是什么?创建一个散点图,并成功地将此参数设置为TRUE。
  2. 在前一个练习中绘制的图形中添加如下说明:Data come from the palmerpenguins package。提示:请查看labs()的文档。
  3. 重现以下可视化效果。bill_depth_mm应该映射到什么?它应该在全局级别上映射还是在几何级别?

image.png

  1. 在你的脑海中运行这段代码,并预测输出将是什么样子。然后,在R中运行代码并检查你的预测。
1
2
3
4
5
6
ggplot(
data = penguins,
mapping = aes(x = flipper_length_mm, y = body_mass_g, color = island)
) +
geom_point() +
geom_smooth(se = FALSE)
  1. 这两幅图看起来会不同吗?为什么/为什么不?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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)
)