写在前面
本系列推文为《R for Data Science (2)》的中文翻译版本。所有内容都通过开源免费的方式上传至Github,欢迎大家参与贡献,详细信息见:
Books-zh-cn 项目介绍:
Books-zh-cn:开源免费的中文书籍社区
r4ds-zh-cn Github 地址:
https://github.com/Books-zh-cn/r4ds-zh-cn
r4ds-zh-cn 网站地址:
https://books-zh-cn.github.io/r4ds-zh-cn/
目录
-
19.1 介绍
-
19.2 键
-
19.3 基础连接
19.1 介绍
数据分析很少只涉及单个数据框。 通常你会拥有多个数据框,并且必须通过连接(join)将它们合并起来,以回答你感兴趣的问题。 本章将向你介绍两种重要的连接类型:
-
Mutating joins, 从另一个数据框的匹配观测中添加新变量到一个数据框。
-
Filtering joins, 根据观测是否与另一个数据框中的观测匹配,从一个数据框中筛选观测。
我们将首先讨论键------用于在连接中关联一对数据框的变量。 然后通过检查 nycflights13 包中数据集的键来巩固理论,并利用这些知识开始连接数据框。 接着我们将讨论连接的工作原理,重点关注它们对行的操作。 最后,我们将讨论非等值连接------一类提供比默认相等关系更灵活的键匹配方式的连接。
19.1.1 先决条件
在本章中,我们将使用 dplyr 的连接函数来探索 nycflights13 中的五个相关数据集。
library(tidyverse)
library(nycflights13)
19.2 键
要理解连接,首先需要了解两个表如何通过各自内部的键对进行关联。 在本节中,你将学习两种类型的键,并查看 nycflights13 包数据集中两种键的示例。 你还将学习如何检查键是否有效,以及如果表缺少键该怎么办。
19.2.1 主键和外键
每次连接都涉及一对键:主键和外键。 主键是唯一标识每个观测的一个变量或一组变量。 当需要多个变量时,该键称为复合键。例 如,在 nycflights13 中:
-
airlines记录了每家航空公司的两条信息:其承运人代码和全称。 你可以用其两个字母的承运人代码来识别航空公司,因此carrier是主键。airlines #> # A tibble: 16 × 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. #> # ℹ 10 more rows -
airports记录了每个机场的数据。 你可以用其三个字母的机场代码来识别每个机场,因此faa是主键。airports #> # A tibble: 1,458 × 8 #> faa name lat lon alt tz dst #> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <chr> #> 1 04G Lansdowne Airport 41.1 -80.6 1044 -5 A #> 2 06A Moton Field Municipal Airport 32.5 -85.7 264 -6 A #> 3 06C Schaumburg Regional 42.0 -88.1 801 -6 A #> 4 06N Randall Airport 41.4 -74.4 523 -5 A #> 5 09J Jekyll Island Airport 31.1 -81.4 11 -5 A #> 6 0A9 Elizabethton Municipal Airpo... 36.4 -82.2 1593 -5 A #> # ℹ 1,452 more rows #> # ℹ 1 more variable: tzone <chr> -
planes记录了每架飞机的数据。 你可以用其尾号来识别飞机,因此tailnum是主键。planes #> # A tibble: 3,322 × 9 #> tailnum year type manufacturer model engines #> <chr> <int> <chr> <chr> <chr> <int> #> 1 N10156 2004 Fixed wing multi... EMBRAER EMB-145XR 2 #> 2 N102UW 1998 Fixed wing multi... AIRBUS INDUSTR... A320-214 2 #> 3 N103US 1999 Fixed wing multi... AIRBUS INDUSTR... A320-214 2 #> 4 N104UW 1999 Fixed wing multi... AIRBUS INDUSTR... A320-214 2 #> 5 N10575 2002 Fixed wing multi... EMBRAER EMB-145LR 2 #> 6 N105UW 1999 Fixed wing multi... AIRBUS INDUSTR... A320-214 2 #> # ℹ 3,316 more rows #> # ℹ 3 more variables: seats <int>, speed <int>, engine <chr> -
weather记录了起飞机场的天气数据。 你可以通过地点和时间的组合来识别每个观测,因此origin和time_hour是复合主键。weather #> # A tibble: 26,115 × 15 #> origin year month day hour temp dewp humid wind_dir #> <chr> <int> <int> <int> <int> <dbl> <dbl> <dbl> <dbl> #> 1 EWR 2013 1 1 1 39.0 26.1 59.4 270 #> 2 EWR 2013 1 1 2 39.0 27.0 61.6 250 #> 3 EWR 2013 1 1 3 39.0 28.0 64.4 240 #> 4 EWR 2013 1 1 4 39.9 28.0 62.2 250 #> 5 EWR 2013 1 1 5 39.0 28.0 64.4 260 #> 6 EWR 2013 1 1 6 37.9 28.0 67.2 240 #> # ℹ 26,109 more rows #> # ℹ 6 more variables: wind_speed <dbl>, wind_gust <dbl>, ...
外键是另一个表中与主键对应的变量(或一组变量)。 例如:
-
flights$tailnum是一个外键,对应主键planes$tailnum. -
flights$carrier是一个外键,对应主键airlines$carrier. -
flights$origin是一个外键,对应主键airports$faa. -
flights$dest是一个外键,对应主键airports$faa. -
flights$origin-flights$time_hour是一个复合外键,对应复合主键weather$origin-weather$time_hour.
这些关系在 Figure 19.1 中进行了可视化总结。

Figure 19.1: nycflights13 包中所有五个数据帧之间的连接。构成主键的变量为灰色,并用箭头连接到相应的外键。
你会注意到这些键设计中的一个优点:主键和外键几乎总是具有相同的名称,这(正如你稍后将看到的)将使连接操作更加简便。 同样值得注意的是相反的关系:在多个表中使用的变量名几乎在每个地方都有相同的含义。 只有一个例外:year 在 flights 中指出发年份,在 planes 中指制造年份。 当我们真正开始连接表时,这一点将变得非常重要。
19.2.2 检查主键
现在我们已经识别出每个表中的主键,最好验证它们是否确实能唯一标识每个观测。 一种方法是使用 count() 对主键进行计数,并查找 n 大于 1 的记录。 这表明 planes 和 weather 的主键情况良好:
planes |>
count(tailnum) |>
filter(n > 1)
#> # A tibble: 0 × 2
#> # ℹ 2 variables: tailnum <chr>, n <int>
weather |>
count(time_hour, origin) |>
filter(n > 1)
#> # A tibble: 0 × 3
#> # ℹ 3 variables: time_hour <dttm>, origin <chr>, n <int>
你还应检查主键中是否存在缺失值------如果某个值是缺失的,它就无法标识一个观测!
planes |>
filter(is.na(tailnum))
#> # A tibble: 0 × 9
#> # ℹ 9 variables: tailnum <chr>, year <int>, type <chr>, manufacturer <chr>,
#> # model <chr>, engines <int>, seats <int>, speed <int>, engine <chr>
weather |>
filter(is.na(time_hour) | is.na(origin))
#> # A tibble: 0 × 15
#> # ℹ 15 variables: origin <chr>, year <int>, month <int>, day <int>,
#> # hour <int>, temp <dbl>, dewp <dbl>, humid <dbl>, wind_dir <dbl>, ...
19.2.3 代理键
到目前为止,我们还没有讨论 flights 的主键。 这里它并不是特别重要,因为没有其他数据框将其用作外键,但考虑它仍然有用,因为如果我们有某种方式向他人描述观测,处理它们会更容易。
经过一些思考和试验,我们确定有三个变量共同唯一标识每个航班:
flights |>
count(time_hour, carrier, flight) |>
filter(n > 1)
#> # A tibble: 0 × 4
#> # ℹ 4 variables: time_hour <dttm>, carrier <chr>, flight <int>, n <int>
没有重复记录是否自动使 time_hour-carrier-flight 成为主键? 这当然是个好的起点,但并不能保证。 例如,海拔和纬度是 airports 的良好主键吗?
airports |>
count(alt, lat) |>
filter(n > 1)
#> # A tibble: 1 × 3
#> alt lat n
#> <dbl> <dbl> <int>
#> 1 13 40.6 2
显然,通过海拔和纬度来标识机场是个糟糕的主意,而且通常仅从数据本身无法判断一组变量能否构成一个好的主键。 但对于航班来说,time_hour, carrier 和 flight 的组合似乎是合理的,因为如果同一时间空中有多个相同航班号的航班,航空公司和乘客都会非常困惑。
也就是说,使用行号引入一个简单的数值代理键可能更好:
flights2 <- flights |>
mutate(id = row_number(), .before = 1)
flights2
#> # A tibble: 336,776 × 20
#> id year month day dep_time sched_dep_time dep_delay arr_time
#> <int> <int> <int> <int> <int> <int> <dbl> <int>
#> 1 1 2013 1 1 517 515 2 830
#> 2 2 2013 1 1 533 529 4 850
#> 3 3 2013 1 1 542 540 2 923
#> 4 4 2013 1 1 544 545 -1 1004
#> 5 5 2013 1 1 554 600 -6 812
#> 6 6 2013 1 1 554 558 -4 740
#> # ℹ 336,770 more rows
#> # ℹ 12 more variables: sched_arr_time <int>, arr_delay <dbl>, ...
代理键在与他人沟通时特别有用:告诉某人查看航班 2001,比说查看 2013年1月3日上午9点起飞的 UA430 航班要容易得多。
19.2.4 练习
-
我们在 Figure 19.1 中忘记了绘制
weather和airports之间的关系。 它们的关系是什么?在 图中应该如何表示? -
weather只包含纽约三个起飞机场的信息。 如果它包含美国所有机场的天气记录,那么它会与flights建立什么额外的连接? -
year,month,day,hour和origin这些变量几乎构成了weather的复合键,但有一个小时存在重复观测。 你能找出那个小时有什么特殊之处吗? -
我们知道一年中有些特殊日子(例如平安夜和圣诞节)的乘客数量比平时少。 你如何将这种数据表示为一个数据框? 主键会是什么? 它将如何与现有的数据框连接?
-
绘制一张图说明 Lahman 包中
Batting、People和Salaries数据框之间的连接关系。 再绘制另一张图展示People,Managers,AwardsManagers之间的关系。 你如何描述Batting,Pitching和Fielding数据框之间的关系?
19.3 基础连接
现在你已经理解了数据框如何通过键连接,我们可以开始使用连接来更好地理解 flights 数据集。 dplyr 提供了六个连接函数:left_join(), inner_join(), right_join(), full_join(), semi_join() 和 anti_join()。它 们都有相同的接口:接收一对数据框(x 和 y)并返回一个数据框。 输出中的行和列顺序主要由 x 决定。
在本节中,你将学习如何使用一个变异连接 left_join() 以及两个筛选连接 semi_join() 和 anti_join()。 下一节你将详细了解这些函数的工作原理,以及剩余的 inner_join(), right_join() 和 full_join()。
19.3.1 变异连接
变异连接(mutating join )允许你合并两个数据框的变量:它首先通过键匹配观测,然后将变量从一个数据框复制到另一个。 与 mutate() 类似,连接函数会将变量添加到右侧,因此如果你的数据集有很多变量,你可能看不到新增的变量。 为了示例清晰,我们将创建一个仅包含六个变量的简化数据集:
flights2 <- flights |>
select(year, time_hour, origin, dest, tailnum, carrier)
flights2
#> # A tibble: 336,776 × 6
#> year time_hour origin dest tailnum carrier
#> <int> <dttm> <chr> <chr> <chr> <chr>
#> 1 2013 2013-01-01 05:00:00 EWR IAH N14228 UA
#> 2 2013 2013-01-01 05:00:00 LGA IAH N24211 UA
#> 3 2013 2013-01-01 05:00:00 JFK MIA N619AA AA
#> 4 2013 2013-01-01 05:00:00 JFK BQN N804JB B6
#> 5 2013 2013-01-01 06:00:00 LGA ATL N668DN DL
#> 6 2013 2013-01-01 05:00:00 EWR ORD N39463 UA
#> # ℹ 336,770 more rows
变异连接有四种类型,但有一种你几乎会一直使用:left_join()。 它的特殊之处在于输出始终与 x 保持相同的行数。left_join() 的主要用途是添加额外的元数据。 例如,我们可以使用 left_join() 将完整的航空公司名称添加到 flights2 数据中:
flights2 |>
left_join(airlines)
#> Joining with `by = join_by(carrier)`
#> # A tibble: 336,776 × 7
#> year time_hour origin dest tailnum carrier name
#> <int> <dttm> <chr> <chr> <chr> <chr> <chr>
#> 1 2013 2013-01-01 05:00:00 EWR IAH N14228 UA United Air Lines In...
#> 2 2013 2013-01-01 05:00:00 LGA IAH N24211 UA United Air Lines In...
#> 3 2013 2013-01-01 05:00:00 JFK MIA N619AA AA American Airlines I...
#> 4 2013 2013-01-01 05:00:00 JFK BQN N804JB B6 JetBlue Airways
#> 5 2013 2013-01-01 06:00:00 LGA ATL N668DN DL Delta Air Lines Inc.
#> 6 2013 2013-01-01 05:00:00 EWR ORD N39463 UA United Air Lines In...
#> # ℹ 336,770 more rows
或者我们可以找出每架飞机起飞时的温度和风速:
flights2 |>
left_join(weather |> select(origin, time_hour, temp, wind_speed))
#> Joining with `by = join_by(time_hour, origin)`
#> # A tibble: 336,776 × 8
#> year time_hour origin dest tailnum carrier temp wind_speed
#> <int> <dttm> <chr> <chr> <chr> <chr> <dbl> <dbl>
#> 1 2013 2013-01-01 05:00:00 EWR IAH N14228 UA 39.0 12.7
#> 2 2013 2013-01-01 05:00:00 LGA IAH N24211 UA 39.9 15.0
#> 3 2013 2013-01-01 05:00:00 JFK MIA N619AA AA 39.0 15.0
#> 4 2013 2013-01-01 05:00:00 JFK BQN N804JB B6 39.0 15.0
#> 5 2013 2013-01-01 06:00:00 LGA ATL N668DN DL 39.9 16.1
#> 6 2013 2013-01-01 05:00:00 EWR ORD N39463 UA 39.0 12.7
#> # ℹ 336,770 more rows
或者飞机的大小;
flights2 |>
left_join(planes |> select(tailnum, type, engines, seats))
#> Joining with `by = join_by(tailnum)`
#> # A tibble: 336,776 × 9
#> year time_hour origin dest tailnum carrier type
#> <int> <dttm> <chr> <chr> <chr> <chr> <chr>
#> 1 2013 2013-01-01 05:00:00 EWR IAH N14228 UA Fixed wing multi en...
#> 2 2013 2013-01-01 05:00:00 LGA IAH N24211 UA Fixed wing multi en...
#> 3 2013 2013-01-01 05:00:00 JFK MIA N619AA AA Fixed wing multi en...
#> 4 2013 2013-01-01 05:00:00 JFK BQN N804JB B6 Fixed wing multi en...
#> 5 2013 2013-01-01 06:00:00 LGA ATL N668DN DL Fixed wing multi en...
#> 6 2013 2013-01-01 05:00:00 EWR ORD N39463 UA Fixed wing multi en...
#> # ℹ 336,770 more rows
#> # ℹ 2 more variables: engines <int>, seats <int>
当 left_join() 无法为 x 中的某行找到匹配项时,它会用缺失值填充新变量。 例如,没有关于尾号为 N3ALAA 的飞机的信息,因此 type, engines 和 seats 将显示为缺失值:
flights2 |>
filter(tailnum == "N3ALAA") |>
left_join(planes |> select(tailnum, type, engines, seats))
#> Joining with `by = join_by(tailnum)`
#> # A tibble: 63 × 9
#> year time_hour origin dest tailnum carrier type engines seats
#> <int> <dttm> <chr> <chr> <chr> <chr> <chr> <int> <int>
#> 1 2013 2013-01-01 06:00:00 LGA ORD N3ALAA AA <NA> NA NA
#> 2 2013 2013-01-02 18:00:00 LGA ORD N3ALAA AA <NA> NA NA
#> 3 2013 2013-01-03 06:00:00 LGA ORD N3ALAA AA <NA> NA NA
#> 4 2013 2013-01-07 19:00:00 LGA ORD N3ALAA AA <NA> NA NA
#> 5 2013 2013-01-08 17:00:00 JFK ORD N3ALAA AA <NA> NA NA
#> 6 2013 2013-01-16 06:00:00 LGA ORD N3ALAA AA <NA> NA NA
#> # ℹ 57 more rows
本章后续部分我们会多次回到这个问题。
19.3.2 指定连接键
默认情况下,left_join() 会使用两个数据框中同时出现的所有变量作为连接键,即所谓的自然连接。 这是一种实用的启发式方法,但并不总是有效。 例如,如果我们尝试将 flights2 与完整的 planes 数据集连接会发生什么?
flights2 |>
left_join(planes)
#> Joining with `by = join_by(year, tailnum)`
#> # A tibble: 336,776 × 13
#> year time_hour origin dest tailnum carrier type manufacturer
#> <int> <dttm> <chr> <chr> <chr> <chr> <chr> <chr>
#> 1 2013 2013-01-01 05:00:00 EWR IAH N14228 UA <NA> <NA>
#> 2 2013 2013-01-01 05:00:00 LGA IAH N24211 UA <NA> <NA>
#> 3 2013 2013-01-01 05:00:00 JFK MIA N619AA AA <NA> <NA>
#> 4 2013 2013-01-01 05:00:00 JFK BQN N804JB B6 <NA> <NA>
#> 5 2013 2013-01-01 06:00:00 LGA ATL N668DN DL <NA> <NA>
#> 6 2013 2013-01-01 05:00:00 EWR ORD N39463 UA <NA> <NA>
#> # ℹ 336,770 more rows
#> # ℹ 5 more variables: model <chr>, engines <int>, seats <int>, ...
由于我们的连接试图将 tailnum 和 year 作为复合键使用,导致大量匹配失败。flights 和 planes 都有 year 列,但含义不同:flights$year 是航班发生的年份,而 planes$year 是飞机的制造年份。 我们只想通过 tailnum 进行连接,因此需要使用 join_by() 提供明确的指定:
flights2 |>
left_join(planes, join_by(tailnum))
#> # A tibble: 336,776 × 14
#> year.x time_hour origin dest tailnum carrier year.y
#> <int> <dttm> <chr> <chr> <chr> <chr> <int>
#> 1 2013 2013-01-01 05:00:00 EWR IAH N14228 UA 1999
#> 2 2013 2013-01-01 05:00:00 LGA IAH N24211 UA 1998
#> 3 2013 2013-01-01 05:00:00 JFK MIA N619AA AA 1990
#> 4 2013 2013-01-01 05:00:00 JFK BQN N804JB B6 2012
#> 5 2013 2013-01-01 06:00:00 LGA ATL N668DN DL 1991
#> 6 2013 2013-01-01 05:00:00 EWR ORD N39463 UA 2012
#> # ℹ 336,770 more rows
#> # ℹ 7 more variables: type <chr>, manufacturer <chr>, model <chr>, ...
请注意,输出中的 year 变量通过后缀(year.x 和 year.y)进行了区分,这告诉你变量来自 x 还是 y 参数。 你可以使用 suffix 参数覆盖默认的后缀。
join_by(tailnum) 是 join_by(tailnum == tailnum) 的简写。 了解这种完整形式很重要,原因有二。 首先,它描述了两个表之间的关系:键必须相等。 这就是为什么这种连接类型通常被称为等值连接。 你将在 Section 19.5 中学习非等值连接。
其次,它允许你为每个表指定不同的连接键。 例如,连接 flight2 和 airports 表有两种方式:通过 dest 或 origin:
flights2 |>
left_join(airports, join_by(dest == faa))
#> # A tibble: 336,776 × 13
#> year time_hour origin dest tailnum carrier name
#> <int> <dttm> <chr> <chr> <chr> <chr> <chr>
#> 1 2013 2013-01-01 05:00:00 EWR IAH N14228 UA George Bush Interco...
#> 2 2013 2013-01-01 05:00:00 LGA IAH N24211 UA George Bush Interco...
#> 3 2013 2013-01-01 05:00:00 JFK MIA N619AA AA Miami Intl
#> 4 2013 2013-01-01 05:00:00 JFK BQN N804JB B6 <NA>
#> 5 2013 2013-01-01 06:00:00 LGA ATL N668DN DL Hartsfield Jackson ...
#> 6 2013 2013-01-01 05:00:00 EWR ORD N39463 UA Chicago Ohare Intl
#> # ℹ 336,770 more rows
#> # ℹ 6 more variables: lat <dbl>, lon <dbl>, alt <dbl>, tz <dbl>, ...
flights2 |>
left_join(airports, join_by(origin == faa))
#> # A tibble: 336,776 × 13
#> year time_hour origin dest tailnum carrier name
#> <int> <dttm> <chr> <chr> <chr> <chr> <chr>
#> 1 2013 2013-01-01 05:00:00 EWR IAH N14228 UA Newark Liberty Intl
#> 2 2013 2013-01-01 05:00:00 LGA IAH N24211 UA La Guardia
#> 3 2013 2013-01-01 05:00:00 JFK MIA N619AA AA John F Kennedy Intl
#> 4 2013 2013-01-01 05:00:00 JFK BQN N804JB B6 John F Kennedy Intl
#> 5 2013 2013-01-01 06:00:00 LGA ATL N668DN DL La Guardia
#> 6 2013 2013-01-01 05:00:00 EWR ORD N39463 UA Newark Liberty Intl
#> # ℹ 336,770 more rows
#> # ℹ 6 more variables: lat <dbl>, lon <dbl>, alt <dbl>, tz <dbl>, ...
在旧代码中,你可能会看到另一种指定连接键的方式,即使用字符向量:
-
by = "x"对应join_by(x)。 -
by = c("a" = "x")对应join_by(a == x)。
既然现在有了 join_by(),我们更倾向于使用它,因为它提供了更清晰、更灵活的规范。
inner_join(), right_join(), full_join() 与 left_join() 具有相同的接口。 区别在于它们保留哪些行:左连接保留 x 中的所有行,右连接保留 y 中的所有行,全连接保留 x 或 y 中的所有行,而内连接只保留同时出现在x 和 y 中的行。 我们稍后会详细讨论这些。
19.3.3 过滤连接
正如你可能猜到的,筛选连接的主要功能是筛选行。 它有两种类型:半连接和反连接。 半连接(Semi-joins )保留 x 中所有在 y 中有匹配的行。 例如,我们可以使用半连接来筛选 airports 数据集,仅显示起飞机场:
airports |>
semi_join(flights2, join_by(faa == origin))
#> # A tibble: 3 × 8
#> faa name lat lon alt tz dst tzone
#> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <chr> <chr>
#> 1 EWR Newark Liberty Intl 40.7 -74.2 18 -5 A America/New_York
#> 2 JFK John F Kennedy Intl 40.6 -73.8 13 -5 A America/New_York
#> 3 LGA La Guardia 40.8 -73.9 22 -5 A America/New_York
或仅显示目的地机场:
airports |>
semi_join(flights2, join_by(faa == dest))
#> # A tibble: 101 × 8
#> faa name lat lon alt tz dst tzone
#> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <chr> <chr>
#> 1 ABQ Albuquerque Internati... 35.0 -107. 5355 -7 A America/Denver
#> 2 ACK Nantucket Mem 41.3 -70.1 48 -5 A America/New_Yo...
#> 3 ALB Albany Intl 42.7 -73.8 285 -5 A America/New_Yo...
#> 4 ANC Ted Stevens Anchorage... 61.2 -150. 152 -9 A America/Anchor...
#> 5 ATL Hartsfield Jackson At... 33.6 -84.4 1026 -5 A America/New_Yo...
#> 6 AUS Austin Bergstrom Intl 30.2 -97.7 542 -6 A America/Chicago
#> # ℹ 95 more rows
反连接(Anti-joins )则相反:它们返回 x 中所有在 y 中没有匹配的行。 它们对于查找数据中隐式的缺失值非常有用,这是 Section 18.3 的主题。 隐式缺失值不会显示为 NAs,而仅表现为不存在。 例如,我们可以通过查找没有匹配目的机场的航班,来发现 airports 中缺失的行:
flights2 |>
anti_join(airports, join_by(dest == faa)) |>
distinct(dest)
#> # A tibble: 4 × 1
#> dest
#> <chr>
#> 1 BQN
#> 2 SJU
#> 3 STT
#> 4 PSE
或者我们可以查找哪些 tailnums 在 planes 中缺失:
flights2 |>
anti_join(planes, join_by(tailnum)) |>
distinct(tailnum)
#> # A tibble: 722 × 1
#> tailnum
#> <chr>
#> 1 N3ALAA
#> 2 N3DUAA
#> 3 N542MQ
#> 4 N730MQ
#> 5 N9EAMQ
#> 6 N532UA
#> # ℹ 716 more rows
19.3.4 练习
-
找出全年中最具延误的 48 小时。 与
weather数据进行交叉比对。 你能发现任何规律吗? -
假设你已使用以下代码找出前 10 个最受欢迎的目的地:
top_dest <- flights2 |> count(dest, sort = TRUE) |> head(10)你如何查找所有飞往这些目的地的航班?
-
每个起飞的航班是否都有对应小时的天气数据?
-
在
planes中没有匹配记录的尾号有何共同点? (提示:一个变量可解释 ~90% 的问题。) -
在
planes中添加一列,列出曾使用该飞机的每个carrier(航空公司)。 你可能认为飞机与航空公司之间存在隐式关系,因为每架飞机只由一家航空公司运营。 请使用之前章节学到的工具验证或否定这一假设。 -
将起飞机场和目的地机场的经纬度添加到
flights中。 更简便的做法是在连接前还是连接后重命名列? -
计算目的地的平均延误时间,然后连接
airports数据框,以便展示延误的空间分布。 以下是绘制美国地图的简单方法:airports |> semi_join(flights, join_by(faa == dest)) |> ggplot(aes(x = lon, y = lat)) + borders("state") + geom_point() + coord_quickmap()你可能希望用点的
size或color来显示每个机场的平均延误。 -
2013 年 6 月 13 日发生了什么? 绘制延误分布地图,并使用谷歌搜索与天气数据进行交叉比对。
worst <- filter(flights, !is.na(dep_time), month == 6, day == 13)
worst |>
group_by(dest) |>
summarize(delay = mean(arr_delay), n = n()) |>
filter(n > 5) |>
inner_join(airports, by = c("dest" = "faa")) |>
ggplot(aes(x = lon, y = lat)) +
borders("state") +
geom_point(aes(size = n, color = delay)) +
coord_quickmap()
-
记住,在 RStudio 中,你也可以使用
View()来避免这个问题。 -
这并不是百分百正确,但如果不是,你就会得到警告。
--------------- 未完待续 ---------------
本期翻译贡献:
@TigerZ生信宝库
注:本文已开启快捷转载,欢迎大家转载,只需标明文章出处即可。