R 数据处理 —— dplyr

前言

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

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

在本节及后续几节,我们将介绍如何使用 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

3 arrange

arrange() 的工作原理与 filter() 类似,只是它不选择行,而是更改行的顺序

它需要传入一个数据框和一组列名(或更复杂的表达式)来排序。如果你提供了多个列名,每个额外的列将被用来打破前面列值的联系

> arrange(flights, year, month, day)
# 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>

使用 desc() 按降序重新排序

> arrange(flights, desc(dep_delay))
# 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     9      641            900      1301     1242           1530      1272 HA          51
 2  2013     6    15     1432           1935      1137     1607           2120      1127 MQ        3535
 3  2013     1    10     1121           1635      1126     1239           1810      1109 MQ        3695
 4  2013     9    20     1139           1845      1014     1457           2210      1007 AA         177
 5  2013     7    22      845           1600      1005     1044           1815       989 MQ        3075
 6  2013     4    10     1100           1900       960     1342           2211       931 DL        2391
 7  2013     3    17     2321            810       911      135           1020       915 DL        2119
 8  2013     6    27      959           1900       899     1236           2226       850 DL        2007
 9  2013     7    22     2257            759       898      121           1026       895 DL        2047
10  2013    12     5      756           1700       896     1058           2020       878 AA         172
# … 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>

缺失值总是排在最后

> df <- tibble(x = c(5, 2, NA))
> arrange(df, x)
# A tibble: 3 x 1
      x
  <dbl>
1     2
2     5
3    NA
> arrange(df, desc(x))
# A tibble: 3 x 1
      x
  <dbl>
1     5
2     2
3    NA
3.1 思考练习
  1. 如何使用 arrange() 将所有缺失的值排在开头位置?(提示:使用 is.na()
  2. 对航班进行排序,找出延误最多的航班。找到最早起飞的航班。
  3. 对航班进行排序,找出最快(速度最快)的航班。
  4. 哪个航班飞行最远?哪一个飞行最短?

4 select

获取包含数百上千个变量的数据并不少见。在这种情况下,第一个挑战往往是缩小你真正感兴趣的变量范围

select() 允许您使用基于变量名称的操作快速提取一个子集。

select() 对于 flights 数据不是很有用,因为只有 19 个变量,但是用法是一致的

# 按名称选择列
> select(flights, year, month, day)
# A tibble: 336,776 x 3
    year month   day
   <int> <int> <int>
 1  2013     1     1
 2  2013     1     1
 3  2013     1     1
 4  2013     1     1
 5  2013     1     1
 6  2013     1     1
 7  2013     1     1
 8  2013     1     1
 9  2013     1     1
10  2013     1     1
# … with 336,766 more rows
# 选取 year 和 day 之间的列
> select(flights, year:day)
# A tibble: 336,776 x 3
    year month   day
   <int> <int> <int>
 1  2013     1     1
 2  2013     1     1
 3  2013     1     1
 4  2013     1     1
 5  2013     1     1
 6  2013     1     1
 7  2013     1     1
 8  2013     1     1
 9  2013     1     1
10  2013     1     1
# … with 336,766 more rows
# 选取所有除了 year 到 day 的列
> select(flights, -(year:day))
# A tibble: 336,776 x 16
   dep_time sched_dep_time dep_delay arr_time sched_arr_time arr_delay carrier flight tailnum origin
      <int>          <int>     <dbl>    <int>          <int>     <dbl> <chr>    <int> <chr>   <chr> 
 1      517            515         2      830            819        11 UA        1545 N14228  EWR   
 2      533            529         4      850            830        20 UA        1714 N24211  LGA   
 3      542            540         2      923            850        33 AA        1141 N619AA  JFK   
 4      544            545        -1     1004           1022       -18 B6         725 N804JB  JFK   
 5      554            600        -6      812            837       -25 DL         461 N668DN  LGA   
 6      554            558        -4      740            728        12 UA        1696 N39463  EWR   
 7      555            600        -5      913            854        19 B6         507 N516JB  EWR   
 8      557            600        -3      709            723       -14 EV        5708 N829AS  LGA   
 9      557            600        -3      838            846        -8 B6          79 N593JB  JFK   
10      558            600        -2      753            745         8 AA         301 N3ALAA  LGA   
# … with 336,766 more rows, and 6 more variables: dest <chr>, air_time <dbl>, distance <dbl>,
#   hour <dbl>, minute <dbl>, time_hour <dttm>
> 

有许多辅助函数可以在 select() 中使用

  • starts_with("abc"): 匹配以 abc 开头的列名

  • ends_with("xyz"): 匹配 xyz 结尾的列名

  • contains("ijk"): 匹配包含 ijk 的列名

  • matches("(.)\\1"): 正则表达式匹配列名,它匹配任何包含重复字符的变量,记住需要对 \ 进行转义

  • num_range("x", 1:3): 匹配 x1, x2x3

select() 可以用来重命名变量,但很少用到它,因为它会删除所有没有显式提到的变量

可以使用 rename(),它是 select() 的一个变体,保存所有未显式提到的变量

> rename(flights, tail_num = tailnum)
# 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: tail_num <chr>, origin <chr>, dest <chr>,
#   air_time <dbl>, distance <dbl>, hour <dbl>, minute <dbl>, time_hour <dttm>

select()everything() 辅助函数结合使用。可以将一些变量移到数据框的开头或结尾

> select(flights, time_hour, air_time, everything())
# A tibble: 336,776 x 19
   time_hour           air_time  year month   day dep_time sched_dep_time dep_delay arr_time
   <dttm>                 <dbl> <int> <int> <int>    <int>          <int>     <dbl>    <int>
 1 2013-01-01 05:00:00      227  2013     1     1      517            515         2      830
 2 2013-01-01 05:00:00      227  2013     1     1      533            529         4      850
 3 2013-01-01 05:00:00      160  2013     1     1      542            540         2      923
 4 2013-01-01 05:00:00      183  2013     1     1      544            545        -1     1004
 5 2013-01-01 06:00:00      116  2013     1     1      554            600        -6      812
 6 2013-01-01 05:00:00      150  2013     1     1      554            558        -4      740
 7 2013-01-01 06:00:00      158  2013     1     1      555            600        -5      913
 8 2013-01-01 06:00:00       53  2013     1     1      557            600        -3      709
 9 2013-01-01 06:00:00      140  2013     1     1      557            600        -3      838
10 2013-01-01 06:00:00      138  2013     1     1      558            600        -2      753
# … with 336,766 more rows, and 10 more variables: sched_arr_time <int>, arr_delay <dbl>,
#   carrier <chr>, flight <int>, tailnum <chr>, origin <chr>, dest <chr>, distance <dbl>, hour <dbl>,
#   minute <dbl>
4.1 思考练习
  1. 想出尽可能多的方法从航班中选择 dep_timedep_delayarr_timearr_dela
  2. 如果在 select() 多次调用变量名会发生什么?
  3. any_of() 函数的作用是什么? 为什么和下面这个向量结合起来会有帮助呢?
vars <- c("year", "month", "day", "dep_delay", "arr_delay")
  1. 以下代码运行的结果会让您感到惊讶吗?select 如何处理默认情况?如何更改默认行为?
select(flights, contains("TIME"))

5 mutate

除了选择现有的列之外,添加与现有列相关的新列通常也很有用。这就是 mutate() 的作用

mutate() 总是在数据集的末尾添加新列,因此我们将首先创建一个更窄的数据集,以便可以看到新变量

> flights_sml <- select(flights, 
+                       year:day, 
+                       ends_with("delay"), 
+                       distance, 
+                       air_time
+ )
> mutate(flights_sml,
+        gain = dep_delay - arr_delay,
+        speed = distance / air_time * 60
+ )
# A tibble: 336,776 x 9
    year month   day dep_delay arr_delay distance air_time  gain speed
   <int> <int> <int>     <dbl>     <dbl>    <dbl>    <dbl> <dbl> <dbl>
 1  2013     1     1         2        11     1400      227    -9  370.
 2  2013     1     1         4        20     1416      227   -16  374.
 3  2013     1     1         2        33     1089      160   -31  408.
 4  2013     1     1        -1       -18     1576      183    17  517.
 5  2013     1     1        -6       -25      762      116    19  394.
 6  2013     1     1        -4        12      719      150   -16  288.
 7  2013     1     1        -5        19     1065      158   -24  404.
 8  2013     1     1        -3       -14      229       53    11  259.
 9  2013     1     1        -3        -8      944      140     5  405.
10  2013     1     1        -2         8      733      138   -10  319.
# … with 336,766 more rows

注意: 你可以在 mutate() 中引用你刚刚创建的列

> mutate(flights_sml,
+        gain = dep_delay - arr_delay,
+        hours = air_time / 60,
+        gain_per_hour = gain / hours
+ )
# A tibble: 336,776 x 10
    year month   day dep_delay arr_delay distance air_time  gain hours gain_per_hour
   <int> <int> <int>     <dbl>     <dbl>    <dbl>    <dbl> <dbl> <dbl>         <dbl>
 1  2013     1     1         2        11     1400      227    -9 3.78          -2.38
 2  2013     1     1         4        20     1416      227   -16 3.78          -4.23
 3  2013     1     1         2        33     1089      160   -31 2.67         -11.6 
 4  2013     1     1        -1       -18     1576      183    17 3.05           5.57
 5  2013     1     1        -6       -25      762      116    19 1.93           9.83
 6  2013     1     1        -4        12      719      150   -16 2.5           -6.4 
 7  2013     1     1        -5        19     1065      158   -24 2.63          -9.11
 8  2013     1     1        -3       -14      229       53    11 0.883         12.5 
 9  2013     1     1        -3        -8      944      140     5 2.33           2.14
10  2013     1     1        -2         8      733      138   -10 2.3           -4.35
# … with 336,766 more rows

如果你只想保留这些新变量,请使用 transmute()

> transmute(flights,
+           gain = dep_delay - arr_delay,
+           hours = air_time / 60,
+           gain_per_hour = gain / hours
+ )
# A tibble: 336,776 x 3
    gain hours gain_per_hour
   <dbl> <dbl>         <dbl>
 1    -9 3.78          -2.38
 2   -16 3.78          -4.23
 3   -31 2.67         -11.6 
 4    17 3.05           5.57
 5    19 1.93           9.83
 6   -16 2.5           -6.4 
 7   -24 2.63          -9.11
 8    11 0.883         12.5 
 9     5 2.33           2.14
10   -10 2.3           -4.35
# … with 336,766 more rows
5.1 思考练习
  1. 目前,dep_timesched_dep_time 便于查看,但是很难计算,因为它们并不是真正的连续数字(517 => 5:17)。将它们转换成更方便的分钟数

  2. 比较 air_timearr_time - dep_time,你期望看到什么结果?但是输出了什么结果?该怎么解决这个问题

  3. 比较 dep_time, sched_dep_timedep_delay,你觉得这三个数字会有什么联系?

  4. 使用排名函数找出延误次数最多的 10 个航班,你想怎样处理(min_rank()

  5. 1:3 + 1:10 返回什么?为什么?

  6. R 提供了什么三角函数?

感谢花花同学的上期参考答案:

http://note.youdao.com/s/NXHvKdvA

6 summarise

最后一个重要的动词 summarise,它将所有信息汇总为一行

> summarise(flights, delay = mean(dep_delay, na.rm = TRUE))
# A tibble: 1 x 1
  delay
  <dbl>
1  12.6

一般很少单独使用 summarise(),而是配合 group_by() 函数使用。这样就从对所有数据统计变成对每个分组进行统计,有助于直观了解组与组之间的差异。

当你在分组数据上使用 dplyr 的动词函数时,它们会自动应用的每个分组上。例如,我们将相同的代码应用于按日期分组的数据中,我们将获得每个日期的平均延迟。

> flights %>% group_by(year, month, day) 
    %>% summarise(delay=mean(dep_delay, na.rm = TRUE))
`summarise()` regrouping output by 'year', 'month' (override with `.groups` argument)
# A tibble: 365 x 4
# Groups:   year, month [12]
    year month   day delay
   <int> <int> <int> <dbl>
 1  2013     1     1 11.5 
 2  2013     1     2 13.9 
 3  2013     1     3 11.0 
 4  2013     1     4  8.95
 5  2013     1     5  5.73
 6  2013     1     6  7.15
 7  2013     1     7  5.42
 8  2013     1     8  2.55
 9  2013     1     9  2.28
10  2013     1    10  2.84
# … with 355 more rows

group_by()summarise() 结合使用是 dplyr 最常用的工具之一。

6.1 用管道连接多个操作

比如,我们想要了解每个地方的距离和平均延迟之间的关系

> by_dest <- group_by(flights, dest)
> delay <- summarise(by_dest,
+    count = n(), # 计算每个分组的大小
+    dist = mean(distance, na.rm = TRUE),
+    delay = mean(arr_delay, na.rm = TRUE)
+ )
`summarise()` ungrouping output (override with `.groups` argument)
> delay <- filter(delay, count > 20, dest != "HNL")
> 
> ggplot(data = delay, mapping = aes(x = dist, y = delay)) +
+   geom_point(aes(size = count), alpha = 1/3) +
+   geom_smooth(se = FALSE)
`geom_smooth()` using method = 'loess' and formula 'y ~ x'

image

我们分三步获取数据:

  1. 将航班根据目的地分组
  2. 计算距离、平均延误和航班数的汇总信息
  3. 过滤掉噪声点和檀香山机场,该机场距离下一个最近的机场几乎是其两倍。

编写这段代码是很让人沮丧的,因为你需要给中间结果指定变量,取名字又是一个纠结的问题。尽管你可能觉得取名字无关紧要,但是最好也是要见名知意的比较好。所以,这也会影响我们的开发效率(个人是很赞成这种说法的,哈哈)。

所以我们引入的管道操作符 %>%,让我们来修改一下上面的代码

> delays <- flights %>% 
+     group_by(dest) %>% 
+     summarise(
+         count = n(),
+         dist = mean(distance, na.rm = TRUE),
+         delay = mean(arr_delay, na.rm = TRUE)
+     ) %>% 
+     filter(count > 20, dest != "HNL")
`summarise()` ungrouping output (override with `.groups` argument)

这使我们的注意力集中在数据的转换上,而不是转换为什么东西,可以让代码变得更加容易阅读。

但是,你也看到了 ggplot2 并没有使用管道操作,因为 ggplot2 出现在管道操作之前,而它的下一代 ggvis 已经可以支持管道操作了,但是这个包还没完全成熟。

6.2 缺失值

你可能想知道我们上面使用的 na.rm 参数,如果我们不使用它会怎么样?

> flights %>% 
+     group_by(year, month, day) %>% 
+     summarise(mean = mean(dep_delay))
`summarise()` regrouping output by 'year', 'month' (override with `.groups` argument)
# A tibble: 365 x 4
# Groups:   year, month [12]
    year month   day  mean
   <int> <int> <int> <dbl>
 1  2013     1     1    NA
 2  2013     1     2    NA
 3  2013     1     3    NA
 4  2013     1     4    NA
 5  2013     1     5    NA
 6  2013     1     6    NA
 7  2013     1     7    NA
 8  2013     1     8    NA
 9  2013     1     9    NA
10  2013     1    10    NA
# … with 355 more rows

我们得到了一列缺失值,因为聚合函数通常遵循缺失值规则:

如果输入中包含任何缺失值,那么输出的结果也将是缺失值。幸运的是,所有聚合函数都有一个 na.rm 参数,在计算之前删除缺失值

> flights %>% 
+     group_by(year, month, day) %>% 
+     summarise(mean = mean(dep_delay, na.rm = TRUE))
`summarise()` regrouping output by 'year', 'month' (override with `.groups` argument)
# A tibble: 365 x 4
# Groups:   year, month [12]
    year month   day  mean
   <int> <int> <int> <dbl>
 1  2013     1     1 11.5 
 2  2013     1     2 13.9 
 3  2013     1     3 11.0 
 4  2013     1     4  8.95
 5  2013     1     5  5.73
 6  2013     1     6  7.15
 7  2013     1     7  5.42
 8  2013     1     8  2.55
 9  2013     1     9  2.28
10  2013     1    10  2.84
# … with 355 more rows

在这种情况下,缺失值表示取消的航班,我们也可以通过首先删除取消的航班来解决问题。

我们将保存这个数据集,以便在接下来的几个示例中重用它。

> not_cancelled <- flights %>% 
+   filter(!is.na(dep_delay), !is.na(arr_delay))
> 
> not_cancelled %>% 
+   group_by(year, month, day) %>% 
+   summarise(mean = mean(dep_delay))
`summarise()` regrouping output by 'year', 'month' (override with `.groups` argument)
# A tibble: 365 x 4
# Groups:   year, month [12]
    year month   day  mean
   <int> <int> <int> <dbl>
 1  2013     1     1 11.4 
 2  2013     1     2 13.7 
 3  2013     1     3 10.9 
 4  2013     1     4  8.97
 5  2013     1     5  5.73
 6  2013     1     6  7.15
 7  2013     1     7  5.42
 8  2013     1     8  2.56
 9  2013     1     9  2.30
10  2013     1    10  2.84
# … with 355 more rows
6.3 计数

无论何时进行任何聚合操作,都最好包含一个计数(n()),或计算非缺失值(sum(!is.na(x))),这样你就可以确认支持你的结论的数据基数。

例如,让我们看一下平均延迟最高的飞机(通过其尾号标识)

> delays <- not_cancelled %>% 
+     group_by(tailnum) %>% 
+     summarise(
+         delay = mean(arr_delay)
+     )
`summarise()` ungrouping output (override with `.groups` argument)
> #> `summarise()` ungrouping output (override with `.groups` argument)
> 
> ggplot(data = delays, mapping = aes(x = delay)) + 
+     geom_freqpoly(binwidth = 10)

image

从图上可以看到,有些飞机平均延误 5 小时(300 分钟)

实际上,这个事有些微妙。如果我们画一个航班数量与平均延误的散点图,我们可以得到更多的信息

delays <- not_cancelled %>% 
  group_by(tailnum) %>% 
  summarise(
    delay = mean(arr_delay, na.rm = TRUE),
    n = n()
  )
#> `summarise()` ungrouping output (override with `.groups` argument)

ggplot(data = delays, mapping = aes(x = n, y = delay)) + 
  geom_point(alpha = 1/10, color='blue')

image

这一点也不奇怪,当航班很少时,平均延误的变化要大得多.

这个图的形状非常有特点:你会发现随着样本大小的增加,变化会减小。

在观察这类图时,可以先筛选出观察次数最少的组,这样你可以在最小的组中看到更多的模式和更少的极端变化情况。

下面,我们将向你展示将 ggplot2 集成到 dplyr 流中的简便方式

> delays %>% 
+     filter(n > 25) %>% 
+     ggplot(mapping = aes(x = n, y = delay)) + 
+     geom_point(alpha = 1/10, color='green')

image.png

这种模式还有一种常见的变体。让我们看看棒球击球手平均表现与他们击球次数的关系

在这里,我使用 Lahman 软件包中的数据来计算每个棒球大联盟运动员的击球平均值(命中次数/尝试次数)

当我们将击球手的技术(以平均击球数 ba 来衡量)与击球机会(以击球 ab 来衡量)来绘图时,您会看到两种模式

  1. 如上所述,当我们获得更多的数据点时,组中的变化会减小
  2. 技能(ba)和击球机会(ab)之间存在正相关关系。显然,这是因为球队控制着谁可以上场,他们肯定会挑选自己最好的球员。
batting <- as_tibble(Lahman::Batting)

batters <- batting %>% 
  group_by(playerID) %>% 
  summarise(
    ba = sum(H, na.rm = TRUE) / sum(AB, na.rm = TRUE),
    ab = sum(AB, na.rm = TRUE)
  )

batters %>% 
  filter(ab > 100) %>% 
  ggplot(mapping = aes(x = ab, y = ba)) +
  geom_point(color='sienna') + 
  geom_smooth(se = FALSE)

image.png

6.4 汇总函数

尽管 meanscountssum 三个汇总函数已经满足大多数要求了,但是 R 还提供了许多汇总函数

  • 位置:
    • mean:均值
    • median:中位值
> not_cancelled %>% 
+   group_by(year, month, day) %>% 
+   summarise(
+     avg_delay1 = mean(arr_delay),
+     avg_delay2 = mean(arr_delay[arr_delay > 0]) # the average positive delay
+   )
`summarise()` regrouping output by 'year', 'month' (override with `.groups` argument)
# A tibble: 365 x 5
# Groups:   year, month [12]
    year month   day avg_delay1 avg_delay2
   <int> <int> <int>      <dbl>      <dbl>
 1  2013     1     1     12.7         32.5
 2  2013     1     2     12.7         32.0
 3  2013     1     3      5.73        27.7
 4  2013     1     4     -1.93        28.3
 5  2013     1     5     -1.53        22.6
 6  2013     1     6      4.24        24.4
 7  2013     1     7     -4.95        27.8
 8  2013     1     8     -3.23        20.8
 9  2013     1     9     -0.264       25.6
10  2013     1    10     -5.90        27.3
# … with 355 more rows
  • 散度:
    • sd(x):标准差
    • IQR(x):四分位范围
    • mad(x):绝对中位差
> not_cancelled %>% 
+     group_by(dest) %>% 
+     summarise(distance_sd = sd(distance)) %>% 
+     arrange(desc(distance_sd))
`summarise()` ungrouping output (override with `.groups` argument)
# A tibble: 104 x 2
   dest  distance_sd
   <chr>       <dbl>
 1 EGE         10.5 
 2 SAN         10.4 
 3 SFO         10.2 
 4 HNL         10.0 
 5 SEA          9.98
 6 LAS          9.91
 7 PDX          9.87
 8 PHX          9.86
 9 LAX          9.66
10 IND          9.46
# … with 94 more rows
  • 秩次:
    • min:最小值
    • max:最大值
    • quantile:分位数
> not_cancelled %>% 
+     group_by(year, month, day) %>% 
+     summarise(
+         first = min(dep_time),
+         last = max(dep_time)
+     )
`summarise()` regrouping output by 'year', 'month' (override with `.groups` argument)
# A tibble: 365 x 5
# Groups:   year, month [12]
    year month   day first  last
   <int> <int> <int> <int> <int>
 1  2013     1     1   517  2356
 2  2013     1     2    42  2354
 3  2013     1     3    32  2349
 4  2013     1     4    25  2358
 5  2013     1     5    14  2357
 6  2013     1     6    16  2355
 7  2013     1     7    49  2359
 8  2013     1     8   454  2351
 9  2013     1     9     2  2252
10  2013     1    10     3  2320
# … with 355 more rows
  • 位置:
    • first(x):第一个数
    • nth(x, 2):第二个数
    • last(x):最后一个数
> not_cancelled %>% 
+     group_by(year, month, day) %>% 
+     summarise(
+         first_dep = first(dep_time), 
+         last_dep = last(dep_time)
+     )
`summarise()` regrouping output by 'year', 'month' (override with `.groups` argument)
# A tibble: 365 x 5
# Groups:   year, month [12]
    year month   day first_dep last_dep
   <int> <int> <int>     <int>    <int>
 1  2013     1     1       517     2356
 2  2013     1     2        42     2354
 3  2013     1     3        32     2349
 4  2013     1     4        25     2358
 5  2013     1     5        14     2357
 6  2013     1     6        16     2355
 7  2013     1     7        49     2359
 8  2013     1     8       454     2351
 9  2013     1     9         2     2252
10  2013     1    10         3     2320
# … with 355 more rows
  • 计数:
    • n():返回当前组的大小
    • n_distinct(x):计算不同值的数量
    • sum(!is.na(x)):计算非缺失值的数量
> not_cancelled %>% 
+     group_by(dest) %>% 
+     summarise(carriers = n_distinct(carrier)) %>% 
+     arrange(desc(carriers))
`summarise()` ungrouping output (override with `.groups` argument)
# A tibble: 104 x 2
   dest  carriers
   <chr>    <int>
 1 ATL          7
 2 BOS          7
 3 CLT          7
 4 ORD          7
 5 TPA          7
 6 AUS          6
 7 DCA          6
 8 DTW          6
 9 IAD          6
10 MSP          6
# … with 94 more rows

简单统计到达的目的地的数目

> not_cancelled %>% 
+     count(dest)
# A tibble: 104 x 2
   dest      n
   <chr> <int>
 1 ABQ     254
 2 ACK     264
 3 ALB     418
 4 ANC       8
 5 ATL   16837
 6 AUS    2411
 7 AVL     261
 8 BDL     412
 9 BGR     358
10 BHM     269
# … with 94 more rows

通过添加权重系数,可以计算飞行的总英里数

> not_cancelled %>% 
+     count(tailnum, wt = distance)
# A tibble: 4,037 x 2
   tailnum      n
   <chr>    <dbl>
 1 D942DN    3418
 2 N0EGMQ  239143
 3 N10156  109664
 4 N102UW   25722
 5 N103US   24619
 6 N104UW   24616
 7 N10575  139903
 8 N105UW   23618
 9 N107US   21677
10 N108UW   32070
# … with 4,027 more rows
  • 逻辑值:
    当使用数值型的函数时,TRUE 会变成 1FALSE 变为 0。可以方便的与 summean 结合使用
> not_cancelled %>% 
+     group_by(year, month, day) %>% 
+     summarise(n_early = sum(dep_time < 500))
`summarise()` regrouping output by 'year', 'month' (override with `.groups` argument)
# A tibble: 365 x 4
# Groups:   year, month [12]
    year month   day n_early
   <int> <int> <int>   <int>
 1  2013     1     1       0
 2  2013     1     2       3
 3  2013     1     3       4
 4  2013     1     4       3
 5  2013     1     5       3
 6  2013     1     6       2
 7  2013     1     7       2
 8  2013     1     8       1
 9  2013     1     9       3
10  2013     1    10       3
# … with 355 more rows
# 计算航班延误超过 1 小时的比例
> not_cancelled %>% 
+     group_by(year, month, day) %>% 
+     summarise(hour_prop = mean(arr_delay > 60))
`summarise()` regrouping output by 'year', 'month' (override with `.groups` argument)
# A tibble: 365 x 4
# Groups:   year, month [12]
    year month   day hour_prop
   <int> <int> <int>     <dbl>
 1  2013     1     1    0.0722
 2  2013     1     2    0.0851
 3  2013     1     3    0.0567
 4  2013     1     4    0.0396
 5  2013     1     5    0.0349
 6  2013     1     6    0.0470
 7  2013     1     7    0.0333
 8  2013     1     8    0.0213
 9  2013     1     9    0.0202
10  2013     1    10    0.0183
# … with 355 more rows
6.5 多变量分组

通过对多变量分组,可以进行逐步汇总

> daily <- group_by(flights, year, month, day)
> (per_day   <- summarise(daily, flights = n()))
`summarise()` regrouping output by 'year', 'month' (override with `.groups` argument)
# A tibble: 365 x 4
# Groups:   year, month [12]
    year month   day flights
   <int> <int> <int>   <int>
 1  2013     1     1     842
 2  2013     1     2     943
 3  2013     1     3     914
 4  2013     1     4     915
 5  2013     1     5     720
 6  2013     1     6     832
 7  2013     1     7     933
 8  2013     1     8     899
 9  2013     1     9     902
10  2013     1    10     932
# … with 355 more rows

> (per_month <- summarise(per_day, flights = sum(flights)))
`summarise()` regrouping output by 'year' (override with `.groups` argument)
# A tibble: 12 x 3
# Groups:   year [1]
    year month flights
   <int> <int>   <int>
 1  2013     1   27004
 2  2013     2   24951
 3  2013     3   28834
 4  2013     4   28330
 5  2013     5   28796
 6  2013     6   28243
 7  2013     7   29425
 8  2013     8   29327
 9  2013     9   27574
10  2013    10   28889
11  2013    11   27268
12  2013    12   28135

> (per_year  <- summarise(per_month, flights = sum(flights)))
`summarise()` ungrouping output (override with `.groups` argument)
# A tibble: 1 x 2
   year flights
  <int>   <int>
1  2013  336776
6.6 取消分组

如果您想要删除分组并对未分组的数据操作,可以使用 ungroup

> daily %>% 
+     ungroup() %>%
+     summarise(flights = n())
# A tibble: 1 x 1
  flights
    <int>
1  336776
6.7 思考练习
  1. 至少用 5 种不同方式评估一组航班的延误情况。并考虑以下情形:
  • 航班有 50% 的概率提前 15 分钟,50% 的概率晚点 15 分钟
  • 航班总是晚点 10 分钟
  • 航班有 50% 的概率提前 30 分钟,50% 的概率晚点 30 分钟
  • 航班用 99% 的概率准点,迟到两小时的几率只有 1%

到达延迟和出发延迟,哪个更重要

  1. 想出另一种方法,得到与下面代码相同的结果(不使用 count()
not_cancelled %>% count(dest)
not_cancelled %>% count(tailnum, wt = distance)
  1. 我们对航班取消的定义(is.na(dep_delay) | is.na(arr_delay))并不是最好的,为什么?哪一列才是最重要的

  2. 看看每天取消的航班数量,是否有规律?取消航班的比例与平均延误是否有关?

  3. 哪家航空公司的延误最严重。挑战:你能分清坏机场和坏航空公司的影响吗?(提示:flights %>% group_by(carrier, dest) %>% summarise(n())

  4. countsort 参数的用处是什么,怎么使用?

7. group + mutate/filter

分组与 summarise() 结合使用是最有用的,但是你也可以与 mutate()filter() 结合使用

  • 找到每个组中最差的成员
> flights %>% 
+     group_by(year, month, day) %>%
+     filter(rank(desc(arr_delay)) < 10)
# A tibble: 3,306 x 19
# Groups:   year, month, day [365]
    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      848           1835       853     1001           1950       851 MQ        3944
 2  2013     1     1     1815           1325       290     2120           1542       338 EV        4417
 3  2013     1     1     1842           1422       260     1958           1535       263 EV        4633
 4  2013     1     1     1942           1705       157     2124           1830       174 MQ        4410
 5  2013     1     1     2006           1630       216     2230           1848       222 EV        4644
 6  2013     1     1     2115           1700       255     2330           1920       250 9E        3347
 7  2013     1     1     2205           1720       285       46           2040       246 AA        1999
 8  2013     1     1     2312           2000       192       21           2110       191 EV        4312
 9  2013     1     1     2343           1724       379      314           1938       456 EV        4321
10  2013     1     2     1244            900       224     1431           1104       207 EV        4412
# … with 3,296 more rows, and 8 more variables: tailnum <chr>, origin <chr>, dest <chr>,
#   air_time <dbl>, distance <dbl>, hour <dbl>, minute <dbl>, time_hour <dttm>
  • 查找所有大于阈值的组
> popular_dests <- flights %>% 
+     group_by(dest) %>% 
+     filter(n() > 365)
> popular_dests
# A tibble: 332,577 x 19
# Groups:   dest [77]
    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 332,567 more rows, and 8 more variables: tailnum <chr>, origin <chr>, dest <chr>,
#   air_time <dbl>, distance <dbl>, hour <dbl>, minute <dbl>, time_hour <dttm>
  • 标准化每组指标
> popular_dests %>% 
+     filter(arr_delay > 0) %>% 
+     mutate(prop_delay = arr_delay / sum(arr_delay)) %>% 
+     select(year:day, dest, arr_delay, prop_delay)
# A tibble: 131,106 x 6
# Groups:   dest [77]
    year month   day dest  arr_delay prop_delay
   <int> <int> <int> <chr>     <dbl>      <dbl>
 1  2013     1     1 IAH          11  0.000111 
 2  2013     1     1 IAH          20  0.000201 
 3  2013     1     1 MIA          33  0.000235 
 4  2013     1     1 ORD          12  0.0000424
 5  2013     1     1 FLL          19  0.0000938
 6  2013     1     1 ORD           8  0.0000283
 7  2013     1     1 LAX           7  0.0000344
 8  2013     1     1 DFW          31  0.000282 
 9  2013     1     1 ATL          12  0.0000400
10  2013     1     1 DTW          16  0.000116 
# … with 131,096 more rows
7.1 思考练习
  1. 哪架飞机(tailnum)的准点率最底?

  2. 如果要尽量避免延误,应该在一天中的什么时间乘坐飞机?

  3. 计算每个目的地的总延迟分钟数。计算每个航班其占目的地总延迟的比例。

  4. 使用 lag(),探索某一航班的延迟与其前一个航班的延迟相关性。

  5. 统计至少有两家航空公司飞过的目的地。利用目的地数量给航空公司排名。

感谢花花同学的上期参考答案:

http://note.youdao.com/s/NXHvKdvA

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

名本无名

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值