数据转换
5.1 简介
可视化是对数据观察的重要工具,但你很少能直接得到你想要的数据格式。通常,需要创建一些新变量或摘要,或者可能只是想重命名变量或重新排列观察值的结果,从而更容易地处理数据。通过使用dplyr包,让新数据集转换成不同数据。
5.1.1 加载包
我们将重点介绍 tidyverse 的另一个核心成员:dplyr,你将学会使用 dplyr 包。我们将使用 nycflights13 包中的数据来进行说明,并使用 ggplot2 帮助我们理解数据。
library(nycflights13)
library(tidyverse)
请注意加载 tidyverse 包时如果显示冲突消息。它告诉你 dplyr 覆盖了基础 R 中的一些函数。如果你想在加载 dplyr 后使用基础包的函数,则需要使用它们的全名:stats::filter()和stats::lag()。
5.1.2 nycflights13包
为了了解 dplyr 的基本数据操作方法,我们将使用nycflights13::flights数据集。此数据框包含 2013 年所有从纽约市出发的 336,776 次航班。数据来自美国运输统计局,通过?flights查看详细信息。
flights
#> # A tibble: 336,776 x 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
#> # … with 336,770 more rows, and 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>
此数据框的显示结果与以往的数据框略有不同:它仅显示适合屏幕的前几行和所有列。(要查看整个数据集,在 RStudio 查看器中打开数据集,可以运行View(flights))。因为它是tibble,所以显示方式不同。
列名称下的字母缩写描述了每个变量的类型:
int
代表整数。dbl
代表双精度实数或实数。chr
代表字符向量或字符串。dttm
代表日期时间(日期 + 时间)。
还有其他类型:
lgl
代表仅包含TRUE
或FALSE
的逻辑向量。fctr
代表因子,R 用它来表示具有固定可能值的分类变量。date
代表日期。
5.1.3 dplyr基础
下面我们将了解五个关键的 dplyr 函数,这些函数可以解决绝大多数数据问题:
- 按值选取观察值 (
filter()
)。 - 重新排列行 (
arrange()
)。 - 按名称选择列变量 (
select()
)。 - 使用现有变量创建新变量(
mutate()
) 。 - 将多个值进行汇总 (
summarise()
)。
这些都可以与group_by()
一起使用,group_by()从对整个数据集进行分组操作。
所有函数工作方式都相似:
- 第一个参数是一个数据框。
- 后面的参数使用变量名称(不带引号)描述如何处理数据框。
- 结果是一个新的数据框。
将这些属性结合在一起,可以轻松地将多个简单的步骤结合,以实现复杂的结果。接下来,让我们详细了解每个方法的使用吧!
5.2 filter()
行过滤
filter()根据观测值对观测结果进行子集化。第一个参数是数据框的名称。第二个和后续参数是过滤数据框的表达式。例如,选择 1 月 1 日的所有航班:
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
#> <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
#> # … with 836 more rows, and 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>
当运行该行代码时,dplyr 会执行过滤操作并返回一个新的数据框。dplyr 函数不会修改它们输入的数据集,因此如果要保存结果,则需要使用赋值运算符<-
进行保存:
jan1 <- filter(flights, month == 1, day == 1)
如果想显示结果的同时将它们保存到一个变量中,可以用括号括起来:
(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
#> <int> <int> <int> <int> <int> <dbl> <int> <int>
#> 1 2013 12 25 456 500 -4 649 651
#> 2 2013 12 25 524 515 9 805 814
#> 3 2013 12 25 542 540 2 832 850
#> 4 2013 12 25 546 550 -4 1022 1027
#> 5 2013 12 25 556 600 -4 730 745
#> 6 2013 12 25 557 600 -3 743 752
#> # … with 713 more rows, and 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>
5.2.1 比较操作符
为了有效地使用过滤方法,需要知道如何使用比较操作符选择您想要的观察结果。R提供了标准比较操作符:>
、>=
、<
、<=
、!=
(不等于)和==
(等于)。
注意,最容易犯的错误是将=
当成==
使用,=
是赋值而==
是判断两者相等:
filter(flights, month = 1)
#> Error: Problem with `filter()` input `..1`.
#> ✖ Input `..1` is named.
#> ℹ This usually means that you've used `=` instead of `==`.
#> ℹ Did you mean `month == 1`?
注意:在浮点数比较时需要注意,得到的结果可能和你预想的不一样。
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
5.2.2 逻辑运算符
filter()的多个参数将" and "组合在一起是为了将一行都表示输出中,每个表达式必须为真。对于其他类型的组合,可以使用布尔运算符:&
是“与”,|
是“或”,!
是“非”。下图显示了布尔操作的完整集合。
查找在 11 月或 12 月起飞的所有航班:
filter(flights, month == 11 | month == 12)
执行顺序不像英语。你不能写filter(flights, month == (11 | 12))
,你可以理解成“查找所有在 11 月或 12 月起飞的航班”,实际上它会查找所有相等的月份,即11 | 12
一个计算结果为TRUE
的表达式。TRUE
就变为一个1,因此这将查找 1 月份的所有航班,而不是 11 月或 12 月。
该问题比较好的简写是x %in% y
。这将选择x
中的每一个值是否也在y
中。我们可以用它来重写上面的代码:
nov_dec <- filter(flights, month %in% c(11, 12))
有时,您可以通过记住De Morgan’s law(德摩根定律)来简化复杂的子集:!(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 也有&&
和 ||
。注意在这里不要使用它们!想了解更多可参阅conditional-execution。
5.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(x)
#> [1] TRUE
filter()
仅过滤条件为TRUE
的行;它排除了FALSE
和NA
值。如果想保留缺失值,需要明确要求它们:
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