使用dplyr处理关系数据
在实际应用中,我们常会涉及到多个数据表,必须综合使用它们才能找到关键信息。存在于多个表中的这种数据统称为关系数据。本章中的很多概念都和SQL中的相似,只是在dplyr中的表达形式略微不同。一般来说,dplyr 要比 SQL 更容易使用。
处理关系数据有三类操作:
- 合并连接:向数据框中加入新变量,新变量的值是另一个数据框中的匹配观测。
- 筛选连接:根据是否匹配另一个数据框中的观测,筛选数据框中的观测。
- 集合操作:将观测作为集合元素来处理。
数据准备
我们将使用 nycflights13 包来学习关系数据。nycflights13 中包含了与 flights 相关的 4 个 tibble:
数据表 | 信息 |
airlines | 可以根据航空公司的缩写码查到公司全名。 |
airports | 给出了每个机场的信息,通过 faa 机场编码进行标识。 |
planes | 给出了每架飞机的信息,通过 tailnum 进行标识。 |
weather | 给出了纽约机场每小时的天气状况。 |
各数据表之间的关系如下:
对于 nycflights13 包中的表来说:
- flights 与 planes 通过单变量 tailnum 相连;
- flights 与 airlines 通过变量 carrier 相连;
- flights 与 airports 通过两种方式相连(变量 origin 和 dest);
- flights 与 weather 通过变量 origin(位置)以及 year、month、day 和 hour(时间)相连。
键
用于连接每对数据表的变量称为键。键是能唯一标识观测的变量(或变量集合)。
键的类型有两种。
- 主键:唯一标识其所在数据表中的观测。例如,planes$tailnum 是一个主键,因为其可以唯一标识 planes 表中的每架飞机。
- 外键:唯一标识另一个数据表中的观测。例如,flights$tailnum 是一个外键,因为其出现在 flights 表中,并可以将每次航班与唯一一架飞机匹配。
一个变量既可以是主键,也可以是外键。例如,origin 是 weather 表主键的一部分,同时 也是 airports 表的外键。一旦识别出表的主键,最好验证一下,看看它们能否真正唯一标识每个观测。一种验证方 法是对主键进行 count() 操作,然后查看是否有 n 大于 1 的记录:
planes %>% count(tailnum) %>% filter(n > 1) #> # A tibble: 0 × 2 #> # ... with 2 variables: tailnum <chr>, n <int> weather %>% count(year, month, day, hour, origin) %>% filter(n > 1) #> Source: local data frame [0 x 6] #> Groups: year, month, day, hour [0] #> #> # ... with 6 variables: year <dbl>, month <dbl>, day <int>, #> # hour <int>, origin <chr>, n <int>
合并连接
合并连接可以将两个表格中的变量组合起来,它先通过两个表格的键匹配观测,然后将一个表格中的变量复制到另一个表格中。下面借助图形来帮助理解连接的原理:
有颜色的列表示作为“键”的变量:它们用于在表间匹配行。灰色列表示“值”列,是与键对应的值。
- 内连接( inner_join() )
只要两个观测的键是相等的,内连接就可以匹配他们,没有匹配的行不会包含在结果中。
- 外连接( inner_join() )
外连接保留至少存在于一个表中的观测。它有3种连接类型,这些连接会向每个表中添加额外的“虚拟”观测,这个观测的值用NA来填充。
连接方式 | 作用 |
左连接 | 保留x中的所有观测 |
右连接 | 保留y中的所有观测 |
全连接 | 保留x和y中的所有观测 |
图形表示如下:
筛选连接
筛选连接匹配观测的方式与合并连接相同,但前者影响的是观测,而不是变量。筛选连接有两种类型:
- semi_join(x,y):保留x表中与y表中的观测相匹配的所有观测。
- anti_join(x,y):丢弃x表中与y表中的观测相匹配的所有观测。
集合操作
集合操作都是作用于整行的,比较的是每个变量的值。集合操作需要x和y具有相同的变量,并将观测按照集合来处理。
操作 | 作用 |
intersect(x,y) | 返回既在x表,又在y表中的观测。 |
union(x,y) | 返回x表或y表中的唯一观测。 |
setdiff(x,y) | 返回在x表,但又不在y表中的观测。 |
给定以下简单数据:
df1 <- tribble( ~x, ~y, 1, 1, 2, 1 ) df2 <- tribble( ~x, ~y, 1, 1, 1, 2 )
4种可能的集合操作为:
intersect(df1, df2) #> # A tibble: 1 × 2 #> x y #> <dbl> <dbl> #> 1 1 1 # 注意,我们得到了3行,而不是4行 union(df1, df2) #> # A tibble: 3 × 2 #> x y #> <dbl> <dbl> #> 1 1 2 #> 2 2 1 #> 3 1 1 setdiff(df1, df2) #> # A tibble: 1 × 2 #> x y #> <dbl> <dbl> #> 1 2 1 setdiff(df2, df1) #> # A tibble: 1 × 2 #> x y #> <dbl> <dbl> #> 1 1 2