【R语言】dplyr笔记

dplyr包是tidyverse中的核心R包。

本文用到的数据集

本文用到的是nycflights13::flights这个数据集,这个数据框包含了2013 年从纽约市出发的所有336776次航班的信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
flights
#> # A tibble: 336,776 × 19
#> year month day dep_time sched_dep_time dep_delay
#> <int> <int> <int> <int> <int> <dbl>
#> 1 2013 1 1 517 515 2
#> 2 2013 1 1 533 529 4
#> 3 2013 1 1 542 540 2
#> 4 2013 1 1 544 545 -1
#> 5 2013 1 1 554 600 -6
#> 6 2013 1 1 554 558 -4
#> # ... with 336,776 more rows, and 13 more variables:
#> # arr_time <int>, sched_arr_time <int>, arr_delay <dbl>,
#> # carrier <chr>, flight <int>, tailnum <chr>, origin <chr>,
#> # dest <chr>, air_time <dbl>, distance <dbl>, hour <dbl>,
#> # minute <dbl>, time_hour <dttm>

dplyr基础

下面主要介绍5分dplyr核心函数:

  • 按值筛选观测(filter())。
  • 对行进行重新排序(arrange())。
  • 按名称选取变量(select())。
  • 使用现有变量的函数创建新变量(mutate())。
  • 将多个值总结为一个摘要统计量(summarize())。

以上5个函数的工作方式都是相同的。

  • 第一个参数是一个数据框。
  • 随后的参数使用变量名称(不带引号)描述了在数据框上进行的操作。
  • 输出结果是一个新数据框。

fliter()函数

filter()函数可以基于观测的值筛选出一个观测子集。第一个参数是数据框名称,第二个参数以及随后的参数是用来筛选数据框的表达式。

1
filter(flights, month == 1, day == 1)

arrange()函数

arrange()函数用来改变行的顺序。它接受一个数据框和一组作为排序依据的列名(或者更复杂的表达式)作为参数。如果列名不只一个,那么就使用后面的列在前面排序的基础上继续排序。

1
arrange(flights, year, month, day)

我们可以采用desc()按列进行降序排序

1
arrange(flights, desc(arr_delay))

select()函数

基本用法

select()函数可以用来快速生成一个有用的变量子集。

1
2
3
4
5
6
7
8
# 按名称选择列
select(flights, year, month, day)

# 选择“year”和“day”之间的所有列(包括“year”和“day”)
select(flights, year:day)

# 选择不在“year”和“day”之间的所有列(不包括“year”和“day”)
select(flights, -(year:day))

select()函数中的辅助函数

select()函数中的辅助函数:

  • starts_with("abc"):匹配以“abc”开头的名称。
  • ends_with("xyz"):匹配以“xyz”结尾的名称。
  • contains("ijk"):匹配包含“ijk”的名称。
  • matches("(.)\\1"):选择匹配正则表达式的那些变量。这个正则表达式会匹配名称中有重复字符的变量。
  • num_range("x", 1:3):匹配x1、x2和x3。

我们可以通过everything()辅助函数将几个变量移到数据框开头:

1
select(flights, time_hour, air_time, everything())

重命名变量

select()可以重命名变量,但我们很少这样使用它,因为这样会丢掉所有未明确提及的变量。我们应该使用select()函数的变体rename()函数来重命名变量,以保留所有未明确提及的变量:

1
rename(flights, tail_num = tailnum)

mutate()函数

我们可以通过mutate()函数根据需要添加新列,新列是现有列的函数。

1
2
3
4
mutate(flights, 
gain = arr_delay - dep_delay,
speed = distance / air_time * 60
)

如果只想保留新变量,可以使用transmute()函数:

1
2
3
4
5
transmute(flights,
gain = arr_delay - dep_delay,
hours = air_time / 60,
gain_per_hour = gain / hours
)

注意:一旦创建后,新列就可以立即使用,比如后面的gain_per_hour可以直接用到刚刚创建的gain以及hours

常用的创建函数

创建函数必须是向量化的:它必须接受一个向量作为输入,并返回一个向量作为输出,而且输入向量与输出向量具有同样数目的分量。

下面介绍下常见的创建函数:

  • 算术运算符:+-*/^
  • 模运算符:%/%%%
  • 对数函数:log()log2()log10()
  • 偏移函数:lead()lag()函数可以返回一个序列的领先值和滞后值
1
2
3
4
5
6
(x <- 1:10)
#> [1] 1 2 3 4 5 6 7 8 9 10
lag(x)
#> [1] NA 1 2 3 4 5 6 7 8 9
lead(x)
#> [1] 2 3 4 5 6 7 8 9 10 NA
  • 累加和滚动聚合:累加和cumsum()、累加积cumprod()、累加最小值commin()和累加最大值cummax()、累加均值cummean()
1
2
3
4
5
6
x
#> [1] 1 2 3 4 5 6 7 8 9 10
cumsum(x)
#> [1] 1 3 6 10 15 21 28 36 45 55
cummean(x)
#> [1] 1.0 1.5 2.0 2.5 3.0 3.5 4.0 4.5 5.0 5.5
  • 逻辑比较:<<=>>=!=
  • 排秩:
    • min_rank()
    • row_number()
    • dense_rank()
    • percent_rank()
    • cume_dist()
    • ntile()
1
2
3
4
5
6
7
8
9
10
11
12
13
y <- c(1, 2, 2, NA, 3, 4)
min_rank(y)
#> [1] 1 2 2 NA 4 5
min_rank(desc(y))
#> [1] 5 3 3 NA 2 1
row_number(y)
#> [1] 1 2 3 NA 4 5
dense_rank(y)
#> [1] 1 2 2 NA 3 4
percent_rank(y)
#> [1] 0.00 0.25 0.25 NA 0.75 1.00
cume_dist(y)
#> [1] 0.2 0.6 0.6 NA 0.8 1.0

summarize()函数

summarize()函数可以将数据框折叠成一行。

1
2
3
4
5
summarize(flights, delay = mean(dep_delay, na.rm = TRUE))
#> # A tibble: 1 × 1
#> delay
#> <dbl>
#> 1 12.6

这个函数一般和group_by()一起使用。

每日平均延误时间的计算:

1
2
by_day <- group_by(flights, year, month, day)
summarize(by_day, delay = mean(dep_delay, na.rm = TRUE))

管道组合多种操作

如果不使用管道,我们不得不对每个中间数据框命名。管道可以显著提高代码的可读性。

下面是用管道来实现每日平均延误时间的计算:

1
2
3
flights %>%
group_by(year, month, day) %>%
summarize(mean = mean(dep_delay), na.rm = TRUE)

缺失值处理

聚合函数中也是遵循缺失值的一般规则:如果输入中有缺失值,那么输出也会是缺失值。因此,我们可以通过聚合函数的na.rm参数,在计算前除去缺失值。

常见的摘要函数

  • 位置度量:平均值mean(x)、中位数median(x)
  • 分散程度度量:均方误差sd(x)、四分位距IQR(x)、绝对中位数mad(x)
  • 秩的度量:
    • 最小值min(x)
    • 最大值max(x)
    • 分位数quantile(x, 0.25):分位数是中位数的扩展。例如,quantile(x, 0.25) 会找出x中按从小到大顺序大于前25%而小于后75%的值
  • 定位度量:first(x)last(x)nth(x, 2)
  • 计数:
    • n():无需任何参数,返回当前分组的大小
    • sum(!is.na(x)):计算非缺失值的数量
    • n_distinct(x):计算出唯一值的数量
  • 逻辑值的计数和比例:当与数值型函数一同使用时,TRUE会转换为1,FALSE会转换为0,这使得sum()mean()非常适用于逻辑值
    • sum(x > 10):找出x中TRUE的数量
    • mean(y == 0):找出比例

dplyr处理关系数据

存在于多个表中的这种数据统称为关系数据,因为重要的是数据间的关系,而不是单个数据集。

  • 主键:唯一标识其所在数据表中的观测。
  • 外键:唯一标识另一个数据表中的观测。

当数据表没有明确的主键(即每行都是一个观测,但没有一个变量组合能够明确地标识
它)时。我们可以使用mutate()函数和row_number()函数为表加上一个主键。这种主键称为代理键

合并连接

合并连接可以将两个表格中的变量组合起来,它先通过两个表格的键匹配观测,然后将一个表格中的变量复制到另一个表格中。

举例:我们可以通过left_join()函数组合airlinesflights2数据框,把航空公司的全名加入数据集:

1
2
flights2 <- flights %>%
select(year:day, hour, tailnum, carrier)
year month day hour tailnum carrier
2013 1 1 5 N14228 UA
2013 1 1 5 N24211 UA
2013 1 1 5 N619AA AA
2013 1 1 5 N804JB B6
2013 1 1 6 N668DN DL
2013 1 1 5 N39463 UA
1
2
flights2 %>%
left_join(airlines, by = "carrier")
year month day hour tailnum carrier name
2013 1 1 5 N14228 UA United Air Lines Inc.
2013 1 1 5 N24211 UA United Air Lines Inc.
2013 1 1 5 N619AA AA American Airlines Inc.
2013 1 1 5 N804JB B6 JetBlue Airways
2013 1 1 6 N668DN DL Delta Air Lines Inc.
2013 1 1 5 N39463 UA United Air Lines Inc.

基本用法

  • 内连接inner_join():只要两个观测的键是相等的,内连接就可以匹配它们。没有匹配的行不会包含在结果中。
  • 外连接:保留至少存在于一个表中的观测。
    • 左连接left_join():保留x中的所有观测。
    • 右连接right_join():保留y中的所有观测
    • 全连接full_join():保留x和y中的所有观测。

两表中的匹配向量名不同

命名字符向量by = c("a" = "b")。这种方式会匹配x表中的a变量和y表中的b变量。输出结果中使用的是x表中的变量。

1
2
flights2 %>%
left_join(airlines, by = d("dest" = "faa"))

其他实现方式

base::merge()函数可以实现所有4种合并连接操作。

dplyr merge
inner_join(x, y) merge(x, y)
left_join(x, y) merge(x, y, all.x = TRUE)
right_join(x, y) merge(x, y, all.y = TRUE)

dplyr连接操作的优点是,可以更加清晰地表达出代码的意图:不同连接间的区别确实非常重要,但隐藏在merge()函数的参数中了。dplyr连接操作的速度明显更快,而且不会弄乱行的顺序。

筛选连接

筛选连接匹配观测的方式与合并连接相同,但前者影响的是观测,而不是变量。筛选连接有两种类型。

  • semi_join(x, y):保留x表中与y表中的观测相匹配的所有观测。
  • anti_join(x, y):丢弃x表中与y表中的观测相匹配的所有观测。

举例:找出飞往最受欢迎的前10个目的地的所有航班:

  • 自己构造筛选器
1
2
3
4
5
6
top_dest <- flights %>%
count(dest, sort = TRUE) %>%
head(10)

flights %>%
filter(dest %in% top_dest$dest)
  • 利用筛选连接:
1
2
3
4
5
6
top_dest <- flights %>%
count(dest, sort = TRUE) %>%
head(10)

flights %>%
semi_join(top_dest)

《R数据科学》学习笔记


----------- 本文结束啦感谢您阅读 -----------

赞赏一杯咖啡

欢迎关注我的其它发布渠道