dplyr包是tidyverse中的核心R包。
本文用到的数据集
本文用到的是nycflights13::flights
这个数据集,这个数据框包含了2013 年从纽约市出发的所有336776次航班的信息。
1 | flights |
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 | # 按名称选择列 |
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 | mutate(flights, |
如果只想保留新变量,可以使用transmute()
函数:
1 | transmute(flights, |
注意:一旦创建后,新列就可以立即使用,比如后面的gain_per_hour
可以直接用到刚刚创建的gain
以及hours
。
常用的创建函数
创建函数必须是向量化的:它必须接受一个向量作为输入,并返回一个向量作为输出,而且输入向量与输出向量具有同样数目的分量。
下面介绍下常见的创建函数:
- 算术运算符:
+
、-
、*
、/
、^
- 模运算符:
%/%
和%%
- 对数函数:
log()
、log2()
和log10()
- 偏移函数:
lead()
和lag()
函数可以返回一个序列的领先值和滞后值
1 | (x <- 1:10) |
- 累加和滚动聚合:累加和
cumsum()
、累加积cumprod()
、累加最小值commin()
和累加最大值cummax()
、累加均值cummean()
1 | x |
- 逻辑比较:
<
、<=
、>
、>=
和!=
- 排秩:
min_rank()
row_number()
dense_rank()
percent_rank()
cume_dist()
ntile()
1 | y <- c(1, 2, 2, NA, 3, 4) |
summarize()函数
summarize()
函数可以将数据框折叠成一行。
1 | summarize(flights, delay = mean(dep_delay, na.rm = TRUE)) |
这个函数一般和group_by()
一起使用。
每日平均延误时间的计算:
1 | by_day <- group_by(flights, year, month, day) |
管道组合多种操作
如果不使用管道,我们不得不对每个中间数据框命名。管道可以显著提高代码的可读性。
下面是用管道来实现每日平均延误时间的计算:
1 | flights %>% |
缺失值处理
聚合函数中也是遵循缺失值的一般规则:如果输入中有缺失值,那么输出也会是缺失值。因此,我们可以通过聚合函数的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()
函数组合airlines
和flights2
数据框,把航空公司的全名加入数据集:
1 | flights2 <- flights %>% |
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 | flights2 %>% |
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 | flights2 %>% |
其他实现方式
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 | top_dest <- flights %>% |
- 利用筛选连接:
1 | top_dest <- flights %>% |
《R数据科学》学习笔记