1 简介
在数据分析过程中,基本上都要涉及多个数据表。那当我们对多个数据表感兴趣时,此时需要将它们组合起来才能解决我们相应的问题。我们将多个数据表统称为数据间的关系。
关系总是在一对表之间定义。其他所有的关系都是从这个简单的想法建立起来的:三个或更多表的关系总是每对之间关系的一个属性。有时一对关系的两个元素可以是同一个表!例如,如果您有一张人员表,并且每个人都有对其父母的引用。
要处理关系数据,您需要学会处理表的一些操作。用于处理数据间关系的操作有以下三类:
变异连接,从另一个数据帧的匹配观察结果中添加新的变量。
过滤连接,根据一个数据帧中的观察结果是否与另一个表中的观察结果相匹配,哪个数据帧对其中的观察结果进行过滤。
集合操作,它们把观察结果看作是集合元素。
1.1 加载包
我们将使用dplyr中的两个表来研究来自nycflights13
的关系数据。
library(tidyverse)
library(nycflights13)
2 nycflights13包中的数据集
我们将使用 nycflights13 包来了解关系数据。nycflights13 包含与flights
使用的表相关的四个小标题:
-
airlines
可以从其缩写代码中查找完整的运营商名称:airlines #> # A tibble: 16 x 2 #> carrier name #> <chr> <chr> #> 1 9E Endeavor Air Inc. #> 2 AA American Airlines Inc. #> 3 AS Alaska Airlines Inc. #> 4 B6 JetBlue Airways #> 5 DL Delta Air Lines Inc. #> 6 EV ExpressJet Airlines Inc. #> # … with 10 more rows
-
airports
提供有关每个机场的信息,由机场代码faa
标识:airports #> # A tibble: 1,458 x 8 #> faa name lat lon alt tz dst tzone #> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <chr> <chr> #> 1 04G Lansdowne Airport 41.1 -80.6 1044 -5 A America/New_Y… #> 2 06A Moton Field Municipal Airp… 32.5 -85.7 264 -6 A America/Chica… #> 3 06C Schaumburg Regional 42.0 -88.1 801 -6 A America/Chica… #> 4 06N Randall Airport 41.4 -74.4 523 -5 A America/New_Y… #> 5 09J Jekyll Island Airport 31.1 -81.4 11 -5 A America/New_Y… #> 6 0A9 Elizabethton Municipal Air… 36.4 -82.2 1593 -5 A America/New_Y… #> # … with 1,452 more rows
-
planes
给出关于每个飞机的信息,由tailnum
标识:planes #> # A tibble: 3,322 x 9 #> tailnum year type manufacturer model engines seats speed engine #> <chr> <int> <chr> <chr> <chr> <int> <int> <int> <chr> #> 1 N10156 2004 Fixed wing mu… EMBRAER EMB-1… 2 55 NA Turbo-… #> 2 N102UW 1998 Fixed wing mu… AIRBUS INDUST… A320-… 2 182 NA Turbo-… #> 3 N103US 1999 Fixed wing mu… AIRBUS INDUST… A320-… 2 182 NA Turbo-… #> 4 N104UW 1999 Fixed wing mu… AIRBUS INDUST… A320-… 2 182 NA Turbo-… #> 5 N10575 2002 Fixed wing mu… EMBRAER EMB-1… 2 55 NA Turbo-… #> 6 N105UW 1999 Fixed wing mu… AIRBUS INDUST… A320-… 2 182 NA Turbo-… #> # … with 3,316 more rows
-
weather
提供纽约市各机场每小时的天气情况:weather #> # A tibble: 26,115 x 15 #> origin year month day hour temp dewp humid wind_dir wind_speed wind_gust #> <chr> <int> <int> <int> <int> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> #> 1 EWR 2013 1 1 1 39.0 26.1 59.4 270 10.4 NA #> 2 EWR 2013 1 1 2 39.0 27.0 61.6 250 8.06 NA #> 3 EWR 2013 1 1 3 39.0 28.0 64.4 240 11.5 NA #> 4 EWR 2013 1 1 4 39.9 28.0 62.2 250 12.7 NA #> 5 EWR 2013 1 1 5 39.0 28.0 64.4 260 12.7 NA #> 6 EWR 2013 1 1 6 37.9 28.0 67.2 240 11.5 NA #> # … with 26,109 more rows, and 4 more variables: precip <dbl>, pressure <dbl>, #> # visib <dbl>, time_hour <dttm>
显示不同表之间关系的一种方法是使用绘图:
理解这样的图的关键是记住每个关系总是与一对表有关。你不需要了解整个事情;您只需要了解您感兴趣的表之间的关系链。
对于 nycflights13:
flights
连接到planes
,通过单个变量tailnum
。flights
连接到airlines
,通过carrier
变量。flights
连接到airports
,通过两个origin
和dest
变量。flights
连接到weather
的变量有origin
(位置)和year
、month
、day
和hour
(时间)。
3 通过key连接
用于连接每对表的变量称为键。对于观察的变量(或变量集)键是唯一标识符。在简单的情况下,单个变量足以识别出观察值。例如,每个飞机都有唯一标识tailnum
。在其他情况下,可能需要多个变量。例如,在确定的观察weather
,你需要五个变量:year
,month
,day
,hour
,和origin
。
有两种类型的键:
主键唯一地标识一个观察结果。例如,
planes$tailnum
是主键,因为它唯一标识了planes
表中的每个飞机。外键唯一标识另一个表的观察结果。例如,
flights$tailnum
是外键,因为它出现在flights
表中,它将每个航班与飞机一对一匹配。
变量既可以是主键,也可以是外键。例如,origin
在weather
表中是主键的一部分,在airports
表中是外键。
一旦确定了表中的主键,最好验证它们唯一对应每个观察结果。一种方法是使用count()
查找住建数量n
大于 1 的条目:
planes %>%
count(tailnum) %>%
filter(n > 1)
#> # A tibble: 0 x 2
#> # … with 2 variables: tailnum <chr>, n <int>
weather %>%
count(year, month, day, hour, origin) %>%
filter(n > 1)
#> # A tibble: 3 x 6
#> year month day hour origin n
#> <int> <int> <int> <int> <chr> <int>
#> 1 2013 11 3 1 EWR 2
#> 2 2013 11 3 1 JFK 2
#> 3 2013 11 3 1 LGA 2
有时一个表没有明确的主键:每一行都是一个观察值,但没有任何变量组合能够单独的标识。例如,flights
表中的主键是什么?您可能认为它是日期加上航班号或尾号,但它们都不是唯一的:
flights %>%
count(year, month, day, flight) %>%
filter(n > 1)
#> # A tibble: 29,768 x 5
#> year month day flight n
#> <int> <int> <int> <int> <int>
#> 1 2013 1 1 1 2
#> 2 2013 1 1 3 2
#> 3 2013 1 1 4 2
#> 4 2013 1 1 11 3
#> 5 2013 1 1 15 2
#> 6 2013 1 1 21 2
#> # … with 29,762 more rows
flights %>%
count(year, month, day, tailnum) %>%
filter(n > 1)
#> # A tibble: 64,928 x 5
#> year month day tailnum n
#> <int> <int> <int> <chr> <int>
#> 1 2013 1 1 N0EGMQ 2
#> 2 2013 1 1 N11189 2
#> 3 2013 1 1 N11536 2
#> 4 2013 1 1 N11544 3
#> 5 2013 1 1 N11551 2
#> 6 2013 1 1 N12540 2
#> # … with 64,922 more rows
在开始处理这些数据时,我们可能会认为每个航班号每天只使用一次,然而事实并非如此!如果表缺乏一个主键,它有时根据需要增加一个mutate()
和row_number()
。如果您已经进行了一些过滤并想要重新检查原始数据,则可以更轻松地匹配观察结果。这称为代理键。
另一个表中的主键和相应的外键形成关系。关系通常是一对多的。例如,每个航班都有一架飞机,但每架飞机都有许多航班。在其他数据中,您偶尔会看到一对一的关系。您可以将其视为一对多的特殊情况。您可以使用多对一关系和一对多关系来建模多对多关系。例如,在此数据中,航空公司和机场之间存在多对多关系:每家航空公司飞往多个机场;每个机场都有许多航空公司。
4 变异连接
我们组合一对表的第一个工具是mutating join。变异连接允许您组合来自两个表的变量。它首先通过键匹配观察值,然后将变量从一个表复制到另一个表。
如同mutate()
,连接函数在右边添加变量,所以如果你已经有很多变量,新的变量不会被打印出来。
flights2 <- flights %>%
select(year:day, hour, origin, dest, tailnum, carrier)
flights2
#> # A tibble: 336,776 x 8
#> year month day hour origin dest tailnum carrier
#> <int> <int> <int> <dbl> <chr> <chr> <chr> <chr>
#> 1 2013 1 1 5 EWR IAH N14228 UA
#> 2 2013 1 1 5 LGA IAH N24211 UA
#> 3 2013 1 1 5 JFK MIA N619AA AA
#> 4 2013 1 1 5 JFK BQN N804JB B6
#> 5 2013 1 1 6 LGA ATL N668DN DL
#> 6 2013 1 1 5 EWR ORD N39463 UA
#> # … with 336,770 more rows
假设您想将完整的航空公司名称添加到flights2
数据中。您可以将airlines
和flights2
通过left_join()
进行组合:
flights2 %>%
select(-origin, -dest) %>%
left_join(airlines, by = "carrier")
#> # A tibble: 336,776 x 7
#> year month day hour tailnum carrier name
#> <int> <int> <int> <dbl> <chr> <chr> <chr>
#> 1 2013 1 1 5 N14228 UA United Air Lines Inc.
#> 2 2013 1 1 5 N24211 UA United Air Lines Inc.
#> 3 2013 1 1 5 N619AA AA American Airlines Inc.
#> 4 2013 1 1 5 N804JB B6 JetBlue Airways
#> 5 2013 1 1 6 N668DN DL Delta Air Lines Inc.
#> 6 2013 1 1 5 N39463 UA United Air Lines Inc.
#> # … with 336,770 more rows
将airlines的航空公司名称name
加入到flight2的结果。将这种类型的连接称为变异连接。在这种情况下,也可以使用mutate()
和 R 的base包达到同样的效果:
flights2 %>%
select(-origin, -dest) %>%
mutate(name = airlines$name[match(carrier, airlines$carrier)])
#> # A tibble: 336,776 x 7
#> year month day hour tailnum carrier name
#> <int> <int> <int> <dbl> <chr> <chr> <chr>
#> 1 2013 1 1 5 N14228 UA United Air Lines Inc.
#> 2 2013 1 1 5 N24211 UA United Air Lines Inc.
#> 3 2013 1 1 5 N619AA AA American Airlines Inc.
#> 4 2013 1 1 5 N804JB B6 JetBlue Airways
#> 5 2013 1 1 6 N668DN DL Delta Air Lines Inc.
#> 6 2013 1 1 5 N39463 UA United Air Lines Inc.
#> # … with 336,770 more rows
但是当您需要匹配多个变量时,这就非常的麻烦,而且需要仔细理解才能弄清楚整体意图,达到期望的结果。
下面将详细解释变异连接的原理。首先学习一种有用的连接可视化表示。然后我们将使用它来解释四个变异连接函数:内连接和三个外连接。
在处理真实数据时,键并不总是唯一标识观察结果,因此接下来我们将讨论没有唯一匹配时会发生什么。最后,您将学习如何告诉 dplyr 哪些变量是给定连接的键。
4.1 理解连接原理
为了帮助您了解连接的工作原理,通过图形展示:
x <- tribble(
~key, ~val_x,
1, "x1",
2, "x2",
3, "x3"
)
y <- tribble(
~key, ~val_y,
1, "y1",
2, "y2",
4, "y3"
)
彩色列代表“key”变量:它们用于匹配表之间的行。灰色列表示对应的“value”列。在这些示例中,通过展示单个键变量,但该想法以一种直接的方式概括为多个键和多个值。
连接是将x
中的每一行连接到y
中的0行、1行或多行的一种方法。下图显示了作为一对直线的交点的每个潜在匹配。
(如果您仔细观察,您可能会注意到我们已经切换了x
中的键和值列的顺序。这是为了强调连接基于键进行匹配;该值只是在过程中被携带。)
在实际的连接中,匹配将用点表示。点的数量=匹配的数量=输出中的行数。
4.2 内连接
最简单的连接类型是内连接。只要它们的键相等,内连接就会匹配观察值对:
(准确地说,这是一个内部等值联接,因为键是使用=
运算符匹配的。由于大多数连接是等值联接,我们通常不采用该连接方法。)
内连接的输出是一个包含键、x 值和 y 值的新数据框。我们用by
来告诉 dplyr 哪个变量是键:
x %>%
inner_join(y, by = "key")
#> # A tibble: 2 x 3
#> key val_x val_y
#> <dbl> <chr> <chr>
#> 1 1 x1 y1
#> 2 2 x2 y2
内连接最重要的特性是结果中只包含匹配的行。这意味着通常内部连接通常不适合用于分析,因为它会丢失很多的观察值。
4.3 外连接
内连接保留出现在两个表中的观察结果。外连接保存至少出现在其中一个表中的观察结果。外连接有三种类型:
-
左连接:保留
x
所有观察值。 -
右连接:保留
y
所有观测值。 -
全连接:保留
x
和y
所有观测值。
这些连接通过向每个表添加额外的“虚拟”观察值。这个观察值有一个总是匹配的键(如果没有其他键匹配),则填充NA
。
下面通过图形来解释具体原理:
最常用的连接是左连接:每当您从另一个表中查找附加数据时都可以使用它,因为即使没有匹配,它也会保留原始观察结果。左连接应该是默认连接方式。
描述不同连接类型的另一种方法是使用维恩图:
但是维恩图有个限制:维恩图无法显示当键不能唯一标识观察时会发生什么。
4.4 重复的键
前面我们遇到的数据中键都是唯一的,但是我们在处理数据时往往会碰到键不唯一,有下面两种情况:
-
一张数据表有重复的键。这在您想要添加附加信息时很有用,因为通常存在一对多关系。
注意,我将键列放在了输出中稍微不同的位置。这反映了该键在y
中是主键,在x
中是外键。
```
x <- tribble(
~key, ~val_x,
1, "x1",
2, "x2",
2, "x3",
1, "x4"
)
y <- tribble(
~key, ~val_y,
1, "y1",
2, "y2"
)
left_join(x, y, by = "key")
#> # A tibble: 4 x 3
#> key val_x val_y
#> <dbl> <chr> <chr>
#> 1 1 x1 y1
#> 2 2 x2 y2
#> 3 2 x3 y2
#> 4 1 x4 y1
```
-
两个表都有重复的键。这通常是一个错误,因为在这两个表中,键都不能唯一地标识观察。当你加入重复的键时,将使用笛卡尔积来展示:
x <- tribble( ~key, ~val_x, 1, "x1", 2, "x2", 2, "x3", 3, "x4" ) y <- tribble( ~key, ~val_y, 1, "y1", 2, "y2", 2, "y3", 3, "y4" ) left_join(x, y, by = "key") #> # A tibble: 6 x 3 #> key val_x val_y #> <dbl> <chr> <chr> #> 1 1 x1 y1 #> 2 2 x2 y2 #> 3 2 x2 y3 #> 4 2 x3 y2 #> 5 2 x3 y3 #> 6 3 x4 y4
4.5 定义关键列
到目前为止,这对表总是由一个变量连接,并且该变量在两个表中具有相同的名称。通过by = "key"来约定。你可以使用by
指定其他键值来连接表:
-
默认情况下
by = NULL
将使用出现在两个表中的所有变量,即所谓的自然连接。例如,航班和天气表格匹配他们共同的变量:year
,month
,day
,hour
和origin
。flights2 %>% left_join(weather) #> Joining, by = c("year", "month", "day", "hour", "origin") #> # A tibble: 336,776 x 18 #> year month day hour origin dest tailnum carrier temp dewp humid #> <int> <int> <int> <dbl> <chr> <chr> <chr> <chr> <dbl> <dbl> <dbl> #> 1 2013 1 1 5 EWR IAH N14228 UA 39.0 28.0 64.4 #> 2 2013 1 1 5 LGA IAH N24211 UA 39.9 25.0 54.8 #> 3 2013 1 1 5 JFK MIA N619AA AA 39.0 27.0 61.6 #> 4 2013 1 1 5 JFK BQN N804JB B6 39.0 27.0 61.6 #> 5 2013 1 1 6 LGA ATL N668DN DL 39.9 25.0 54.8 #> 6 2013 1 1 5 EWR ORD N39463 UA 39.0 28.0 64.4 #> # … with 336,770 more rows, and 7 more variables: wind_dir <dbl>, #> # wind_speed <dbl>, wind_gust <dbl>, precip <dbl>, pressure <dbl>, #> # visib <dbl>, time_hour <dttm>
-
通过字符向量,by = "x"。例如,航班和飞机有年份变量,但它们有不同的含义,所以我们只想通过
tailnum
连接。flights2 %>% left_join(planes, by = "tailnum") #> # A tibble: 336,776 x 16 #> year.x month day hour origin dest tailnum carrier year.y type #> <int> <int> <int> <dbl> <chr> <chr> <chr> <chr> <int> <chr> #> 1 2013 1 1 5 EWR IAH N14228 UA 1999 Fixe… #> 2 2013 1 1 5 LGA IAH N24211 UA 1998 Fixe… #> 3 2013 1 1 5 JFK MIA N619AA AA 1990 Fixe… #> 4 2013 1 1 5 JFK BQN N804JB B6 2012 Fixe… #> 5 2013 1 1 6 LGA ATL N668DN DL 1991 Fixe… #> 6 2013 1 1 5 EWR ORD N39463 UA 2012 Fixe… #> # … with 336,770 more rows, and 6 more variables: manufacturer <chr>, #> # model <chr>, engines <int>, seats <int>, speed <int>, engine <chr>
请注意,
year
变量(出现在两个输入数据框中,但不限于相等)在输出中通过后缀消除歧义。 -
通过字符向量连接:
by = c("a" = "b")
. 这将匹配表x
中变量a
和表y
中的变量b
。例如,如果我们要绘制地图,我们需要将航班数据与每个机场位置 (
lat
和lon
)的机场数据结合起来。每个航班都有一个出发地和目的地机场,因此我们需要指定要加入哪个机场:flights2 %>% left_join(airports, c("dest" = "faa")) #> # A tibble: 336,776 x 15 #> year month day hour origin dest tailnum carrier name lat lon alt #> <int> <int> <int> <dbl> <chr> <chr> <chr> <chr> <chr> <dbl> <dbl> <dbl> #> 1 2013 1 1 5 EWR IAH N14228 UA Geor… 30.0 -95.3 97 #> 2 2013 1 1 5 LGA IAH N24211 UA Geor… 30.0 -95.3 97 #> 3 2013 1 1 5 JFK MIA N619AA AA Miam… 25.8 -80.3 8 #> 4 2013 1 1 5 JFK BQN N804JB B6 <NA> NA NA NA #> 5 2013 1 1 6 LGA ATL N668DN DL Hart… 33.6 -84.4 1026 #> 6 2013 1 1 5 EWR ORD N39463 UA Chic… 42.0 -87.9 668 #> # … with 336,770 more rows, and 3 more variables: tz <dbl>, dst <chr>, #> # tzone <chr> flights2 %>% left_join(airports, c("origin" = "faa")) #> # A tibble: 336,776 x 15 #> year month day hour origin dest tailnum carrier name lat lon alt #> <int> <int> <int> <dbl> <chr> <chr> <chr> <chr> <chr> <dbl> <dbl> <dbl> #> 1 2013 1 1 5 EWR IAH N14228 UA Newa… 40.7 -74.2 18 #> 2 2013 1 1 5 LGA IAH N24211 UA La G… 40.8 -73.9 22 #> 3 2013 1 1 5 JFK MIA N619AA AA John… 40.6 -73.8 13 #> 4 2013 1 1 5 JFK BQN N804JB B6 John… 40.6 -73.8 13 #> 5 2013 1 1 6 LGA ATL N668DN DL La G… 40.8 -73.9 22 #> 6 2013 1 1 5 EWR ORD N39463 UA Newa… 40.7 -74.2 18 #> # … with 336,770 more rows, and 3 more variables: tz <dbl>, dst <chr>, #> # tzone <chr>
4.7 其他实现方式
base::merge()
可以执行所有四种变异连接类型:
dplyr包 | base::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) , |
full_join(x, y) |
merge(x, y, all.x = TRUE, all.y = TRUE) |
使用dplyr 包优点是它们更清楚地传达出代码的意图,dplyr 的连接速度要快得多,而且不会弄乱行的顺序。
dplyr 是根据SQL 语法来编译的:
dplyr | SQL |
---|---|
inner_join(x, y, by = "z") |
SELECT * FROM x INNER JOIN y USING (z) |
left_join(x, y, by = "z") |
SELECT * FROM x LEFT OUTER JOIN y USING (z) |
right_join(x, y, by = "z") |
SELECT * FROM x RIGHT OUTER JOIN y USING (z) |
full_join(x, y, by = "z") |
SELECT * FROM x FULL OUTER JOIN y USING (z) |
请注意,“INNER”和“OUTER”是可选的,经常被省略。
5 连接过滤
过滤连接与变异连接通过相同的方式匹配观察值,有两种类型:
-
semi_join(x, y)
将所有与y
匹配的观测值保存在x
中。 -
anti_join(x, y)
去掉x
中与y
匹配的所有观察值。
semi_join对于将过滤后的汇总表与原始行进行匹配非常有用。如flights数据中,你需要找到了十大最受欢迎的目的地:
top_dest <- flights %>%
count(dest, sort = TRUE) %>%
head(10)
top_dest
#> # A tibble: 10 x 2
#> dest n
#> <chr> <int>
#> 1 ORD 17283
#> 2 ATL 17215
#> 3 LAX 16174
#> 4 BOS 15508
#> 5 MCO 14082
#> 6 CLT 14064
#> # … with 4 more rows
现在,您要查找飞往这些目的地中的每个航班。通过filter过滤:
flights %>%
filter(dest %in% top_dest$dest)
#> # A tibble: 141,145 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 542 540 2 923 850
#> 2 2013 1 1 554 600 -6 812 837
#> 3 2013 1 1 554 558 -4 740 728
#> 4 2013 1 1 555 600 -5 913 854
#> 5 2013 1 1 557 600 -3 838 846
#> 6 2013 1 1 558 600 -2 753 745
#> # … with 141,139 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>
如果有多个变量时,这种方法就很复杂了。例如,假设你找到了平均延迟最高的10天。如何构造使用年、月、日将其匹配回航班的过滤器语句?
相反,您可以使用semi_join
,它像变异连接一样连接两个表,但不是添加新列,而是只保留其中x
匹配的行y
:
flights %>%
semi_join(top_dest)
#> Joining, by = "dest"
#> # A tibble: 141,145 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 542 540 2 923 850
#> 2 2013 1 1 554 600 -6 812 837
#> 3 2013 1 1 554 558 -4 740 728
#> 4 2013 1 1 555 600 -5 913 854
#> 5 2013 1 1 557 600 -3 838 846
#> 6 2013 1 1 558 600 -2 753 745
#> # … with 141,139 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>
从图形上看,semi_join()
如下所示:
只有匹配的存在行才是重要的;匹配哪个观察并不重要。这意味着过滤连接永远不会像变异连接那样重复行:
semi_join()
的反向操作是 anti_join()
。 anti_join()
保留没有匹配的行:
anti_join()
对于解决不匹配的连接很有用。例如,在连接flights
和planes
,您可能想知道在planes
中没有匹配到flights
项:
flights %>%
anti_join(planes, by = "tailnum") %>%
count(tailnum, sort = TRUE)
#> # A tibble: 722 x 2
#> tailnum n
#> <chr> <int>
#> 1 <NA> 2512
#> 2 N725MQ 575
#> 3 N722MQ 513
#> 4 N723MQ 507
#> 5 N713MQ 483
#> 6 N735MQ 396
#> # … with 716 more rows
6 集合操作
二个数据表的最后一种操作类型是集合操作。一般来说,我很少使用这些方法,但是当您想要将单个复杂的过滤器分解成更简单的部分时,它们偶尔会很有用。所有这些操作都与一个完整的行一起工作,比较每个变量的值。它们期望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
)
四种可能结果:
intersect(df1, df2)
#> # A tibble: 1 x 2
#> x y
#> <dbl> <dbl>
#> 1 1 1
# Note that we get 3 rows, not 4
union(df1, df2)
#> # A tibble: 3 x 2
#> x y
#> <dbl> <dbl>
#> 1 1 1
#> 2 2 1
#> 3 1 2
setdiff(df1, df2)
#> # A tibble: 1 x 2
#> x y
#> <dbl> <dbl>
#> 1 2 1
setdiff(df2, df1)
#> # A tibble: 1 x 2
#> x y
#> <dbl> <dbl>
#> 1 1 2