R 数据处理(八)

前言

通常,我们读取的数据不可能完全满足我们后续的分析需求,因此需要对读取进来的数据进行处理。

比如,创建一些新的变量或总结,或者只是想重命名变量或重新排列观察结果,以便使数据更易于使用。

在本节及后续几节,我们将介绍如何使用 tidyverse 中的 dplyr 包来对数据进行操作。

我们使用的数据是 2013 年从纽约出发的航班信息。

1 介绍

1.1 准备

在这里我们的重点是介绍 dplyr 包的使用,并使用的 nycflights13 包中的数据来说明 dplyr 的核心思想,同时会加入一些 ggplot2 展示数据的示例,帮助我们理解。

ggplot2 包的详细介绍将会放在后续的 R 绘图系列中。

# 安装 nycflights13 包
install.packages("nycflights13")
# 导入
library(nycflights13)
# 导入 tidyverse
> library(tidyverse)
─ Attaching packages ─────────────────────────────── tidyverse 1.3.0 ─
✓ ggplot2 3.3.3     ✓ purrr   0.3.4
✓ tibble  3.0.4     ✓ dplyr   1.0.2
✓ tidyr   1.1.2     ✓ stringr 1.4.0
✓ readr   1.4.0     ✓ forcats 0.5.0
─ Conflicts ───────────────────────────────── tidyverse_conflicts() ─
x dplyr::filter() masks stats::filter()
x dplyr::lag()    masks stats::lag()
# 或者只导入 dplyr
> library(dplyr)

请注意导入 tidyverse 时打印的冲突信息,它告诉你 dplyr 会覆盖基本 R 中的某些函数。

如果要在加载 dplyr 后使用这些函数的基本版本,则需要使用其全名 stats::filter()stats::lag()

1.2 nycflights13

为了探索 dplyr 的基本数据操作,我们将使用 nycflights13::flights

此数据包含 2013 年从纽约起飞的所有 336776 架次航班,这些数据来自美国交通统计局。

> flights
# A tibble: 336,776 x 19
    year month   day dep_time sched_dep_time dep_delay arr_time sched_arr_time arr_delay carrier flight
   <int> <int> <int>    <int>          <int>     <dbl>    <int>          <int>     <dbl> <chr>    <int>
 1  2013     1     1      517            515         2      830            819        11 UA        1545
 2  2013     1     1      533            529         4      850            830        20 UA        1714
 3  2013     1     1      542            540         2      923            850        33 AA        1141
 4  2013     1     1      544            545        -1     1004           1022       -18 B6         725
 5  2013     1     1      554            600        -6      812            837       -25 DL         461
 6  2013     1     1      554            558        -4      740            728        12 UA        1696
 7  2013     1     1      555            600        -5      913            854        19 B6         507
 8  2013     1     1      557            600        -3      709            723       -14 EV        5708
 9  2013     1     1      557            600        -3      838            846        -8 B6          79
10  2013     1     1      558            600        -2      753            745         8 AA         301
# … with 336,766 more rows, and 8 more variables: tailnum <chr>, origin <chr>, dest <chr>,
#   air_time <dbl>, distance <dbl>, hour <dbl>, minute <dbl>, time_hour <dttm>

从打印信息可以看出是一个 tibble 类型,而不是 data.frame 类型

每列的类型代表的含义是:

  • int: 代表整数

  • dbl: 代表 double 或者实数

  • chr: 代表字符向量或字符串

  • dttm: 代表日期-时间

还有另外三种没在这些数据中出现的类型

  • lgl: 代表逻辑值

  • fctr: 代表因子,即分类变量

  • date: 代表日期

1.3 dplyr 基础

在这里,我们将介绍五个关键的 dplyr 动词函数,这些函数可帮助您解决绝大多数的数据处理难题

  • filter(): 对值进行筛选
  • arrange(): 对行进行重排
  • select(): 根据变量名提取变量
  • mutate(): 使用现有变量创建新变量
  • summarise(): 将许多值汇总成一个摘要

这些函数都可以与 groupby() 一起使用,groupby() 将每个函数的作用域从对整个数据集进行操作改为对其分组进行操作

所有动词的工作方式相似:

  1. 第一个参数是数据框
  2. 后面的参数使用变量名描述如何处理数据框
  3. 返回的结果是一个新的数据框

将这些属性结合在一起,可以轻松地将多个简单步骤连接在一起,以获得复杂的结果。让我们深入看看这些动词是如何工作的

2 filter

filter() 允许您根据观察值对其进行筛选子集

第一个参数是数据框的名称。第二个和随后的参数是过滤数据框的表达式

例如,我们可以通过以下方式选择 11 日的所有航班

> filter(flights, month == 1, day == 1)
# A tibble: 842 x 19
    year month   day dep_time sched_dep_time dep_delay arr_time sched_arr_time arr_delay carrier flight
   <int> <int> <int>    <int>          <int>     <dbl>    <int>          <int>     <dbl> <chr>    <int>
 1  2013     1     1      517            515         2      830            819        11 UA        1545
 2  2013     1     1      533            529         4      850            830        20 UA        1714
 3  2013     1     1      542            540         2      923            850        33 AA        1141
 4  2013     1     1      544            545        -1     1004           1022       -18 B6         725
 5  2013     1     1      554            600        -6      812            837       -25 DL         461
 6  2013     1     1      554            558        -4      740            728        12 UA        1696
 7  2013     1     1      555            600        -5      913            854        19 B6         507
 8  2013     1     1      557            600        -3      709            723       -14 EV        5708
 9  2013     1     1      557            600        -3      838            846        -8 B6          79
10  2013     1     1      558            600        -2      753            745         8 AA         301
# … with 832 more rows, and 8 more variables: tailnum <chr>, origin <chr>, dest <chr>,
#   air_time <dbl>, distance <dbl>, hour <dbl>, minute <dbl>, time_hour <dttm>

当您运行上面这行代码时,dplyr 执行过滤操作并返回一个新的数据框。

dplyr 函数不会修改其输入,因此如果要保存结果,则需要使用赋值运算符

jan1 <- filter(flights, month == 1, day == 1)

R 要么打印出结果,要么将其保存到变量中。如果你想同时做这两件事,你可以用圆括号括起来

> (dec25 <- filter(flights, month == 12, day == 25))
# A tibble: 719 x 19
    year month   day dep_time sched_dep_time dep_delay arr_time sched_arr_time arr_delay carrier flight
   <int> <int> <int>    <int>          <int>     <dbl>    <int>          <int>     <dbl> <chr>    <int>
 1  2013    12    25      456            500        -4      649            651        -2 US        1895
 2  2013    12    25      524            515         9      805            814        -9 UA        1016
 3  2013    12    25      542            540         2      832            850       -18 AA        2243
 4  2013    12    25      546            550        -4     1022           1027        -5 B6         939
 5  2013    12    25      556            600        -4      730            745       -15 AA         301
 6  2013    12    25      557            600        -3      743            752        -9 DL         731
 7  2013    12    25      557            600        -3      818            831       -13 DL         904
 8  2013    12    25      559            600        -1      855            856        -1 B6         371
 9  2013    12    25      559            600        -1      849            855        -6 B6         605
10  2013    12    25      600            600         0      850            846         4 B6         583
# … with 709 more rows, and 8 more variables: tailnum <chr>, origin <chr>, dest <chr>,
#   air_time <dbl>, distance <dbl>, hour <dbl>, minute <dbl>, time_hour <dttm>

2.1 比较

要有效地使用过滤,您必须知道如何使用比较运算符来选择需要的值

>, >=, <, <=, != ,  ==

注意,上面的筛选参数用的是 == 而不是 =,如果你用错了

> filter(flights, month = 1)
错误: Problem with `filter()` input `..1`.
x Input `..1` is named.
ℹ This usually means that you've used `=` instead of `==`.
ℹ Did you mean `month == 1`?
Run `rlang::last_error()` to see where the error occurred.

就会产生错误。

使用 == 时可能会遇到的另一个常见问题是: 浮点数。

> sqrt(2) ^ 2 == 2
[1] FALSE
> 1 / 49 * 49 == 1
[1] FALSE

计算机使用有限精度算法(显然是不能存储无限多的数字)所以你看到的每个数字都是近似值

我们使用 near() 函数代替 ==

> near(sqrt(2) ^ 2,  2)
[1] TRUE
> near(1 / 49 * 49, 1)
[1] TRUE

2.2 逻辑运算

filter() 的多个参数会以逻辑与的方式连接,而对于其他类型的组合,您需要自己使用布尔操作符:& | !

我们用下面的代码查找所有在 11 月或 12 月起飞的航班

filter(flights, month == 11 | month == 12)

你不能写成

filter(flights, month == (11 | 12))

它会找到等于 11 | 12 的所有月份,这个表达式的结果是 TRUE

在一个数字上下文中(如这里),TRUE 会变成 1,因此它会查找 1 月份的所有航班,而不是 11 月或 12 月。这真是太让人困惑了.

解决这一问题的一个有用的简写就是

x %in% y

返回一个长度等于 x 的逻辑值向量,即 x 中的每一个元素是否出现在 y 中。

我们重写上面的代码

nov_dec <- filter(flights, month %in% c(11, 12))

有时你可以通过摩根定律来简化复杂的子集,如

!(x & y) => !x | !y
!(x | y) => !x & !y

例如,如果您想查找延迟(到达或离开时)不超过两小时的航班,可以使用下面两个过滤器中的任何一个

filter(flights, !(arr_delay > 120 | dep_delay > 120))
filter(flights, arr_delay <= 120, dep_delay <= 120)

R 中也有 &&||,但是不要在这里使用它们

2.3 缺失值

R 的一个重要特性是缺失值,也就是 NA

NA 表示一个未知值,几乎任何涉及未知值的操作也将是未知的

> NA > 5
[1] NA
> 10 == NA
[1] NA
> NA + 10
[1] NA
> NA / 2
[1] NA

最令人困惑的结果是这个

> NA == NA
[1] NA

是不是想不明白,看看下面的例子,也许可以帮助你理解这是为什么

# Let x be Mary's age. We don't know how old she is.
x <- NA

# Let y be John's age. We don't know how old he is.
y <- NA

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

可以使用 is.na() 判断一个值是不是缺失值

> is.na(NA)
[1] TRUE

filter() 仅包含条件为 TRUE 的行;它不包括 FALSENA 值。如果要保留缺失值,需要明确指定

> df <- tibble(x = c(1, NA, 3))
> filter(df, x > 1)
# A tibble: 1 x 1
      x
  <dbl>
1     3
> filter(df, is.na(x) | x > 1)
# A tibble: 2 x 1
      x
  <dbl>
1    NA
2     3

2.4 思考练习

  1. 提取下面所有的航班信息
  • 迟到两个或两个多小时
  • 飞往休斯敦(IAHHOU)
  • UnitedAmerican,或 Delta 经营的航班
  • 夏季出发(July, August, September)
  • 延迟到达超过两小时,但未延迟起飞
  • 至少晚点了一小时,其中飞行时间占了超过 30 分钟
  • 午夜至凌晨 6 点之间出发(0-6
  1. 另一个有用的 dplyr 过滤函数是 between(),它有什么作用?您可以使用它来简化解决前面问题的代码吗?

  2. 有多少航班缺失了 dep_time?同时缺少哪些其他变量?想想这些行代表什么?

  3. 为什么 NA ^ 0 不是缺失值?为什么 NA | TRUE 不是缺失值?为什么 FALSE & NA 不是缺失值?你能弄清楚一般规则吗?(NA * 0 是一个棘手的反例)

感谢花花同学的上期参考答案
http://note.youdao.com/s/DPASe3xt

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,179评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,229评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,032评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,533评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,531评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,539评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,916评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,813评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,568评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,654评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,354评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,937评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,918评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,152评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,852评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,378评论 2 342

推荐阅读更多精彩内容