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

写在前面

本系列推文为《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/


目录

  • 12.1 介绍

  • 12.2 比较

  • 12.3 布尔代数

转换

本书的第二部分是深入研究数据可视化。在本书的这一部分中,您将了解您在数据框架内遇到的最重要类型的变量类型,并学习可以使用的工具与它们使用。

Figure 1: 数据转换的选项在很大程度上取决于所涉及的数据类型,这是本书的这一部分的主题。

您可以根据需要阅读这些章节;它们的设计基本上是独一无二的,以便可以逐步阅读它们。

  • 12 Logical vectors 教您逻辑向量。 这些是最简单的矢量类型,但非常强大。 您将学习如何通过数字比较,如何将它们与布尔代数相结合,如何在摘要中使用它们以及如何将它们用于条件转换。

  • 13 Numbers 深入探讨数值向量的处理工具,这些工具是数据科学的动力源泉。 你将进一步学习计数方法,以及一系列重要的转换与汇总函数。

  • 14 Strings 将赋予你处理文本的工具:拆分字符串、精细切割、再重新拼接。 本章重点介绍 stringr 包,同时也会学习更多 tidyr 包中专用于字符串数据提取的函数。

  • 15 Regular expressions 将带你认识这个强大的字符串处理工具。 从面对混乱字符时的茫然,到能读写复杂字符串模式,本章将完成你的蜕变。

  • 16 Factors 介绍因子: R 语言存储分类数据的数据类型。 当变量具有固定取值范围时,或需要非字母顺序的字符串排列时,因子便是你的利器。

  • 17 Dates and times 提供处理日期和时间的关键工具。 虽然日期时间越学越显复杂,但借助 lubridate 包,你将掌握应对常见挑战的方法。

  • 18 Missing values 深度探讨缺失值问题。 虽然之前零散提及过,但现在是系统讨论的时候了,帮助你理解显式与隐式缺失值的区别,以及转换方式和意义。

  • 19 Joins 作为本部分的收官之作,将教你合并多个数据框的工具。 学习连接操作会让你深入理解键的概念,并思考如何唯一标识数据集中的每一行。

12.1 介绍

在本章中,您将学习与逻辑向量(logical vectors)合作的工具。逻辑向量是最简单的向量类型,因为每个元素只能是三个可能的值之一:TRUE, FALSE, NA。在原始数据中找到逻辑向量相对较少,但是在几乎所有分析过程中,您都会创建并操纵它们。

我们将首先讨论创建逻辑向量的最常见方法:使用数字比较。然后,您将了解如何使用布尔代数组合不同的逻辑向量以及一些有用的摘要。我们将使用 if_else()case_when() 结束,这是两个有用的函数,用于进行由逻辑向量提供动力的有条件更改。

12.1.1 先决条件

您将在本章中学习的大多数功能由 base R 提供,因此我们不需要 tidyverse,但是我们仍然会加载它,因此我们可以使用 mutate()filter(),等来处理数据框架。 我们还将继续从 nycflights13::flights 数据集中绘制示例。

复制代码
library(tidyverse)
library(nycflights13)

但是,随着我们开始涵盖更多工具,并不总是有一个完美的真实示例。 因此,我们将开始使用 c() 来构成一些虚拟数据:

复制代码
x <- c(1, 2, 3, 5, 7, 11, 13)
x * 2
#> [1]  2  4  6 10 14 22 26

这使解释单个函数更容易,但代价是更难看出它如何应用于你的数据问题。 请记住,我们对自由向量的任何操作,都可以通过 mutate() 等函数对数据框中的变量进行操作。

复制代码
df <- tibble(x)
df |> 
  mutate(y = x *  2)
#> # A tibble: 7 × 2
#>       x     y
#>   <dbl> <dbl>
#> 1     1     2
#> 2     2     4
#> 3     3     6
#> 4     5    10
#> 5     7    14
#> 6    11    22
#> # ℹ 1 more row

12.2 比较

创建逻辑向量的一种非常常见的方法是通过数字比较 <, <=, >, >=, !=, ==。 到目前为止,我们主要在 filter() 中瞬时创建了逻辑变量 --- 它们被计算,使用,然后丢弃。 例如,下面的 filter 找出所有白天出发且基本准点到达的航班:

复制代码
flights |> 
  filter(dep_time > 600 & dep_time < 2000 & abs(arr_delay) < 20)
#> # A tibble: 172,286 × 19
#>    year month   day dep_time sched_dep_time dep_delay arr_time sched_arr_time
#>   <int> <int> <int>    <int>          <int>     <dbl>    <int>          <int>
#> 1  2013     1     1      601            600         1      844            850
#> 2  2013     1     1      602            610        -8      812            820
#> 3  2013     1     1      602            605        -3      821            805
#> 4  2013     1     1      606            610        -4      858            910
#> 5  2013     1     1      606            610        -4      837            845
#> 6  2013     1     1      607            607         0      858            915
#> # ℹ 172,280 more rows
#> # ℹ 11 more variables: arr_delay <dbl>, carrier <chr>, flight <int>, ...

知道这是一个快捷方式很有用,你也可以用 mutate() 显式创建底层的逻辑变量:

复制代码
flights |> 
  mutate(
    daytime = dep_time > 600 & dep_time < 2000,
    approx_ontime = abs(arr_delay) < 20,
    .keep = "used"
  )
#> # A tibble: 336,776 × 4
#>   dep_time arr_delay daytime approx_ontime
#>      <int>     <dbl> <lgl>   <lgl>        
#> 1      517        11 FALSE   TRUE         
#> 2      533        20 FALSE   FALSE        
#> 3      542        33 FALSE   FALSE        
#> 4      544       -18 FALSE   TRUE         
#> 5      554       -25 FALSE   FALSE        
#> 6      554        12 FALSE   TRUE         
#> # ℹ 336,770 more rows

这在处理更复杂的逻辑时尤为有用,因为命名中间步骤能让代码更易读,也便于检查每一步的计算是否正确。

总的来说,最初的筛选条件等价于:

复制代码
flights |> 
  mutate(
    daytime = dep_time > 600 & dep_time < 2000,
    approx_ontime = abs(arr_delay) < 20,
  ) |> 
  filter(daytime & approx_ontime)

12.2.1 浮点比较

当心使用 == 与数字。 例如,看起来这个向量包含数字 1 和 2:

复制代码
x <- c(1 / 49 * 49, sqrt(2) ^ 2)
x
#> [1] 1 2

但是,如果您测试它们是否相等,则会得到 FALSE

复制代码
x == c(1, 2)
#> [1] FALSE FALSE

怎么回事? 计算机以固定小数位数存储数字,因此无法精确表示 1/49sqrt(2),后续计算会出现极微小的误差。 我们可以查看实际存储的精确值,通过调用 print() 并设置 digits 参数:

复制代码
print(x, digits = 16)
#> [1] 0.9999999999999999 2.0000000000000004

你就能明白为什么 R 默认会舍入这些数字;它们确实非常接近你的预期值。

既然知道了 == 失败的原因,该如何解决呢? 一种方法是使用 dplyr::near(),它会忽略微小的差异:

复制代码
near(x, c(1, 2))
#> [1] TRUE TRUE

12.2.2 缺失值

缺少值代表未知数,因此它们具有"传染性":几乎所有涉及未知值的操作也将是未知的:

复制代码
NA > 5
#> [1] NA
10 == NA
#> [1] NA

最令人困惑的结果是:

复制代码
NA == NA
#> [1] NA

若我们稍加补充背景信息,就更容易理解为何如此:

复制代码
# We don't know how old Mary is
age_mary <- NA

# We don't know how old John is
age_john <- NA

# Are Mary and John the same age?
age_mary == age_john
#> [1] NA
# We don't know!

因此若想筛选所有 dep_time 为缺失值的航班,以下代码无法奏效,因为 dep_time == NA 会在每一行都返回 NA,而 filter() 会自动剔除缺失值:

复制代码
flights |> 
  filter(dep_time == NA)
#> # A tibble: 0 × 19
#> # ℹ 19 variables: year <int>, month <int>, day <int>, dep_time <int>,
#> #   sched_dep_time <int>, dep_delay <dbl>, arr_time <int>, ...

相反,我们需要一个新工具:is.na()

12.2.3 is.na()

is.na(x) 适用于任何类型的向量,对缺失值返回 TRUE,其他情况返回 FALSE

复制代码
is.na(c(TRUE, NA, FALSE))
#> [1] FALSE  TRUE FALSE
is.na(c(1, NA, 3))
#> [1] FALSE  TRUE FALSE
is.na(c("a", NA, "b"))
#> [1] FALSE  TRUE FALSE

我们可以用 is.na() 查找所有 dep_time 缺失的行:

复制代码
flights |> 
  filter(is.na(dep_time))
#> # A tibble: 8,255 × 19
#>    year month   day dep_time sched_dep_time dep_delay arr_time sched_arr_time
#>   <int> <int> <int>    <int>          <int>     <dbl>    <int>          <int>
#> 1  2013     1     1       NA           1630        NA       NA           1815
#> 2  2013     1     1       NA           1935        NA       NA           2240
#> 3  2013     1     1       NA           1500        NA       NA           1825
#> 4  2013     1     1       NA            600        NA       NA            901
#> 5  2013     1     2       NA           1540        NA       NA           1747
#> 6  2013     1     2       NA           1620        NA       NA           1746
#> # ℹ 8,249 more rows
#> # ℹ 11 more variables: arr_delay <dbl>, carrier <chr>, flight <int>, ...

is.na()arrange() 中也很有用。arrange() 通常将缺失值排在末尾,但通过先按 is.na() 排序可覆盖这一默认行为:

复制代码
flights |> 
  filter(month == 1, day == 1) |> 
  arrange(dep_time)
#> # A tibble: 842 × 19
#>    year month   day dep_time sched_dep_time dep_delay arr_time sched_arr_time
#>   <int> <int> <int>    <int>          <int>     <dbl>    <int>          <int>
#> 1  2013     1     1      517            515         2      830            819
#> 2  2013     1     1      533            529         4      850            830
#> 3  2013     1     1      542            540         2      923            850
#> 4  2013     1     1      544            545        -1     1004           1022
#> 5  2013     1     1      554            600        -6      812            837
#> 6  2013     1     1      554            558        -4      740            728
#> # ℹ 836 more rows
#> # ℹ 11 more variables: arr_delay <dbl>, carrier <chr>, flight <int>, ...

flights |> 
  filter(month == 1, day == 1) |> 
  arrange(desc(is.na(dep_time)), dep_time)
#> # A tibble: 842 × 19
#>    year month   day dep_time sched_dep_time dep_delay arr_time sched_arr_time
#>   <int> <int> <int>    <int>          <int>     <dbl>    <int>          <int>
#> 1  2013     1     1       NA           1630        NA       NA           1815
#> 2  2013     1     1       NA           1935        NA       NA           2240
#> 3  2013     1     1       NA           1500        NA       NA           1825
#> 4  2013     1     1       NA            600        NA       NA            901
#> 5  2013     1     1      517            515         2      830            819
#> 6  2013     1     1      533            529         4      850            830
#> # ℹ 836 more rows
#> # ℹ 11 more variables: arr_delay <dbl>, carrier <chr>, flight <int>, ...

我们将在 Chapter 18 中回来更深入地探讨缺失值。

12.2.4 练习

  1. dplyr::near() 是如何工作的?输入 near 查看源代码。sqrt(2)^2 接近 2 吗?

  2. 结合使用mutate()is.na()count() 来描述 dep_timesched_dep_timedep_delay 中缺失值的关联情况。

12.3 布尔代数

当存在多个逻辑向量时,可以使用布尔代数进行组合运算。 在 R 中,& 表示 "and",| 表示 "or",! 表示 "not",xor() 表示 "exclusive or"。 例如,df |> filter(!is.na(x)) 会筛选出 x 值非缺失的所有行,而 df |> filter(x < -10 | x > 0) 会筛选出 x 值小于 -10 或大于 0 的所有行。Figure 12.1 完整展示了布尔运算的规则及其运作原理。

Figure 12.1: 完整的布尔运算集合。x 代表左侧圆圈,y 代表右侧圆圈,阴影区域显示各运算符选择的部分。

除了 &| 外,R 还有 &&||。 不要在 dplyr 函数中使用它们! 这些是短路运算符,只会返回单个 TRUEFALSE。 它们对编程很重要,但对数据科学并不适用。

12.3.1 缺失值

布尔代数中缺失值的规则解释起来有些棘手,因为乍看之下似乎不太一致:

复制代码
df <- tibble(x = c(TRUE, FALSE, NA))

df |> 
  mutate(
    and = x & NA,
    or = x | NA
  )
#> # A tibble: 3 × 3
#>   x     and   or   
#>   <lgl> <lgl> <lgl>
#> 1 TRUE  NA    TRUE 
#> 2 FALSE FALSE NA   
#> 3 NA    NA    NA

要理解其中的逻辑,可以思考 NA | TRUENATRUE)的情况。 逻辑向量中的缺失值表示该值可能是 TRUEFALSE。 由于 TRUE | TRUEFALSE | TRUE 的结果都是 TRUE,因为至少有一个为真。NA | TRUE 也必定是 TRUE,因为无论 NA 代表 TRUE 还是 FALSE,结果都为 TRUE。 然而,NA | FALSE 会返回 NA,因为我们无法确定 NATRUE 还是 FALSE。 类似的逻辑也适用于 NA & FALSE 的情况。

12.3.2 操作顺序

需要注意的是,运算顺序与英语表达习惯不同。 请看以下查找 11 月或 12 月起飞航班的代码:

复制代码
flights |> 
   filter(month == 11 | month == 12)

你可能会想按照英语表达习惯写成:"查找所有 11 月或 12 月起飞的航班":

复制代码
flights |> 
   filter(month == 11 | 12)
#> # A tibble: 336,776 × 19
#>    year month   day dep_time sched_dep_time dep_delay arr_time sched_arr_time
#>   <int> <int> <int>    <int>          <int>     <dbl>    <int>          <int>
#> 1  2013     1     1      517            515         2      830            819
#> 2  2013     1     1      533            529         4      850            830
#> 3  2013     1     1      542            540         2      923            850
#> 4  2013     1     1      544            545        -1     1004           1022
#> 5  2013     1     1      554            600        -6      812            837
#> 6  2013     1     1      554            558        -4      740            728
#> # ℹ 336,770 more rows
#> # ℹ 11 more variables: arr_delay <dbl>, carrier <chr>, flight <int>, ...

这段代码虽然不会报错,但似乎并未达到预期效果。 原因是什么? 在这里,R 会首先计算 month == 11 生成逻辑向量,我们称为 nov。 然后执行 nov | 12。 当数值与逻辑运算符结合时,除 0 外的所有数值都会被转换为 TRUE,因此实际等价于 nov | TRUE,这个表达式永远返回 TRUE,最终会导致所有行都被选中:

复制代码
flights |> 
  mutate(
    nov = month == 11,
    final = nov | 12,
    .keep = "used"
  )
#> # A tibble: 336,776 × 3
#>   month nov   final
#>   <int> <lgl> <lgl>
#> 1     1 FALSE TRUE 
#> 2     1 FALSE TRUE 
#> 3     1 FALSE TRUE 
#> 4     1 FALSE TRUE 
#> 5     1 FALSE TRUE 
#> 6     1 FALSE TRUE 
#> # ℹ 336,770 more rows

12.3.3 %in%

避免混淆 ==| 运算顺序的简单方法是使用%in%x %in% y 会生成一个与 x 等长的逻辑向量,当 x 中的值出现在 y 中时,对应位置返回 TRUE

复制代码
1:12 %in% c(1, 5, 11)
#>  [1]  TRUE FALSE FALSE FALSE  TRUE FALSE FALSE FALSE FALSE FALSE  TRUE FALSE
letters[1:10] %in% c("a", "e", "i", "o", "u")
#>  [1]  TRUE FALSE FALSE FALSE  TRUE FALSE FALSE FALSE  TRUE FALSE

因此要查找 11 月和 12 月的所有航班,我们可以这样写:

复制代码
flights |> 
  filter(month %in% c(11, 12))

需要注意的是,%in%NA 的处理规则与 == 不同,因为 NA %in% NA 会返回 TRUE

复制代码
c(1, 2, NA) == NA
#> [1] NA NA NA
c(1, 2, NA) %in% NA
#> [1] FALSE FALSE  TRUE

这可以成为一个实用的快捷方式:

复制代码
flights |> 
  filter(dep_time %in% c(NA, 0800))
#> # A tibble: 8,803 × 19
#>    year month   day dep_time sched_dep_time dep_delay arr_time sched_arr_time
#>   <int> <int> <int>    <int>          <int>     <dbl>    <int>          <int>
#> 1  2013     1     1      800            800         0     1022           1014
#> 2  2013     1     1      800            810       -10      949            955
#> 3  2013     1     1       NA           1630        NA       NA           1815
#> 4  2013     1     1       NA           1935        NA       NA           2240
#> 5  2013     1     1       NA           1500        NA       NA           1825
#> 6  2013     1     1       NA            600        NA       NA            901
#> # ℹ 8,797 more rows
#> # ℹ 11 more variables: arr_delay <dbl>, carrier <chr>, flight <int>, ...

12.3.4 练习

  1. 查找所有 arr_delay 缺失但 dep_delay 未缺失的航班。查找所有 arr_timesched_arr_time 均未缺失,但 arr_delay 缺失的航班。

  2. 有多少航班的 dep_time 缺失?这些行中还有哪些变量缺失?这些行可能代表什么情况?

  3. 假设 dep_time 缺失意味着航班被取消,查看每天取消航班的数量。是否存在某种规律?取消航班的比例与未取消航班的平均延误时间之间是否存在关联?

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

本期翻译贡献:

  • @TigerZ生信宝库

注:本文已开启快捷转载,欢迎大家转载,只需标明文章出处即可。

相关推荐
AI纪元故事会16 小时前
《目标检测全解析:从R-CNN到DETR,六大经典模型深度对比与实战指南》
人工智能·yolo·目标检测·r语言·cnn
YangYang9YangYan20 小时前
高职大数据技术专业学习与发展指南
大数据·人工智能·学习·数据分析
得物技术20 小时前
得物管理类目配置线上化:从业务痛点到技术实现
后端·算法·数据分析
形宙数字1 天前
【形宙数字】MANGOLD INTERACT 行为观察分析系统-行为观察统计分析-人类行为学研究-行为逻辑
信息可视化·数据分析·行为观察分析系统·行为观察统计分析·人类行为学研究·行为逻辑·形宙数字
fl1768312 天前
基于python的天气预报系统设计和可视化数据分析源码+报告
开发语言·python·数据分析
YangYang9YangYan2 天前
高职新能源汽车技术专业职业发展指南
大数据·人工智能·数据分析·汽车
CoovallyAIHub2 天前
首届AI交易大赛对决!中国模型包揽冠亚军,GPT-5亏损62%垫底
人工智能·google·数据分析
学掌门2 天前
用Python做数据分析之数据表清洗
数据挖掘·数据分析
人大博士的交易之路2 天前
今日行情明日机会——20251104
大数据·数据挖掘·数据分析·缠论·涨停回马枪·道琼斯结构