《R for Data Science (2e)》免费中文翻译 (第19章) --- Joins(1)

写在前面

本系列推文为《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 记录了起飞机场的天气数据。 你可以通过地点和时间的组合来识别每个观测,因此 origintime_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 包中所有五个数据帧之间的连接。构成主键的变量为灰色,并用箭头连接到相应的外键。

你会注意到这些键设计中的一个优点:主键和外键几乎总是具有相同的名称,这(正如你稍后将看到的)将使连接操作更加简便。 同样值得注意的是相反的关系:在多个表中使用的变量名几乎在每个地方都有相同的含义。 只有一个例外:yearflights 中指出发年份,在 planes 中指制造年份。 当我们真正开始连接表时,这一点将变得非常重要。

19.2.2 检查主键

现在我们已经识别出每个表中的主键,最好验证它们是否确实能唯一标识每个观测。 一种方法是使用 count() 对主键进行计数,并查找 n 大于 1 的记录。 这表明 planesweather 的主键情况良好:

复制代码
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, carrierflight 的组合似乎是合理的,因为如果同一时间空中有多个相同航班号的航班,航空公司和乘客都会非常困惑。

也就是说,使用行号引入一个简单的数值代理键可能更好:

复制代码
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 练习

  1. 我们在 Figure 19.1 中忘记了绘制 weatherairports 之间的关系。 它们的关系是什么?在 图中应该如何表示?

  2. weather 只包含纽约三个起飞机场的信息。 如果它包含美国所有机场的天气记录,那么它会与 flights 建立什么额外的连接?

  3. year, month, day, hourorigin 这些变量几乎构成了 weather 的复合键,但有一个小时存在重复观测。 你能找出那个小时有什么特殊之处吗?

  4. 我们知道一年中有些特殊日子(例如平安夜和圣诞节)的乘客数量比平时少。 你如何将这种数据表示为一个数据框? 主键会是什么? 它将如何与现有的数据框连接?

  5. 绘制一张图说明 Lahman 包中 BattingPeopleSalaries 数据框之间的连接关系。 再绘制另一张图展示 People, Managers, AwardsManagers 之间的关系。 你如何描述 Batting, PitchingFielding 数据框之间的关系?

19.3 基础连接

现在你已经理解了数据框如何通过键连接,我们可以开始使用连接来更好地理解 flights 数据集。 dplyr 提供了六个连接函数:left_join(), inner_join(), right_join(), full_join(), semi_join()anti_join()。它 们都有相同的接口:接收一对数据框(xy)并返回一个数据框。 输出中的行和列顺序主要由 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, enginesseats 将显示为缺失值:

复制代码
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>, ...

由于我们的连接试图将 tailnumyear 作为复合键使用,导致大量匹配失败。flightsplanes 都有 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.xyear.y)进行了区分,这告诉你变量来自 x 还是 y 参数。 你可以使用 suffix 参数覆盖默认的后缀。

join_by(tailnum)join_by(tailnum == tailnum) 的简写。 了解这种完整形式很重要,原因有二。 首先,它描述了两个表之间的关系:键必须相等。 这就是为什么这种连接类型通常被称为等值连接。 你将在 Section 19.5 中学习非等值连接。

其次,它允许你为每个表指定不同的连接键。 例如,连接 flight2airports 表有两种方式:通过 destorigin

复制代码
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 中的所有行,全连接保留 xy 中的所有行,而内连接只保留同时出现在xy 中的行。 我们稍后会详细讨论这些。

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 练习

  1. 找出全年中最具延误的 48 小时。 与 weather 数据进行交叉比对。 你能发现任何规律吗?

  2. 假设你已使用以下代码找出前 10 个最受欢迎的目的地:

    复制代码
    top_dest <- flights2 |>
      count(dest, sort = TRUE) |>
      head(10)

    你如何查找所有飞往这些目的地的航班?

  3. 每个起飞的航班是否都有对应小时的天气数据?

  4. planes 中没有匹配记录的尾号有何共同点? (提示:一个变量可解释 ~90% 的问题。)

  5. planes 中添加一列,列出曾使用该飞机的每个 carrier(航空公司)。 你可能认为飞机与航空公司之间存在隐式关系,因为每架飞机只由一家航空公司运营。 请使用之前章节学到的工具验证或否定这一假设。

  6. 将起飞机场和目的地机场的经纬度添加到 flights 中。 更简便的做法是在连接前还是连接后重命名列?

  7. 计算目的地的平均延误时间,然后连接 airports 数据框,以便展示延误的空间分布。 以下是绘制美国地图的简单方法:

    复制代码
    airports |>
      semi_join(flights, join_by(faa == dest)) |>
      ggplot(aes(x = lon, y = lat)) +
        borders("state") +
        geom_point() +
        coord_quickmap()

    你可能希望用点的 sizecolor 来显示每个机场的平均延误。

  8. 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()


  1. 记住,在 RStudio 中,你也可以使用 View() 来避免这个问题。

  2. 这并不是百分百正确,但如果不是,你就会得到警告。

--------------- 未完待续 ---------------

本期翻译贡献:

  • @TigerZ生信宝库

注:本文已开启快捷转载,欢迎大家转载,只需标明文章出处即可。

相关推荐
Tiger Z3 小时前
《R for Data Science (2e)》免费中文翻译 (第18章) --- Missing values
开发语言·r语言
带我去滑雪5 小时前
R语言抑郁症状网络分析
r语言
AC赳赳老秦7 小时前
R语言数据分析:DeepSeek辅助生成统计建模代码与可视化图表
开发语言·人工智能·jmeter·数据挖掘·数据分析·r语言·deepseek
czliutz4 天前
R语言gm玩音乐示例代码Rmarkdown
开发语言·r语言
LASDAaaa12315 天前
【计算机视觉】基于Mask R-CNN的自动扶梯缺陷检测方法实现
计算机视觉·r语言·cnn
xp4758063805 天前
陪诊公司是什么?绿通在北京陪诊服务中的作用是什么?
游戏·编程
酬勤-人间道5 天前
XPlote3DGenie 2.1.1.0:实用 3D 数据处理工具,百度网盘可直接安装
c++·3d·gis·编程·计算机软件·岩土
没有梦想的咸鱼185-1037-16635 天前
AI大模型支持下的:R-Meta分析核心技术:从热点挖掘到高级模型、助力高效科研与论文发表
开发语言·人工智能·机器学习·chatgpt·数据分析·r语言·ai写作
2501_941333105 天前
表格结构识别与内容解析——基于Cascade R-CNN的表格行、列、单元格自动检测与分类_1
分类·r语言·cnn