8.数据关系

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>
    

显示不同表之间关系的一种方法是使用绘图:

image

理解这样的图的关键是记住每个关系总是与一对表有关。你不需要了解整个事情;您只需要了解您感兴趣的表之间的关系链。

对于 nycflights13:

  • flights连接到planes,通过单个变量tailnum

  • flights连接到airlines,通过carrier变量。

  • flights连接到airports,通过两个origindest变量。

  • flights连接到weather的变量有origin(位置)和 yearmonthdayhour(时间)。

3 通过key连接

用于连接每对表的变量称为。对于观察的变量(或变量集)键是唯一标识符。在简单的情况下,单个变量足以识别出观察值。例如,每个飞机都有唯一标识tailnum。在其他情况下,可能需要多个变量。例如,在确定的观察weather,你需要五个变量:yearmonthdayhour,和origin

有两种类型的键:

  • 主键唯一地标识一个观察结果。例如,planes$tailnum是主键,因为它唯一标识了planes表中的每个飞机。

  • 外键唯一标识另一个表的观察结果。例如,flights$tailnum是外键,因为它出现在 flights表中,它将每个航班与飞机一对一匹配。

变量既可以是主键也可以是外键。例如,originweather表中是主键的一部分,在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数据中。您可以将airlinesflights2通过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 理解连接原理

为了帮助您了解连接的工作原理,通过图形展示:

image
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行或多行的一种方法。下图显示了作为一对直线的交点的每个潜在匹配。

image

(如果您仔细观察,您可能会注意到我们已经切换了x中的键和值列的顺序。这是为了强调连接基于进行匹配;该只是在过程中被携带。)

在实际的连接中,匹配将用点表示。点的数量=匹配的数量=输出中的行数。


image

4.2 内连接

最简单的连接类型是内连接。只要它们的键相等,内连接就会匹配观察值对:

image

(准确地说,这是一个内部等值联接,因为键是使用=运算符匹配的。由于大多数连接是等值联接,我们通常不采用该连接方法。)

内连接的输出是一个包含键、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所有观测值。
  • 全连接:保留xy所有观测值。

这些连接通过向每个表添加额外的“虚拟”观察值。这个观察值有一个总是匹配的键(如果没有其他键匹配),则填充NA

下面通过图形来解释具体原理:

image

最常用的连接是左连接:每当您从另一个表中查找附加数据时都可以使用它,因为即使没有匹配,它也会保留原始观察结果。左连接应该是默认连接方式。

描述不同连接类型的另一种方法是使用维恩图:

image

但是维恩图有个限制:维恩图无法显示当键不能唯一标识观察时会发生什么。

4.4 重复的键

前面我们遇到的数据中键都是唯一的,但是我们在处理数据时往往会碰到键不唯一,有下面两种情况:

  1. 一张数据表有重复的键。这在您想要添加附加信息时很有用,因为通常存在一对多关系。

    image

注意,我将键列放在了输出中稍微不同的位置。这反映了该键在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
```
  1. 两个表都有重复的键。这通常是一个错误,因为在这两个表中,键都不能唯一地标识观察。当你加入重复的键时,将使用笛卡尔积来展示:

    image
    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将使用出现在两个表中的所有变量,即所谓的自然连接。例如,航班和天气表格匹配他们共同的变量:yearmonthdayhourorigin

    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

    例如,如果我们要绘制地图,我们需要将航班数据与每个机场位置 (latlon)的机场数据结合起来。每个航班都有一个出发地和目的地机场,因此我们需要指定要加入哪个机场:

    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()如下所示:

image

只有匹配的存在行才是重要的;匹配哪个观察并不重要。这意味着过滤连接永远不会像变异连接那样重复行:

image

semi_join()的反向操作是 anti_join()anti_join()保留没有匹配的行:

image

anti_join()对于解决不匹配的连接很有用。例如,在连接flightsplanes,您可能想知道在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 集合操作

二个数据表的最后一种操作类型是集合操作。一般来说,我很少使用这些方法,但是当您想要将单个复杂的过滤器分解成更简单的部分时,它们偶尔会很有用。所有这些操作都与一个完整的行一起工作,比较每个变量的值。它们期望xy输入具有相同的变量,并将观察结果视为集合:

  • intersect(x, y): 只返回xy中都有的观察值。
  • union(x, y): 返回xy中合并的观察结果。
  • 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
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,839评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,543评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,116评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,371评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,384评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,111评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,416评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,053评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,558评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,007评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,117评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,756评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,324评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,315评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,539评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,578评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,877评论 2 345

推荐阅读更多精彩内容