10장. dplyr로 하는 관계형 데이터

10.1 Introduction

  1. 관계형 데이터란?
    여러 데이터 테이블을 총칭한 것
    데이터 분석 시, 데이터 테이블이 단 하나와만 관련된 경우는 없음
    -> 개별 데이터셋보다 데이터셋의 관계가 중요함

  2. 관계는 항상 두 개의 테이블 사이에서 정의됨
    테이블 3개 이상인 관계: 각 쌍 사이의 관계를 이용하여 정의
    쌍을 이루는 두 요소가 같은 테이블이 될 수도 있음

  3. 관계형 데이터로 작업하기 위해 필요한 것?
    두 개의 테이블에 작동하는 동사

관계형 데이터에 동작하도록 설계된 3가지 계열 동사
- 뮤테이팅(mutating, 변형) 조인: 다른 데이터프레임의 관측값을 가져와 테이블의 새로운 변수로 추가
- 필터링 조인: 다른 테이블의 관측값과 일치하는지 여부로 관측값을 걸러냄
- 집합 연산: 관측값을 집합 원소로 취급
  1. 관계형 데이터가 있는 가장 기본 장소
    관계형 데이터베이스 관리 시스템(RDBMS, Relational DataBase Management System)

  2. 실습 준비

library(tidyverse)
library(nycflights13) # 실습에서 사용할 테이블
  • nycflights13 참고 사항 2013년 뉴욕에서 출발한 336,776개 항공편 정보 (출처: 미 교통통계)
flights 테이블과 관련된 4개의 티블이 존재
: airlines, airports planes, weather

10.2 nycflights13

nycflights13::flights %>%
   print(n = 6, width = Inf)
## # A tibble: 336,776 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      517            515         2      830            819
## 2  2013     1     1      533            529         4      850            830
## 3  2013     1     1      542            540         2      923            850
## 4  2013     1     1      544            545        -1     1004           1022
## 5  2013     1     1      554            600        -6      812            837
## 6  2013     1     1      554            558        -4      740            728
##   arr_delay carrier flight tailnum origin dest  air_time distance  hour minute
##       <dbl> <chr>    <int> <chr>   <chr>  <chr>    <dbl>    <dbl> <dbl>  <dbl>
## 1        11 UA        1545 N14228  EWR    IAH        227     1400     5     15
## 2        20 UA        1714 N24211  LGA    IAH        227     1416     5     29
## 3        33 AA        1141 N619AA  JFK    MIA        160     1089     5     40
## 4       -18 B6         725 N804JB  JFK    BQN        183     1576     5     45
## 5       -25 DL         461 N668DN  LGA    ATL        116      762     6      0
## 6        12 UA        1696 N39463  EWR    ORD        150      719     5     58
##   time_hour          
##   <dttm>             
## 1 2013-01-01 05:00:00
## 2 2013-01-01 05:00:00
## 3 2013-01-01 05:00:00
## 4 2013-01-01 05:00:00
## 5 2013-01-01 06:00:00
## 6 2013-01-01 05:00:00
## # ... with 336,770 more rows
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.
nycflights13::airports %>% # 각 공항 정보가 faa 공항 코드로 식별됨
   print(n = 10, width = Inf)
## # A tibble: 1,458 x 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 Airport  36.4  -82.2  1593    -5 A    
##  7 0G6   Williams County Airport         41.5  -84.5   730    -5 A    
##  8 0G7   Finger Lakes Regional Airport   42.9  -76.8   492    -5 A    
##  9 0P2   Shoestring Aviation Airfield    39.8  -76.6  1000    -5 U    
## 10 0S9   Jefferson County Intl           48.1 -123.    108    -8 A    
##    tzone              
##    <chr>              
##  1 America/New_York   
##  2 America/Chicago    
##  3 America/Chicago    
##  4 America/New_York   
##  5 America/New_York   
##  6 America/New_York   
##  7 America/New_York   
##  8 America/New_York   
##  9 America/New_York   
## 10 America/Los_Angeles
## # ... with 1,448 more rows
nycflights13::planes %>% # 각 여객기 정보가 tailnum으로 식별됨
   print(n = 10, width = Inf)
## # A tibble: 3,322 x 9
##    tailnum  year type                    manufacturer     model     engines
##    <chr>   <int> <chr>                   <chr>            <chr>       <int>
##  1 N10156   2004 Fixed wing multi engine EMBRAER          EMB-145XR       2
##  2 N102UW   1998 Fixed wing multi engine AIRBUS INDUSTRIE A320-214        2
##  3 N103US   1999 Fixed wing multi engine AIRBUS INDUSTRIE A320-214        2
##  4 N104UW   1999 Fixed wing multi engine AIRBUS INDUSTRIE A320-214        2
##  5 N10575   2002 Fixed wing multi engine EMBRAER          EMB-145LR       2
##  6 N105UW   1999 Fixed wing multi engine AIRBUS INDUSTRIE A320-214        2
##  7 N107US   1999 Fixed wing multi engine AIRBUS INDUSTRIE A320-214        2
##  8 N108UW   1999 Fixed wing multi engine AIRBUS INDUSTRIE A320-214        2
##  9 N109UW   1999 Fixed wing multi engine AIRBUS INDUSTRIE A320-214        2
## 10 N110UW   1999 Fixed wing multi engine AIRBUS INDUSTRIE A320-214        2
##    seats speed engine   
##    <int> <int> <chr>    
##  1    55    NA Turbo-fan
##  2   182    NA Turbo-fan
##  3   182    NA Turbo-fan
##  4   182    NA Turbo-fan
##  5    55    NA Turbo-fan
##  6   182    NA Turbo-fan
##  7   182    NA Turbo-fan
##  8   182    NA Turbo-fan
##  9   182    NA Turbo-fan
## 10   182    NA Turbo-fan
## # ... with 3,312 more rows
nycflights13::weather %>% # 각 NYC 공항의 매 시각 날씨 정보
   print(n = 6, width = Inf)
## # 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
##   precip pressure visib time_hour          
##    <dbl>    <dbl> <dbl> <dttm>             
## 1      0    1012     10 2013-01-01 01:00:00
## 2      0    1012.    10 2013-01-01 02:00:00
## 3      0    1012.    10 2013-01-01 03:00:00
## 4      0    1012.    10 2013-01-01 04:00:00
## 5      0    1012.    10 2013-01-01 05:00:00
## 6      0    1012.    10 2013-01-01 06:00:00
## # ... with 26,109 more rows
  • 그림으로 정리

    cf) airports와 weather 관계가 빠진 그림

  • 이해해야 할 관계

- flights는 단 하나의 변수 tailum로 planes에 연결
- flights는 carrier 변수로 airlines에 연결
- flights는 origin(위치)과 dest(목적지) 변수로 airports에 연결
- flights는 origin, year, month, day, hour으로 weather에 연결

+ 연습문제

  1. 각 여객기가 출발지에서 목적지까지 날아가는 경로를 그리려고 한다.
    어떤 변수가 필요하고 어떤 테이블을 결합해야 하는가?

    -> 출발지 - 목적지 경로 확인 시, 필요한 변수: origin, dest, + 경로
    결합해야 하는 테이블: flights(각 비행기의 origin & dest 속함) + airports(각 공항의 위도와 경도 값)

1st. 내부 조인 이용하여 테이블 결합하기

flights_latlon <- flights %>% #flights_latlon 변수 생성
 inner_join(select(airports, origin = faa, origin_lat = lat, origin_lon = lon),
  by = "origin") %>% 
 inner_join(select(airports, dest = faa, dest_lat = lat, dest_lon = lon),
  by = "dest")

flights_latlon %>%
   print(n = 6, width = Inf)
## # A tibble: 329,174 x 23
##    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      517            515         2      830            819
## 2  2013     1     1      533            529         4      850            830
## 3  2013     1     1      542            540         2      923            850
## 4  2013     1     1      554            600        -6      812            837
## 5  2013     1     1      554            558        -4      740            728
## 6  2013     1     1      555            600        -5      913            854
##   arr_delay carrier flight tailnum origin dest  air_time distance  hour minute
##       <dbl> <chr>    <int> <chr>   <chr>  <chr>    <dbl>    <dbl> <dbl>  <dbl>
## 1        11 UA        1545 N14228  EWR    IAH        227     1400     5     15
## 2        20 UA        1714 N24211  LGA    IAH        227     1416     5     29
## 3        33 AA        1141 N619AA  JFK    MIA        160     1089     5     40
## 4       -25 DL         461 N668DN  LGA    ATL        116      762     6      0
## 5        12 UA        1696 N39463  EWR    ORD        150      719     5     58
## 6        19 B6         507 N516JB  EWR    FLL        158     1065     6      0
##   time_hour           origin_lat origin_lon dest_lat dest_lon
##   <dttm>                   <dbl>      <dbl>    <dbl>    <dbl>
## 1 2013-01-01 05:00:00       40.7      -74.2     30.0    -95.3
## 2 2013-01-01 05:00:00       40.8      -73.9     30.0    -95.3
## 3 2013-01-01 05:00:00       40.6      -73.8     25.8    -80.3
## 4 2013-01-01 06:00:00       40.8      -73.9     33.6    -84.4
## 5 2013-01-01 05:00:00       40.7      -74.2     42.0    -87.9
## 6 2013-01-01 06:00:00       40.7      -74.2     26.1    -80.2
## # ... with 329,168 more rows

origin과 dest가 faa 공항 코드로 정리(13, 14 columns)되었고
origin_lat, origin_lon, dest_lat, dest_lon(20-23 columns)가 생겼다

2nd. “maps” 패키지와 내부 조인 이용하여 지도 그리기
install.packages(“maps”) # 필요한 패키지 설치&라이브러리

library(maps)
library(tidyverse) #ggplot2, dplyr 함수 포함
flights_latlon %>%
 slice(1:100) %>% # 행 번호로 행을 필터링 하는 함수
 ggplot(aes(x = origin_lon, xend = dest_lon,
            y = origin_lat, yend = dest_lat)) +
 borders("state") + geom_segment(arrow = arrow(length = unit(0.1, "cm"))) + 
 coord_quickmap() + 
 labs(y = "Latitude", x = "Longitude")

  1. weather과 airports 사이의 관계는 어떠한가?
    다이어그램도 그려보아라.

    airports\(faa는 weather\)origin의 외래키이다.

  2. weather는 출발지 NYC 공항 정보만 포함한다.
    미국 모든 공항의 날씨 기록이 포함되어 있다면 flights와 어떤 관계가 추가되는가?

    만약 US의 전체 공항 날씨가 포함된다면, 각 비행기의 목적지 날씨를 제공할 수 있다.
    weather 변수의 year, month, day, hour, origin은 flightS 변수 year, month day, hour, dest의 외래키가 된다.
    weather 변수는 목적지 공항에 비행기가 도착했을 때, 날씨 정보를 알려줄 수 있다.

  3. 1년 중 어떤 날이 ‘특별한’ 것과 특별한 날에는 항공여행객이 평소보다 적다.
    데이터프레임으로 어떻게 표현하는가? 테이블의 기본키는 무엇이며 기존 테이블에 어떻게 연결되는가?

special_days <- tribble(~year, ~month, ~day, ~holiday,
                        2013, 01, 01, "New Years Day",
                        2013, 07, 04, "Independence Day",
                        2013, 11, 29, "Thanksgiving Day",
                        2013, 12, 25, "Christmas Day")

special_days
## # A tibble: 4 x 4
##    year month   day holiday         
##   <dbl> <dbl> <dbl> <chr>           
## 1  2013     1     1 New Years Day   
## 2  2013     7     4 Independence Day
## 3  2013    11    29 Thanksgiving Day
## 4  2013    12    25 Christmas Day

cf) special_days의 기본키는 year, month, day, holiday

10.3 KEY

키(KEY)란 무엇인가?
각 테이블 쌍을 연결할 때 사용되는 변수, 관측값을 고유하게 식별하는 변수(or 변수 집합)
ex) nycflights13에서 각 여객기는 tailnum으로 고유하게 식별됨
weather는 year, month, day, hour, origin 다섯 변수로 식별됨
키(KEY)
- 기본키(primary key)
자신의 테이블에서 관측값을 고유하게 식별
ex) planes$tailnum은 planes테이블의 각 여객기를 고유하게 식별하므로 기본키

- 외래키(foreign key)
다른 테이블의 관측값을 고유하게 식별
ex) flight$tailnum은 flights 테이블에서 각 항공편을 고유한 여객기와 매칭시키므로 외래키

+ 한 변수가 기본키이며 동시에 외래키일 수 있음
ex) 출발지는 weather 기본키의 일부이며 airport 테이블의 외래키이기도 함

테이블에서 기본키를 확인한 후에는 기본키가 실제로 각 관측값을 고유하게 식별하는지 확인해야 함
-> 기본키를 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
  • 추가 테이블에 명시적인 기본키가 없는 경우도 있음
    : 모든 행은 관측값이지만 어떤 변수를 조합해도 각 행을 신뢰성 있게 구분하지 못하는 경우
    ex) flight 테이블의 기본키는? 없다
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
##  7  2013     1     1     27     4
##  8  2013     1     1     31     2
##  9  2013     1     1     32     2
## 10  2013     1     1     35     2
## # ... with 29,758 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
##  7  2013     1     1 N12567      2
##  8  2013     1     1 N13123      2
##  9  2013     1     1 N13538      3
## 10  2013     1     1 N13566      3
## # ... with 64,918 more rows
  • 대체키(surrogate key)
    테이블에 기본키가 없는 경우, mutate()와 row_number()로 기본키 추가
  • 기본키와 외래키의 관계
    대개 일대다(one-to-many) 관계: 각 항공편에는 여객기 하나, 각 여객기에는 여러 항공편
    다대다(many-to-many): 다대일 관계와 일대다 관계를 사용하여 다대다 관계 모델링이 가능
    ex) airline과 airport 간에 다대다 관계가 성립 (각 항공사는 많은 공항으로 운행, 각 공항에는 많은 항공사 존재)
    추가로, 일대일(1-to-1), 다대일(many-to-1) 관계도 있음

+ 연습문제

  1. flights에 대체키를 추가하라.
flights %>%
   arrange(year, month, day, sched_dep_time, carrier, flight) %>%
   mutate(flight_surrogate = row_number()) %>%
   glimpse()
## Rows: 336,776
## Columns: 20
## $ year             <int> 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 20...
## $ month            <int> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,...
## $ day              <int> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,...
## $ dep_time         <int> 517, 533, 542, 544, 554, 559, 558, 559, 558, 558, ...
## $ sched_dep_time   <int> 515, 529, 540, 545, 558, 559, 600, 600, 600, 600, ...
## $ dep_delay        <dbl> 2, 4, 2, -1, -4, 0, -2, -1, -2, -2, -3, NA, 1, 0, ...
## $ arr_time         <int> 830, 850, 923, 1004, 740, 702, 753, 941, 849, 853,...
## $ sched_arr_time   <int> 819, 830, 850, 1022, 728, 706, 745, 910, 851, 856,...
## $ arr_delay        <dbl> 11, 20, 33, -18, 12, -4, 8, 31, -2, -3, -8, NA, -6...
## $ carrier          <chr> "UA", "UA", "AA", "B6", "UA", "B6", "AA", "AA", "B...
## $ flight           <int> 1545, 1714, 1141, 725, 1696, 1806, 301, 707, 49, 7...
## $ tailnum          <chr> "N14228", "N24211", "N619AA", "N804JB", "N39463", ...
## $ origin           <chr> "EWR", "LGA", "JFK", "JFK", "EWR", "JFK", "LGA", "...
## $ dest             <chr> "IAH", "IAH", "MIA", "BQN", "ORD", "BOS", "ORD", "...
## $ air_time         <dbl> 227, 227, 160, 183, 150, 44, 138, 257, 149, 158, 1...
## $ distance         <dbl> 1400, 1416, 1089, 1576, 719, 187, 733, 1389, 1028,...
## $ hour             <dbl> 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,...
## $ minute           <dbl> 15, 29, 40, 45, 58, 59, 0, 0, 0, 0, 0, 0, 0, 0, 0,...
## $ time_hour        <dttm> 2013-01-01 05:00:00, 2013-01-01 05:00:00, 2013-01...
## $ flight_surrogate <int> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,...
  1. 다음 데이터셋의 키를 식별하라.
    1. Lahman::Batting
      install.packages(“Lahman”) library(Lahman)
      str(Batting)
Lahman::Batting %>%
   count(playerID, yearID, stint) %>%
   filter(n > 1) %>%
   nrow()
## [1] 0
  1. babynames::babynames
    install.packages(“babynames”) library(babynames)
    str(babynames)
babynames::babynames %>%
   count(year, sex, name) %>%
   filter(n > 1) %>%
   nrow()
## [1] 0
  1. nasaweather::atmos
    install.packages(“nasaweather”) library(nasaweather)
    str(atmos)
nasaweather::atmos %>%
   count(lat, long, year, month) %>%
   filter(n > 1) %>%
   nrow()
## [1] 0
  1. fueleconomy::vehicles
    install.packages(“fueleconomy”) library(fueleconomy)
    str(vehicles)
fueleconomy::vehicles %>%
   count(id) %>%
   filter(n > 1) %>%
   nrow()
## [1] 0
  1. ggplot2::diamonds
    str(diamonds)
ggplot2::diamonds %>%
   distinct() %>% # 중복 없는 row 추출 함수
   nrow()
## [1] 53794

-> 기본키가 존재하지 않는다. 대체키(surrogate key) 추가하여야 식별 가능하다.

diamonds <- mutate(ggplot2::diamonds, diamonds_surrogate = row_number())

str(diamonds)
## tibble [53,940 x 11] (S3: tbl_df/tbl/data.frame)
##  $ carat             : num [1:53940] 0.23 0.21 0.23 0.29 0.31 0.24 0.24 0.26 0.22 0.23 ...
##  $ cut               : Ord.factor w/ 5 levels "Fair"<"Good"<..: 5 4 2 4 2 3 3 3 1 3 ...
##  $ color             : Ord.factor w/ 7 levels "D"<"E"<"F"<"G"<..: 2 2 2 6 7 7 6 5 2 5 ...
##  $ clarity           : Ord.factor w/ 8 levels "I1"<"SI2"<"SI1"<..: 2 3 5 4 2 6 7 3 4 5 ...
##  $ depth             : num [1:53940] 61.5 59.8 56.9 62.4 63.3 62.8 62.3 61.9 65.1 59.4 ...
##  $ table             : num [1:53940] 55 61 65 58 58 57 57 55 61 61 ...
##  $ price             : int [1:53940] 326 326 327 334 335 336 336 337 337 338 ...
##  $ x                 : num [1:53940] 3.95 3.89 4.05 4.2 4.34 3.94 3.95 4.07 3.87 4 ...
##  $ y                 : num [1:53940] 3.98 3.84 4.07 4.23 4.35 3.96 3.98 4.11 3.78 4.05 ...
##  $ z                 : num [1:53940] 2.43 2.31 2.31 2.63 2.75 2.48 2.47 2.53 2.49 2.39 ...
##  $ diamonds_surrogate: int [1:53940] 1 2 3 4 5 6 7 8 9 10 ...
#대체키 추가된 상태로 키 식별하기
diamonds %>%
   count(carat, cut, diamonds_surrogate) %>%
   filter(n > 1) %>%
   nrow()
## [1] 0

대체키를 추가하고 조회하니, 키 식별이 가능해졌다.

  1. Lahman 패키지의 Batting, Master, Salaries 테이블 간의 연결을 나타내는 다이어그램을 그려라.
    Master, Managers, AwardsManagers 사이 관계를 보여주는 또 다른 다이어그램을 그려라.
    Batting, Pitching, Fielding 테이블 간의 관계를 어떻게 규정하겠는가?

10.4 뮤테이팅 조인

뮤테이팅 조인(mutating join): 한 쌍의 테이블을 결합하기 위해 살펴볼 도구
뮤테이팅 조인을 사용하면 두 테이블의 변수를 결합할 수 있음
관측값을 키로 매칭시킨 뒤, 한 테이블에서 다른 테이블로 변수를 복사
변수를 오른쪽에 추가하므로 이미 많은 변수가 있는 경우 새 변수가 출력되지 않음

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     
##  7  2013     1     1     6 EWR    FLL   N516JB  B6     
##  8  2013     1     1     6 LGA    IAD   N829AS  EV     
##  9  2013     1     1     6 JFK    MCO   N593JB  B6     
## 10  2013     1     1     6 LGA    ORD   N3ALAA  AA     
## # ... with 336,766 more rows

가정) flight2 데이터에 항공사 전체 이름을 추가 -> left_join()으로 airlines와 flights2 데이터프레임 결합 가능

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.   
##  7  2013     1     1     6 N516JB  B6      JetBlue Airways         
##  8  2013     1     1     6 N829AS  EV      ExpressJet Airlines Inc.
##  9  2013     1     1     6 N593JB  B6      JetBlue Airways         
## 10  2013     1     1     6 N3ALAA  AA      American Airlines Inc.  
## # ... with 336,766 more rows

-> flights2는 airlines와 결합하여 새 변수 name이 추가됨

10.4.1 조인 이해하기

조인 작동법 배우기

x <- tribble(~key, ~val_x,
             1, "x1",
             2, "x2",
             3, "x3")
y <- tribble(~key, ~val_y,
             1, "y1",
             2, "y2",
             4, "y3")

x
## # A tibble: 3 x 2
##     key val_x
##   <dbl> <chr>
## 1     1 x1   
## 2     2 x2   
## 3     3 x3
y
## # A tibble: 3 x 2
##     key val_y
##   <dbl> <chr>
## 1     1 y1   
## 2     2 y2   
## 3     4 y3

왼쪽 열은 ‘키’ 변수를 나타내며 테이블 사이 행을 일치시키는 데 사용
오른쪽 열은 따라가는 ‘값’ 열을 나타냄

조인이란? x의 각 행을 y의 0, 1개 또는 여러 행에 연결하는 방법

실제 조인에서는 매치 항목이 점으로 표시됨
(점 수 = 매치 수 = 출력 행 수)

10.4.2 내부 조인

inner join: 키가 같을 때마다 두 관측값을 매칭함

내부 조인 출력은 키, x 값과 y 값을 포함하는 새로운 데이터 프레임
by를 사용하여 어떤 변수가 키인지 결정

  • 내부 조인의 가장 중요한 특성
    매칭되지 않는 행은 결과에 포함되지 않음
    -> 관측값을 잃기 쉬워서 일반적인 분석 시, 적합하지 않음

10.4.3 외부 조인

내부 조인에서는 두 테이블에 나타나는 관측값이 모두 보존
외부 조인에서는 적어도 하나의 테이블에 있는 관측값 보존

외부 조인의 3가지 유형
- 왼쪽 조인(left join)은 x의 모든 관측값을 보존
- 오른쪽 조인(right join)은 y의 모든 관측값을 보존
- 전체 조인(full join)은 x와 y의 모든 관측값을 보존

각 테이블에 ‘가상’ 관측값을 추가하여 작동하는데,
이 관측값에는 항상 매칭되는 키(다른 키 매칭 안 되는 경우)와 NA로 채워진 값이 있음

벤 다이어그램으로 서로 다른 유형의 조인 묘사하기

cf) 벤 다이어그램은 키가 고유하게 관측값을 식별하지 못할 때, 발생하는 문제를 표현하지 못함

10.4.4 중복키

현재까지 모든 테이블 키가 고유하다고 가정하였으나, 키가 고유하지 않은 2가지 경우 살펴보기

  • 하나의 테이블에 중복키가 있는 경우
    중복키는 추가 정보를 넣을 때 유용한데 일반적으로 일대다 관계가 있기 때문
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
  • 두 테이블 모두 중복키가 있는 경우
    키가 어느 테이블에서도 고유하게 관측값을 식별하지 않음 -> 일반적 오류
    중복키로 조인하면 가능한 모든 조합인 데카르트곱(Cartesian product)을 얻을 수 있음
y <- tribble(
   ~key, ~val_y,
   1, "y1",
   2, "y2",
   2, "y3",
   1, "y4")
left_join(x, y, by = "key")
## # A tibble: 8 x 3
##     key val_x val_y
##   <dbl> <chr> <chr>
## 1     1 x1    y1   
## 2     1 x1    y4   
## 3     2 x2    y2   
## 4     2 x2    y3   
## 5     2 x3    y2   
## 6     2 x3    y3   
## 7     1 x4    y1   
## 8     1 x4    y4

10.4.5 키 열 정의하기

현재까지 테이블 쌍은 항상 하나의 변수로 조인되었으며,
그 변수는 두 테이블에서 같은 이름을 가짐
(by = “key”로 제약사항 코드화함)

by에 다른 값을 사용하여 테이블을 연결하는 방법 살펴보기

  • 기본값 by = NULL 사용
    두 테이블에 있는 모든 변수를 사용하는 자연 조인(nature join)
flights2 %>%
   left_join(weather) %>%
   print(n = 6, width = Inf)
## # 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
##   wind_dir wind_speed wind_gust precip pressure visib time_hour          
##      <dbl>      <dbl>     <dbl>  <dbl>    <dbl> <dbl> <dttm>             
## 1      260       12.7      NA        0    1012.    10 2013-01-01 05:00:00
## 2      250       15.0      21.9      0    1011.    10 2013-01-01 05:00:00
## 3      260       15.0      NA        0    1012.    10 2013-01-01 05:00:00
## 4      260       15.0      NA        0    1012.    10 2013-01-01 05:00:00
## 5      260       16.1      23.0      0    1012.    10 2013-01-01 06:00:00
## 6      260       12.7      NA        0    1012.    10 2013-01-01 05:00:00
## # ... with 336,770 more rows
  • 문자형 벡터 by = “x” 사용하기
    자연 조인과 같지만 일부 공통 변수만 사용
    ex) flights와 planes에는 year 변수가 있지만 서로 다른 의미라 tailnum만 조인하고 싶은 경우
flights2 %>%
   left_join(planes, by = "tailnum") %>%
   print(n = 6, width = Inf)
## # A tibble: 336,776 x 16
##   year.x month   day  hour origin dest  tailnum carrier year.y
##    <int> <int> <int> <dbl> <chr>  <chr> <chr>   <chr>    <int>
## 1   2013     1     1     5 EWR    IAH   N14228  UA        1999
## 2   2013     1     1     5 LGA    IAH   N24211  UA        1998
## 3   2013     1     1     5 JFK    MIA   N619AA  AA        1990
## 4   2013     1     1     5 JFK    BQN   N804JB  B6        2012
## 5   2013     1     1     6 LGA    ATL   N668DN  DL        1991
## 6   2013     1     1     5 EWR    ORD   N39463  UA        2012
##   type                    manufacturer model     engines seats speed engine   
##   <chr>                   <chr>        <chr>       <int> <int> <int> <chr>    
## 1 Fixed wing multi engine BOEING       737-824         2   149    NA Turbo-fan
## 2 Fixed wing multi engine BOEING       737-824         2   149    NA Turbo-fan
## 3 Fixed wing multi engine BOEING       757-223         2   178    NA Turbo-fan
## 4 Fixed wing multi engine AIRBUS       A320-232        2   200    NA Turbo-fan
## 5 Fixed wing multi engine BOEING       757-232         2   178    NA Turbo-fan
## 6 Fixed wing multi engine BOEING       737-924ER       2   191    NA Turbo-fan
## # ... with 336,770 more rows

cf) year 변수에 접미사가 붙어서 출력 시 헷갈리지 않게 나옴

  • 이름 있는 문자 벡터, by = c(“a” = “b”) 사용
    테이블 x의 변수 a와 테이블 y의 변수 b를 매칭 -> x 변수가 출력에 사용됨
    ex) 지도 그릴 때, flights 데이터를 각 공항 위치(lat, lon)가 포함된 airports 데이터와 결합해야 함, 각 항공편에는 출발 공항와 도착 공항이 있으므로 어떤 것에 조인할지 지정해야 함

+ 연습문제

  1. 목적지별 평균 지연 시간을 계산한 뒤, airports 데이터프레임에 조인하여 지연의 공간 분포를 표시하라. 다음을 이용해 미국 지도를 쉽게 그릴 수 있다.
airports %>%
   semi_join(flights, c("faa" = "dest")) %>%
   ggplot(aes(lon, lat)) +
     borders("state") + 
     geom_point() +
     coord_quickmap()

avg_dest_delays <- flights %>%
   group_by(dest) %>%
   summarise(delay = mean(arr_delay, na.rm = TRUE), .groups = 'drop') %>%
   inner_join(airports, by = c(dest = "faa"))

avg_dest_delays %>%
   print(n = 6, width = Inf)
## # A tibble: 101 x 9
##   dest  delay name                                lat    lon   alt    tz dst  
##   <chr> <dbl> <chr>                             <dbl>  <dbl> <dbl> <dbl> <chr>
## 1 ABQ    4.38 Albuquerque International Sunport  35.0 -107.   5355    -7 A    
## 2 ACK    4.85 Nantucket Mem                      41.3  -70.1    48    -5 A    
## 3 ALB   14.4  Albany Intl                        42.7  -73.8   285    -5 A    
## 4 ANC   -2.5  Ted Stevens Anchorage Intl         61.2 -150.    152    -9 A    
## 5 ATL   11.3  Hartsfield Jackson Atlanta Intl    33.6  -84.4  1026    -5 A    
## 6 AUS    6.02 Austin Bergstrom Intl              30.2  -97.7   542    -6 A    
##   tzone            
##   <chr>            
## 1 America/Denver   
## 2 America/New_York 
## 3 America/New_York 
## 4 America/Anchorage
## 5 America/New_York 
## 6 America/Chicago  
## # ... with 95 more rows
avg_dest_delays %>%
   ggplot(aes(lon, lat, color = delay)) +
   borders("state") + 
   geom_point() +
   coord_quickmap()

  1. flights에 출발지와 목적지의 위치(즉, lat과 lon)를 추가하라.
airport_locations <- airports %>%
   select(faa, lat, lon)

flights_locations <- flights %>%
   select(year:day, hour, origin, dest) %>%
   left_join(airport_locations, by = c("origin" = "faa")) %>%
   left_join(airport_locations, by = c("dest" = "faa"))

flights_locations %>%
   print(n = 6, width = Inf)
## # A tibble: 336,776 x 10
##    year month   day  hour origin dest  lat.x lon.x lat.y lon.y
##   <int> <int> <int> <dbl> <chr>  <chr> <dbl> <dbl> <dbl> <dbl>
## 1  2013     1     1     5 EWR    IAH    40.7 -74.2  30.0 -95.3
## 2  2013     1     1     5 LGA    IAH    40.8 -73.9  30.0 -95.3
## 3  2013     1     1     5 JFK    MIA    40.6 -73.8  25.8 -80.3
## 4  2013     1     1     5 JFK    BQN    40.6 -73.8  NA    NA  
## 5  2013     1     1     6 LGA    ATL    40.8 -73.9  33.6 -84.4
## 6  2013     1     1     5 EWR    ORD    40.7 -74.2  42.0 -87.9
## # ... with 336,770 more rows
  1. 여객기의 나이와 지연 시간 사이에 관계가 있는가?

  2. 어떤 기상 조건이 지연 가능성을 더 높이는가?

  3. 2013년 6월 13일에 무슨 일이 일어났는가? 지연의 공간 패턴을 표시한 후, 구글을 사용하여 날씨와 상호참조하라.

+ 기타 구현

base::merge()는 4가지 유형의 뮤테이팅 조인을 수행할 수 있음

특정 dplyr 동사 장점은 코드 의도를 더 명확하게 전달하는 것 -> 조인 간의 차이는 중요하지만 merge() 인수에 있음, dplyr 조인은 상당히 빠르고 행 순서를 어지럽히지 않음

dplyr 규칙은 SQL에 기반하여 서로 옮겨쓰기 쉬움

inner 및 outer는 선택사항으로 종종 생략됨

inner_join(x, y, by = c(“a” = "b))처럼 테이블간 다른 변수를 결합할 때, SQL에서 약간 다르게 표현
SQL은 dplyr보다 넓은 조인 유형을 지원하며 등식이 아닌 다른 제약 조건으로도 테이블 연결이 가능(비동등 조인, non-equisjoin)

10.5 필터링 조인

필터링 조인은 뮤테이팅 조인과 같은 방식으로 관측값을 매칭함
단, 변수가 아닌 관측값에 영향을 줌

- semi_join(x, y)는 y와 매치되는 x의 모든 관측값을 보존함
- anti_join(x, y)는 y와 매치되는 x의 모든 관측값을 삭제함

semi_join은 필터링된 요약 테이블을 다시 원래 행과 매치시킬 때 유용
ex) 가장 인기 있는 상위 10개 도착지를 구했다고 가정

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
##  7 SFO   13331
##  8 FLL   12055
##  9 MIA   11728
## 10 DCA    9705

목적지 중 한 곳으로 운행한 항공편을 찾는 방법: 필터 만들기

flights %>%
   filter(dest %in% top_dest$dest) %>%
   print(n = 6, width = Inf)
## # 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
##   arr_delay carrier flight tailnum origin dest  air_time distance  hour minute
##       <dbl> <chr>    <int> <chr>   <chr>  <chr>    <dbl>    <dbl> <dbl>  <dbl>
## 1        33 AA        1141 N619AA  JFK    MIA        160     1089     5     40
## 2       -25 DL         461 N668DN  LGA    ATL        116      762     6      0
## 3        12 UA        1696 N39463  EWR    ORD        150      719     5     58
## 4        19 B6         507 N516JB  EWR    FLL        158     1065     6      0
## 5        -8 B6          79 N593JB  JFK    MCO        140      944     6      0
## 6         8 AA         301 N3ALAA  LGA    ORD        138      733     6      0
##   time_hour          
##   <dttm>             
## 1 2013-01-01 05:00:00
## 2 2013-01-01 06:00:00
## 3 2013-01-01 05:00:00
## 4 2013-01-01 06:00:00
## 5 2013-01-01 06:00:00
## 6 2013-01-01 06:00:00
## # ... with 141,139 more rows

그러나, 이러한 접근 방식을 여러 변수로 확장하기는 어려움

  • semi_join을 사용하는 방법
    단, 새 열을 추가하는 대신 y에서 일치하는 x의 행만 보존
flights %>%
   semi_join(top_dest) %>%
   print(n = 6, width = Inf)
## # 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
##   arr_delay carrier flight tailnum origin dest  air_time distance  hour minute
##       <dbl> <chr>    <int> <chr>   <chr>  <chr>    <dbl>    <dbl> <dbl>  <dbl>
## 1        33 AA        1141 N619AA  JFK    MIA        160     1089     5     40
## 2       -25 DL         461 N668DN  LGA    ATL        116      762     6      0
## 3        12 UA        1696 N39463  EWR    ORD        150      719     5     58
## 4        19 B6         507 N516JB  EWR    FLL        158     1065     6      0
## 5        -8 B6          79 N593JB  JFK    MCO        140      944     6      0
## 6         8 AA         301 N3ALAA  LGA    ORD        138      733     6      0
##   time_hour          
##   <dttm>             
## 1 2013-01-01 05:00:00
## 2 2013-01-01 06:00:00
## 3 2013-01-01 05:00:00
## 4 2013-01-01 06:00:00
## 5 2013-01-01 06:00:00
## 6 2013-01-01 06:00:00
## # ... with 141,139 more rows

매칭 여부만이 중요할 뿐, 어떤 관측값이 매칭되는지는 중요하지 않음
-> 뮤테이팅 조인처럼 행을 복제하지는 않음을 의미

anti_join은 semi_join의 반대이며, 매칭되지 않는 행을 보존함

anti_join은 조인 불일치를 진단할 때 유용함
ex) 항공편과 여객기를 연결하는 경우, 여객기에 매칭되지 않는 항공편이 많음을 알고 싶을 수 있음

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. 공편에 tailnum이 없는 것은 무엇을 의미하는가?
    planes에 매치되는 관측값이 없는 tailnum 관측값들의 공통점은 무엇인가?
    hint: 한 변수가 문제의 약 90%를 설명
    -> tailnum이 없다는 것은 비행기가 취소되었다는 것: arr_time도 없음
flights %>%
   filter(is.na(tailnum), !is.na(arr_time)) %>%
   nrow()
## [1] 0

?planes
“American Airways (AA) and Envoy Air (MQ) report fleet numbers rather than tail numbers so can’t be matched.”

flights %>%
   anti_join(planes, by = "tailnum") %>%
   count(carrier, sort = TRUE) %>%
   mutate(p = n / sum(n))
## # A tibble: 10 x 3
##    carrier     n        p
##    <chr>   <int>    <dbl>
##  1 MQ      25397 0.483   
##  2 AA      22558 0.429   
##  3 UA       1693 0.0322  
##  4 9E       1044 0.0198  
##  5 B6        830 0.0158  
##  6 US        699 0.0133  
##  7 FL        187 0.00355 
##  8 DL        110 0.00209 
##  9 F9         50 0.000950
## 10 WN         38 0.000722

그러나, 모든 flights의 tail numbers가 planes 테이블에서 사라진 것은 아니다. 불일치를 조정하여야 한다.

flights %>%
  distinct(carrier, tailnum) %>%
  left_join(planes, by = "tailnum") %>%
  group_by(carrier) %>%
  summarise(total_planes = n(),
            not_in_planes = sum(is.na(model))) %>%
  mutate(missing_pct = not_in_planes / total_planes) %>%
  arrange(desc(missing_pct))
## # A tibble: 16 x 4
##    carrier total_planes not_in_planes missing_pct
##    <chr>          <int>         <int>       <dbl>
##  1 MQ               238           234     0.983  
##  2 AA               601           430     0.715  
##  3 F9                26             3     0.115  
##  4 FL               129            12     0.0930 
##  5 UA               621            23     0.0370 
##  6 US               290             9     0.0310 
##  7 DL               629            10     0.0159 
##  8 B6               193             3     0.0155 
##  9 WN               583             3     0.00515
## 10 9E               204             1     0.00490
## 11 AS                84             0     0      
## 12 EV               316             0     0      
## 13 HA                14             0     0      
## 14 OO                28             0     0      
## 15 VX                53             0     0      
## 16 YV                58             0     0
  1. filghts를 필터링하여 최소 100편을 운행한 여객기의 항공편만 표시하라.

1st. 최소 100번 운행한 비행기 중, 유효한 비행기 모두 찾기

planes_100flights <- flights %>%
   filter(!is.na(tailnum)) %>%
   group_by(tailnum) %>%
   count() %>%
   filter(n >= 100)

planes_100flights
## # A tibble: 1,217 x 2
## # Groups:   tailnum [1,217]
##    tailnum     n
##    <chr>   <int>
##  1 N0EGMQ    371
##  2 N10156    153
##  3 N10575    289
##  4 N11106    129
##  5 N11107    148
##  6 N11109    148
##  7 N11113    138
##  8 N11119    148
##  9 N11121    154
## 10 N11127    124
## # ... with 1,207 more rows

2nd. semi_join을 이용해서 원래 행과 매칭시키기

flights %>%
  semi_join(planes_100flights, by = "tailnum")
## # A tibble: 228,390 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      517            515         2      830            819
##  2  2013     1     1      533            529         4      850            830
##  3  2013     1     1      544            545        -1     1004           1022
##  4  2013     1     1      554            558        -4      740            728
##  5  2013     1     1      555            600        -5      913            854
##  6  2013     1     1      557            600        -3      709            723
##  7  2013     1     1      557            600        -3      838            846
##  8  2013     1     1      558            600        -2      849            851
##  9  2013     1     1      558            600        -2      853            856
## 10  2013     1     1      558            600        -2      923            937
## # ... with 228,380 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>
  1. fueleconomy::vehicles와 fueleconomy::common을 조인하여 가장 많은 차량 모델의 레코드만 찾아라.
fueleconomy::vehicles %>%
  semi_join(fueleconomy::common, by = c("make", "model")) %>% #make 사용 이유: 같은 모델명이 있을 수 있으므로
   print(n = 6, width = Inf)
## # A tibble: 14,531 x 12
##      id make  model    year class           trans           drive            
##   <dbl> <chr> <chr>   <dbl> <chr>           <chr>           <chr>            
## 1  1833 Acura Integra  1986 Subcompact Cars Automatic 4-spd Front-Wheel Drive
## 2  1834 Acura Integra  1986 Subcompact Cars Manual 5-spd    Front-Wheel Drive
## 3  3037 Acura Integra  1987 Subcompact Cars Automatic 4-spd Front-Wheel Drive
## 4  3038 Acura Integra  1987 Subcompact Cars Manual 5-spd    Front-Wheel Drive
## 5  4183 Acura Integra  1988 Subcompact Cars Automatic 4-spd Front-Wheel Drive
## 6  4184 Acura Integra  1988 Subcompact Cars Manual 5-spd    Front-Wheel Drive
##     cyl displ fuel      hwy   cty
##   <dbl> <dbl> <chr>   <dbl> <dbl>
## 1     4   1.6 Regular    28    22
## 2     4   1.6 Regular    28    23
## 3     4   1.6 Regular    28    22
## 4     4   1.6 Regular    28    23
## 5     4   1.6 Regular    27    22
## 6     4   1.6 Regular    28    23
## # ... with 14,525 more rows
fueleconomy::vehicles %>%
  distinct(model, make) %>%
  group_by(model) %>%
  filter(n() > 1) %>%
  arrange(model)
## # A tibble: 126 x 2
## # Groups:   model [60]
##    make                   model              
##    <chr>                  <chr>              
##  1 Audi                   200                
##  2 Chrysler               200                
##  3 Mcevoy Motors          240 DL/240 GL Wagon
##  4 Volvo                  240 DL/240 GL Wagon
##  5 Lambda Control Systems 300E               
##  6 Mercedes-Benz          300E               
##  7 J.K. Motors            300SL              
##  8 Mercedes-Benz          300SL              
##  9 Mercedes-Benz          500SE              
## 10 Texas Coach Company    500SE              
## # ... with 116 more rows
fueleconomy::common %>%
  distinct(model, make) %>%
  group_by(model) %>%
  filter(n() > 1) %>%
  arrange(model)
## # A tibble: 8 x 2
## # Groups:   model [3]
##   make       model    
##   <chr>      <chr>    
## 1 Dodge      Colt     
## 2 Plymouth   Colt     
## 3 Mitsubishi Truck 2WD
## 4 Nissan     Truck 2WD
## 5 Toyota     Truck 2WD
## 6 Mitsubishi Truck 4WD
## 7 Nissan     Truck 4WD
## 8 Toyota     Truck 4WD
  1. 1년 중 가장 지연된 48시간을 찾아, 날씨 데이터와 교차 참조하라.
    어떤 패턴을 볼 수 있는가?

  2. anti_join(flights, airports, by = c(“dest” = “faa”))를 보고 무엇을 알 수 있는가?
    anti_join(airports, flights, by - c(“faa” = “dest”))는 어떤가?

  3. 각 항공기는 단일 항공사가 운항하므로 항공기와 항공사 간 암묵적인 관계를 예상할 수 있다. 이전 절에서 배운 도구로 가설을 확인하거나 기각하라.

10.6 조인 문제

현재 공부한 데이터는 정제된 데이터로 문제가 발생하지 않았으나, 실제로 작업할 데이터는 문제가 많을 수 있음

  • 데이터 확인 시, 수행할 것
  1. 기본키를 구성하는 변수 식별하기
    고유한 식별자가 되는 변수 조합을 찾아야 함
airports %>% count(alt, lon) %>% filter(n > 1)
## # A tibble: 0 x 3
## # ... with 3 variables: alt <dbl>, lon <dbl>, n <int>
  1. 기본키 변수에 결측값이 없는지 확인하기
    값이 결측된 경우에는 관측값을 식별할 수 없음
  2. 외래키가 다른 테이블의 기본키와 매칭되는지 확인하기
    anti_join을 사용하는 것이 가장 좋은 방법

10.7 집합 연산

복잡한 필터를 단순한 필터를로 분해하려는 경우에 종종 사용
행 전체에 동작하면서 모든 변수의 값을 비교함
x와 y 입력이 같은 변수를 가진다고 간주하며 관측값을 집합으로 취급

- intersect(x, y): x, y 모두에 있는 관측값만 반환
- union(x, y): x와 y의 고유한 관측값을 반환
- setdiff(x, y):x에 있지만, y에는 없는 관측값을 반환

ex) 간단한 데이터로 살펴보기

df1 <- tribble(
   ~x, ~y,
   1, 1,
   2, 1)
df2 <- tribble(
   ~x, ~y,
   1, 1,
   1, 2)

intersect(df1, df2) #행이 3개로 나옴!
## # A tibble: 1 x 2
##       x     y
##   <dbl> <dbl>
## 1     1     1
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
  • dplyr로 하는 관계형 데이터 끝