2017年11月28日

关联数据

通常来说,做数据分析仅涉及一个数据框的情况比较少。更多的时候,你需要关联整合多个数据框,去回答你所关心的问题。
本次课程依然使用dplyrnycflights13包。

library(dplyr)
library(nycflights13)
Warning: package 'nycflights13' was built under R version 3.4.2

之前我们用到了flights数据框,其实nycflights13包里还有另外一些相关的数据框。
比如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.
 7      F9      Frontier Airlines Inc.
 8      FL AirTran Airways Corporation
 9      HA      Hawaiian Airlines Inc.
10      MQ                   Envoy Air
11      OO       SkyWest Airlines Inc.
12      UA       United Air Lines Inc.
13      US             US Airways Inc.
14      VX              Virgin America
15      WN      Southwest Airlines Co.
16      YV          Mesa Airlines Inc.

airports数据框给出了机场的全称、经纬度等信息。

airports
# A tibble: 1,458 x 8
     faa                           name      lat        lon   alt    tz
   <chr>                          <chr>    <dbl>      <dbl> <int> <dbl>
 1   04G              Lansdowne Airport 41.13047  -80.61958  1044    -5
 2   06A  Moton Field Municipal Airport 32.46057  -85.68003   264    -6
 3   06C            Schaumburg Regional 41.98934  -88.10124   801    -6
 4   06N                Randall Airport 41.43191  -74.39156   523    -5
 5   09J          Jekyll Island Airport 31.07447  -81.42778    11    -5
 6   0A9 Elizabethton Municipal Airport 36.37122  -82.17342  1593    -5
 7   0G6        Williams County Airport 41.46731  -84.50678   730    -5
 8   0G7  Finger Lakes Regional Airport 42.88356  -76.78123   492    -5
 9   0P2   Shoestring Aviation Airfield 39.79482  -76.64719  1000    -5
10   0S9          Jefferson County Intl 48.05381 -122.81064   108    -8
# ... with 1,448 more rows, and 2 more variables: dst <chr>, tzone <chr>

planes数据框给出了飞机信息。

planes
# A tibble: 3,322 x 9
   tailnum  year                    type     manufacturer     model
     <chr> <int>                   <chr>            <chr>     <chr>
 1  N10156  2004 Fixed wing multi engine          EMBRAER EMB-145XR
 2  N102UW  1998 Fixed wing multi engine AIRBUS INDUSTRIE  A320-214
 3  N103US  1999 Fixed wing multi engine AIRBUS INDUSTRIE  A320-214
 4  N104UW  1999 Fixed wing multi engine AIRBUS INDUSTRIE  A320-214
 5  N10575  2002 Fixed wing multi engine          EMBRAER EMB-145LR
 6  N105UW  1999 Fixed wing multi engine AIRBUS INDUSTRIE  A320-214
 7  N107US  1999 Fixed wing multi engine AIRBUS INDUSTRIE  A320-214
 8  N108UW  1999 Fixed wing multi engine AIRBUS INDUSTRIE  A320-214
 9  N109UW  1999 Fixed wing multi engine AIRBUS INDUSTRIE  A320-214
10  N110UW  1999 Fixed wing multi engine AIRBUS INDUSTRIE  A320-214
# ... with 3,312 more rows, and 4 more variables: engines <int>,
#   seats <int>, speed <int>, engine <chr>

weather数据框给出了纽约3个机场每小时的天气信息。

weather
# A tibble: 26,130 x 15
   origin  year month   day  hour  temp  dewp humid wind_dir wind_speed
    <chr> <dbl> <dbl> <int> <int> <dbl> <dbl> <dbl>    <dbl>      <dbl>
 1    EWR  2013     1     1     0 37.04 21.92 53.97      230   10.35702
 2    EWR  2013     1     1     1 37.04 21.92 53.97      230   13.80936
 3    EWR  2013     1     1     2 37.94 21.92 52.09      230   12.65858
 4    EWR  2013     1     1     3 37.94 23.00 54.51      230   13.80936
 5    EWR  2013     1     1     4 37.94 24.08 57.04      240   14.96014
 6    EWR  2013     1     1     6 39.02 26.06 59.37      270   10.35702
 7    EWR  2013     1     1     7 39.02 26.96 61.63      250    8.05546
 8    EWR  2013     1     1     8 39.02 28.04 64.43      240   11.50780
 9    EWR  2013     1     1     9 39.92 28.04 62.21      250   12.65858
10    EWR  2013     1     1    10 39.02 28.04 64.43      260   12.65858
# ... with 26,120 more rows, and 5 more variables: wind_gust <dbl>,
#   precip <dbl>, pressure <dbl>, visib <dbl>, time_hour <dttm>

各数据框之间的关系如下图。

联系两个数据框的变量叫做关键字(keys),关键字是可以唯一确定一条记录的变量。

  • 简单的情况,一个变量就能够确定一条记录,比如飞机可以由tailnum唯一确定。

  • 复杂的情况,需要有多个变量才能唯一确定一条记录,比如天气需要由yearmonthdayhourorigin组合才能确定。

关键字可以分为两类:

  • 主关键字 (primary key):也叫主键,是确定一条记录的唯一标识。比如planes$tailnumplanes数据框的主键。

  • 外关键字 (foreign key):也叫外键,用于与另一张表的关联。比如flights$tailnumflights数据框的外键。

主键和其在另外数据框中相应的外键组成一个关联。关联通常是一对多的。比如一个航班对应一架飞机,一架飞机对应多个航班。另外也有一对一的关联,是一对多的关联的特殊情况。

一旦确定了数据框的主键,建议校核主键能否唯一对应一条记录。

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: 0 x 6
# ... with 6 variables: year <dbl>, month <dbl>, day <int>, hour <int>,
#   origin <chr>, n <int>

连接两个数据框

内连接inner_join()

左连接left_join() 右连接right_join() 全连接full_join()

以上所有的关联中,数据框的关键字是唯一的。更多的时候,数据框的关键字可能并不唯一。
比如其中一个数据框的关键字存在重复,这是一种典型的一对多关联。通常是需要向该数据框添加额外的信息。

注意key在y前,表明key是y的主键。key在x后,表明key是x的外键。

如果两个数据框的关键字都存在重复,这是一种多对多的关联,输出结果是所有可能的组合。这种情况在实际中用处很少,需要特别小心。

默认情况下使用所有同时出现在两个数据框中的变量作为关键字进行关联by = NULL
比如关联flight数据框和weather数据框,同时出现的变量有yearmonthdayoriginhourtime_hour

flights %>% left_join(weather)
Joining, by = c("year", "month", "day", "origin", "hour", "time_hour")
# A tibble: 336,776 x 28
    year month   day dep_time sched_dep_time dep_delay arr_time
   <dbl> <dbl> <int>    <int>          <int>     <dbl>    <int>
 1  2013     1     1      517            515         2      830
 2  2013     1     1      533            529         4      850
 3  2013     1     1      542            540         2      923
 4  2013     1     1      544            545        -1     1004
 5  2013     1     1      554            600        -6      812
 6  2013     1     1      554            558        -4      740
 7  2013     1     1      555            600        -5      913
 8  2013     1     1      557            600        -3      709
 9  2013     1     1      557            600        -3      838
10  2013     1     1      558            600        -2      753
# ... with 336,766 more rows, and 21 more variables: sched_arr_time <int>,
#   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>, temp <dbl>, dewp <dbl>, humid <dbl>,
#   wind_dir <dbl>, wind_speed <dbl>, wind_gust <dbl>, precip <dbl>,
#   pressure <dbl>, visib <dbl>

人为指定关键字进行关联,即仅选择部分同时出现在两个数据框中的变量作为关键字进行关联by = "x"
比如flights数据框和planes数据框都有year变量,但含义不同,所以我们仅用tailnum变量作为关键字进行关联。
注意同时出现在在两个数据框中的year变量在输出结果中分别使用了下标进行区分。

flights %>% left_join(planes, by = "tailnum")
# A tibble: 336,776 x 27
   year.x month   day dep_time sched_dep_time dep_delay arr_time
    <int> <int> <int>    <int>          <int>     <dbl>    <int>
 1   2013     1     1      517            515         2      830
 2   2013     1     1      533            529         4      850
 3   2013     1     1      542            540         2      923
 4   2013     1     1      544            545        -1     1004
 5   2013     1     1      554            600        -6      812
 6   2013     1     1      554            558        -4      740
 7   2013     1     1      555            600        -5      913
 8   2013     1     1      557            600        -3      709
 9   2013     1     1      557            600        -3      838
10   2013     1     1      558            600        -2      753
# ... with 336,766 more rows, and 20 more variables: sched_arr_time <int>,
#   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>, year.y <int>, type <chr>,
#   manufacturer <chr>, model <chr>, engines <int>, seats <int>,
#   speed <int>, engine <chr>

如果需要关联的关键字在两个数据框中的变量名不同,可以使用by = c("a" = "b")进行关联。
比如绘制飞行航线图,需要在flight数据框中关联出发机场和到达机场的经纬度。

airports_coord <- airports %>% select(faa, lat, lon)
flights <- flights %>% left_join(airports_coord, c("dest" = "faa")) %>% 
    left_join(airports_coord, c("origin" = "faa"))

flights
# A tibble: 336,776 x 23
    year month   day dep_time sched_dep_time dep_delay arr_time
   <int> <int> <int>    <int>          <int>     <dbl>    <int>
 1  2013     1     1      517            515         2      830
 2  2013     1     1      533            529         4      850
 3  2013     1     1      542            540         2      923
 4  2013     1     1      544            545        -1     1004
 5  2013     1     1      554            600        -6      812
 6  2013     1     1      554            558        -4      740
 7  2013     1     1      555            600        -5      913
 8  2013     1     1      557            600        -3      709
 9  2013     1     1      557            600        -3      838
10  2013     1     1      558            600        -2      753
# ... with 336,766 more rows, and 16 more variables: sched_arr_time <int>,
#   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>, lat.x <dbl>, lon.x <dbl>, lat.y <dbl>,
#   lon.y <dbl>

练习

  1. 计算每个目的地机场的平均延误,将计算出的延误关联到airpots数据框。
  2. 飞机的服役年限和它的延误有关系吗?
  3. 什么样的天气状况更容易延误?

筛选关联(filtering join)

筛选关联不影响变量(列),仅影响记录(行)。
semi_join(x, y)保留x中所有的记录,只要该记录在y中有匹配。

semi_join关注的只是在y中有没有匹配,而并不在意该匹配是否重复。

比如要找出飞往10个最热门目的地机场的所有航班。

(top_dest <- flights %>% count(dest, sort = TRUE) %>% head(10))
# 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
 7   SFO 13331
 8   FLL 12055
 9   MIA 11728
10   DCA  9705

筛选飞往上述10个目的地机场的航班。

flights %>% filter(dest %in% top_dest$dest)
# A tibble: 141,145 x 33
    year month   day dep_time sched_dep_time dep_delay arr_time
   <int> <int> <int>    <int>          <int>     <dbl>    <int>
 1  2013     1     1      542            540         2      923
 2  2013     1     1      554            600        -6      812
 3  2013     1     1      554            558        -4      740
 4  2013     1     1      555            600        -5      913
 5  2013     1     1      557            600        -3      838
 6  2013     1     1      558            600        -2      753
 7  2013     1     1      558            600        -2      924
 8  2013     1     1      558            600        -2      923
 9  2013     1     1      559            559         0      702
10  2013     1     1      600            600         0      851
# ... with 141,135 more rows, and 26 more variables: sched_arr_time <int>,
#   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>, name.x <chr>, lat.x <dbl>,
#   lon.x <dbl>, alt.x <int>, tz.x <dbl>, dst.x <chr>, tzone.x <chr>,
#   name.y <chr>, lat.y <dbl>, lon.y <dbl>, alt.y <int>, tz.y <dbl>,
#   dst.y <chr>, tzone.y <chr>

也可以使用semi_join()

flights %>% semi_join(top_dest)
Joining, by = "dest"
# A tibble: 141,145 x 33
    year month   day dep_time sched_dep_time dep_delay arr_time
   <int> <int> <int>    <int>          <int>     <dbl>    <int>
 1  2013     1     1      554            558        -4      740
 2  2013     1     1      558            600        -2      753
 3  2013     1     1      608            600         8      807
 4  2013     1     1      629            630        -1      824
 5  2013     1     1      656            700        -4      854
 6  2013     1     1      709            700         9      852
 7  2013     1     1      715            713         2      911
 8  2013     1     1      739            745        -6      918
 9  2013     1     1      749            710        39      939
10  2013     1     1      828            830        -2     1027
# ... with 141,135 more rows, and 26 more variables: sched_arr_time <int>,
#   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>, name.x <chr>, lat.x <dbl>,
#   lon.x <dbl>, alt.x <int>, tz.x <dbl>, dst.x <chr>, tzone.x <chr>,
#   name.y <chr>, lat.y <dbl>, lon.y <dbl>, alt.y <int>, tz.y <dbl>,
#   dst.y <chr>, tzone.y <chr>

当拓展到多个变量时,filter()函数就不再适用。
比如要找出平均延误最大的10天内的所有航班。

(top_delay <- flights %>% group_by(year, month, day) %>% 
     summarise(avg_delay = mean(arr_delay, na.rm = TRUE)) %>% 
     select(year, month, day, avg_delay) %>% 
     arrange(avg_delay) %>% tail(10)) 
# A tibble: 10 x 4
# Groups:   year, month [7]
    year month   day avg_delay
   <int> <int> <int>     <dbl>
 1  2013    12     5  51.66625
 2  2013     8     8  55.48116
 3  2013    12    17  55.87186
 4  2013     7     1  58.28050
 5  2013     9    12  58.91242
 6  2013     7    10  59.62648
 7  2013     5    23  61.97090
 8  2013     7    22  62.76340
 9  2013     6    13  63.75369
10  2013     3     8  85.86216

以下哪种写法是可行的?

flights %>% filter(year %in% top_delay$year, month %in% top_delay$month, 
                   day %in% top_delay$day)
flights %>% semi_join(top_delay)

anti_join(x, y)删除x中所有的记录,只要该记录在y中有匹配。

anti_join常被用来检出数据输入的错误。
比如将flights数据框和planes数据框关联,可以找出有些航班并没有相对应的飞机。

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
 7  N0EGMQ   371
 8  N534MQ   364
 9  N542MQ   363
10  N531MQ   349
# ... with 712 more rows

练习

  1. 选择由执飞次数至少100次的飞机所执飞的全部航班。
  2. 找出平均延误最大的48个小时,结合天气数据分析天气对航班延误的影响。
  3. 从以下代码的输出结果可以分析出什么?
    anti_join(flights, airports, by = c("dest" = "faa"))
    anti_join(airports, flights, by = c("faa" = "dest"))
  4. 通常认为一架飞机仅有一个航空公司运营,请验证上述判断是否成立。