通常来说,做数据分析仅涉及一个数据框的情况比较少。更多的时候,你需要关联整合多个数据框,去回答你所关心的问题。
本次课程依然使用dplyr和nycflights13包。
library(dplyr) library(nycflights13)
Warning: package 'nycflights13' was built under R version 3.4.2
2017年11月28日
通常来说,做数据分析仅涉及一个数据框的情况比较少。更多的时候,你需要关联整合多个数据框,去回答你所关心的问题。
本次课程依然使用dplyr和nycflights13包。
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唯一确定。
复杂的情况,需要有多个变量才能唯一确定一条记录,比如天气需要由year,month,day,hour和origin组合才能确定。
关键字可以分为两类:
主关键字 (primary key):也叫主键,是确定一条记录的唯一标识。比如planes$tailnum是planes数据框的主键。
外关键字 (foreign key):也叫外键,用于与另一张表的关联。比如flights$tailnum是flights数据框的外键。
主键和其在另外数据框中相应的外键组成一个关联。关联通常是一对多的。比如一个航班对应一架飞机,一架飞机对应多个航班。另外也有一对一的关联,是一对多的关联的特殊情况。
一旦确定了数据框的主键,建议校核主键能否唯一对应一条记录。
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数据框,同时出现的变量有year,month,day,origin,hour,time_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>
airpots数据框。筛选关联不影响变量(列),仅影响记录(行)。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
anti_join(flights, airports, by = c("dest" = "faa"))anti_join(airports, flights, by = c("faa" = "dest"))