2 数据变换
可视化是生成图形的重要工具,但很少能直接获得所需形式的数据来制作理想的图表。在本章中,你将学习如何完成所有这些(以及更多!)操作,本章将介绍使用dplyr包和 2013 年从纽约市出发的航班新数据集进行数据转换。
dplyr包提供了一组用于数据转换的函数,这些函数可以组合使用以实现复杂的数据操作。dplyr中的大多数函数都遵循相同的语法结构:
- 第一个参数是数据框(或 tibble)。
- 后续的参数通常是变量名(不带引号),用来指定要操作的列。
- 大多数函数返回一个数据框(或 tibble)。
2.1 行操作
filter()选择满足条件的行: 可以使用任意逻辑符号,==,!=,<,>,<=,>=,&,|,!。arrange()根据列的值对行进行排序: 排序所依据的列可以是单列,也可以是多列。可以使用desc()函数对结果进行降序排序。distinct()查找数据集中的所有唯一行。在需要找出特定变量组合时,可以提供列名,即找出符合提供列名组合的行。如果您想找到出现次数,最好将distinct()换成 count(),同时使用 sort = TRUE 参数,可以按出现次数降序排列。有关count()的更多信息,请参见后面的章节 小节 9.1。
|和==可以合并为%in%# 查找数据集中的唯一行
flights |>
distinct()# 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
7 2013 1 1 555 600 -5 913 854
8 2013 1 1 557 600 -3 709 723
9 2013 1 1 557 600 -3 838 846
10 2013 1 1 558 600 -2 753 745
# ℹ 336,766 more rows
# ℹ 11 more variables: 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>
# 查找特定列组合的唯一行,注意与`select()`的区别
flights |>
distinct(origin, dest)# A tibble: 224 × 2
origin dest
<chr> <chr>
1 EWR IAH
2 LGA IAH
3 JFK MIA
4 JFK BQN
5 LGA ATL
6 EWR ORD
7 EWR FLL
8 LGA IAD
9 JFK MCO
10 LGA ORD
# ℹ 214 more rows
# 计算特定列组合的出现次数,并按次数降序排列
flights |>
count(origin, dest, sort = TRUE)# A tibble: 224 × 3
origin dest n
<chr> <chr> <int>
1 JFK LAX 11262
2 LGA ATL 10263
3 LGA ORD 8857
4 JFK SFO 8204
5 LGA CLT 6168
6 EWR ORD 6100
7 JFK BOS 5898
8 LGA MIA 5781
9 JFK MCO 5464
10 EWR BOS 5327
# ℹ 214 more rows
2.2 列操作
-
select()选择特定列: 可以使用列名或列编号。有许多的辅助函数:
-
starts_with()选择以指定字符串开头的列。 -
ends_with()选择以指定字符串结尾的列。 -
contains()选择包含指定字符串的列。 - 使用
?select()可以产看所有辅助函数及其用法。
一旦我们了解了正则表达式(?sec-regular-expressions), 我们就可以使用matches()函数来选择符合正则表达式模式的列。
-
rename()重命名列: 可以使用列名对列进行重命名。
- 使用
janitor::clean_names()可以批量自动清理、整理列名。
-
mutate()创建新列: 可以使用任意公式对列进行计算。默认情况下,新列会添加到数据集的最后一列,在函数中传入参数.before1或.after可以调整新列的位置,如.before = 1表示新列添加到原数据集的第一列之前,即第一列。
-
mutate()有一个非常有用的参数,.keep,用于控制在创建新列后保留哪些列:-
.keep = "all": 保留所有列(默认值)。 -
.keep = "used": 仅保留用于计算新列的列以及新列本身。最常用。 -
.keep = "unused": 仅保留未用于计算新列的列。 -
.keep = "none": 仅保留新列。
-
flights |>
mutate(
gain = dep_delay - arr_delay,
hours = air_time / 60,
.keep = "used"
)# A tibble: 336,776 × 5
dep_delay arr_delay air_time gain hours
<dbl> <dbl> <dbl> <dbl> <dbl>
1 2 11 227 -9 3.78
2 4 20 227 -16 3.78
3 2 33 160 -31 2.67
4 -1 -18 183 17 3.05
5 -6 -25 116 19 1.93
6 -4 12 150 -16 2.5
7 -5 19 158 -24 2.63
8 -3 -14 53 11 0.883
9 -3 -8 140 5 2.33
10 -2 8 138 -10 2.3
# ℹ 336,766 more rows
以上代码中生成新变量用到了dep_delay、arr_delay、air_time3个变量,由于使用了参数.keep = "used" ,因此最终结果只保留了这3个变量和新生成的变量gain和hours。
-
relocate()更改列排序。
- 和
select()类似,relocate()中也有.before和.after参数。
flights |>
relocate(time_hour, air_time)# A tibble: 336,776 × 19
time_hour air_time year month day dep_time sched_dep_time
<dttm> <dbl> <int> <int> <int> <int> <int>
1 2013-01-01 05:00:00 227 2013 1 1 517 515
2 2013-01-01 05:00:00 227 2013 1 1 533 529
3 2013-01-01 05:00:00 160 2013 1 1 542 540
4 2013-01-01 05:00:00 183 2013 1 1 544 545
5 2013-01-01 06:00:00 116 2013 1 1 554 600
6 2013-01-01 05:00:00 150 2013 1 1 554 558
7 2013-01-01 06:00:00 158 2013 1 1 555 600
8 2013-01-01 06:00:00 53 2013 1 1 557 600
9 2013-01-01 06:00:00 140 2013 1 1 557 600
10 2013-01-01 06:00:00 138 2013 1 1 558 600
# ℹ 336,766 more rows
# ℹ 12 more variables: dep_delay <dbl>, arr_time <int>, sched_arr_time <int>,
# arr_delay <dbl>, carrier <chr>, flight <int>, tailnum <chr>, origin <chr>,
# dest <chr>, distance <dbl>, hour <dbl>, minute <dbl>
2.3 分组
-
group_by()对数据集进行分组: 可以使用列名对数据集进行分组。
- 可以同时以多行为基准进行分组操作。
-
summarise()对分组后的结果进行汇总: 可以使用任意公式对分组后的结果进行汇总。
-
在使用
summarise()时,可以省略group_by(),并在函数中加入.by参数。-
.by参数是dplyr在1.10版本中新加入summarise()函数的参数,与group_by()效果一致,它还有一个优点是无需在分组后使用.group或ungroup函数,同时可以起到简化代码的作用。值得注意的是,.by参数的结果不会改变原数据的类型,而group_by会将结果转化为tibble。具体可参看dplyr 1.1.0的博客介绍
-
使用
n()函数可以计算分组后的行数,这有点类似于data.table中的.N。在进行分组汇总计算时,要特别注意缺失值。在
R中,缺失值是会传染的。
- 可以针对多个变量进行分组,在此情况下,每次汇总计算都会针对分组的最后一个变量,
group_by()也会返回一个提示信息,我们可以通过设置.groups参数来控制这个信息的显示。.groups参数有四个选项:
-
"drop_last": 删除最后一个分组变量(默认值)。 -
"drop": 删除所有分组变量。 -
"keep": 保留所有分组变量。 -
"rowwise": 将结果转换为按行分组的tibble。
2.3.1 切片
slice_head(df, n = 1)从每组中取第一行。slice_tail(df, n = 1)取每组的最后一行。-
slice_min(x, n = 1)取 x 列中数值最小的一行。slice_max(x, n = 1)取 x 列中数值最大的一行。-
slice_min()和slice_max()会保留并列值,因此n = 1表示返回所有具有最高值的行。如果你希望每组只有一条记录,可以设置with_ties = FALSE。
-
slice_sample(df, n = 1)随机取一行。以上所有函数中的参数
n均可以使用prop代替,即使用比例来指定切片的行数量。
2.4 案例-aggregates and sample size
每当进行聚合(group_by()+summarise())操作时,最终结果包含计数结果(n())总是很有用的。我们用Lahman包中的Batting数据集来演示这一点。Batting数据集包含了从1871年到2016年期间所有大联盟棒球运动员的击球数据。
# A tibble: 24,011 × 3
playerID performance n
<chr> <dbl> <int>
1 aardsda01 0 9
2 aaronha01 0.305 23
3 aaronto01 0.229 7
4 aasedo01 0 13
5 abadan01 0.0952 3
6 abadfe01 0.111 12
7 abadijo01 0.224 2
8 abbated01 0.254 10
9 abbeybe01 0.169 6
10 abbeych01 0.281 5
# ℹ 24,001 more rows
在上面的代码中,我们计算了每个球员的击球表现(performance),即击中次数(H)与击球次数(AB)的比值。同时,我们还计算了每个球员的比赛场次(n)。这样做的好处是,我们可以根据比赛场次来评估击球表现的可靠性。例如,一个球员如果只参加了几场比赛,那么他的击球表现可能并不具有代表性。
2.5 总结
本章介绍了dplyr包中的一些常用数据转换函数,包括行操作、列操作和分组操作。通过这些函数,我们可以轻松地对数据进行过滤、排序、选择、重命名、创建新列以及分组汇总等操作。这些技能对于数据分析和可视化都是非常重要的基础。
在第三部分变量类型和变量操作工具中,我们将针对特定的变量,对本章介绍的内容进行更深入的探讨。
.符号表示为函数的参数,而不是要创建新列的变量名称。↩︎