관계형 데이터란?
여러 데이터 테이블을 총칭한 것
데이터 분석 시, 데이터 테이블이 단 하나와만 관련된 경우는 없음
-> 개별 데이터셋보다 데이터셋의 관계가 중요함
관계는 항상 두 개의 테이블 사이에서 정의됨
테이블 3개 이상인 관계: 각 쌍 사이의 관계를 이용하여 정의
쌍을 이루는 두 요소가 같은 테이블이 될 수도 있음
관계형 데이터로 작업하기 위해 필요한 것?
두 개의 테이블에 작동하는 동사
관계형 데이터에 동작하도록 설계된 3가지 계열 동사
- 뮤테이팅(mutating, 변형) 조인: 다른 데이터프레임의 관측값을 가져와 테이블의 새로운 변수로 추가
- 필터링 조인: 다른 테이블의 관측값과 일치하는지 여부로 관측값을 걸러냄
- 집합 연산: 관측값을 집합 원소로 취급
관계형 데이터가 있는 가장 기본 장소
관계형 데이터베이스 관리 시스템(RDBMS, Relational DataBase Management System)
실습 준비
library(tidyverse)
library(nycflights13) # 실습에서 사용할 테이블
flights 테이블과 관련된 4개의 티블이 존재
: airlines, airports planes, weather
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에 연결
각 여객기가 출발지에서 목적지까지 날아가는 경로를 그리려고 한다.
어떤 변수가 필요하고 어떤 테이블을 결합해야 하는가?
-> 출발지 - 목적지 경로 확인 시, 필요한 변수: 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")
weather과 airports 사이의 관계는 어떠한가?
다이어그램도 그려보아라.
airports\(faa는 weather\)origin의 외래키이다.
weather는 출발지 NYC 공항 정보만 포함한다.
미국 모든 공항의 날씨 기록이 포함되어 있다면 flights와 어떤 관계가 추가되는가?
만약 US의 전체 공항 날씨가 포함된다면, 각 비행기의 목적지 날씨를 제공할 수 있다.
weather 변수의 year, month, day, hour, origin은 flightS 변수 year, month day, hour, dest의 외래키가 된다.
weather 변수는 목적지 공항에 비행기가 도착했을 때, 날씨 정보를 알려줄 수 있다.
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
키(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
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
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,...
Lahman::Batting %>%
count(playerID, yearID, stint) %>%
filter(n > 1) %>%
nrow()
## [1] 0
babynames::babynames %>%
count(year, sex, name) %>%
filter(n > 1) %>%
nrow()
## [1] 0
nasaweather::atmos %>%
count(lat, long, year, month) %>%
filter(n > 1) %>%
nrow()
## [1] 0
fueleconomy::vehicles %>%
count(id) %>%
filter(n > 1) %>%
nrow()
## [1] 0
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
대체키를 추가하고 조회하니, 키 식별이 가능해졌다.
뮤테이팅 조인(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이 추가됨
조인 작동법 배우기
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개 또는 여러 행에 연결하는 방법
실제 조인에서는 매치 항목이 점으로 표시됨
(점 수 = 매치 수 = 출력 행 수)
inner join: 키가 같을 때마다 두 관측값을 매칭함
내부 조인 출력은 키, x 값과 y 값을 포함하는 새로운 데이터 프레임
by를 사용하여 어떤 변수가 키인지 결정
내부 조인에서는 두 테이블에 나타나는 관측값이 모두 보존
외부 조인에서는 적어도 하나의 테이블에 있는 관측값 보존
외부 조인의 3가지 유형
- 왼쪽 조인(left join)은 x의 모든 관측값을 보존
- 오른쪽 조인(right join)은 y의 모든 관측값을 보존
- 전체 조인(full join)은 x와 y의 모든 관측값을 보존
각 테이블에 ‘가상’ 관측값을 추가하여 작동하는데,
이 관측값에는 항상 매칭되는 키(다른 키 매칭 안 되는 경우)와 NA로 채워진 값이 있음
벤 다이어그램으로 서로 다른 유형의 조인 묘사하기
cf) 벤 다이어그램은 키가 고유하게 관측값을 식별하지 못할 때, 발생하는 문제를 표현하지 못함
현재까지 모든 테이블 키가 고유하다고 가정하였으나, 키가 고유하지 않은 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
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
현재까지 테이블 쌍은 항상 하나의 변수로 조인되었으며,
그 변수는 두 테이블에서 같은 이름을 가짐
(by = “key”로 제약사항 코드화함)
by에 다른 값을 사용하여 테이블을 연결하는 방법 살펴보기
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
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 변수에 접미사가 붙어서 출력 시 헷갈리지 않게 나옴
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()
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
여객기의 나이와 지연 시간 사이에 관계가 있는가?
어떤 기상 조건이 지연 가능성을 더 높이는가?
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)
필터링 조인은 뮤테이팅 조인과 같은 방식으로 관측값을 매칭함
단, 변수가 아닌 관측값에 영향을 줌
- 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
그러나, 이러한 접근 방식을 여러 변수로 확장하기는 어려움
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
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
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>
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년 중 가장 지연된 48시간을 찾아, 날씨 데이터와 교차 참조하라.
어떤 패턴을 볼 수 있는가?
anti_join(flights, airports, by = c(“dest” = “faa”))를 보고 무엇을 알 수 있는가?
anti_join(airports, flights, by - c(“faa” = “dest”))는 어떤가?
각 항공기는 단일 항공사가 운항하므로 항공기와 항공사 간 암묵적인 관계를 예상할 수 있다. 이전 절에서 배운 도구로 가설을 확인하거나 기각하라.
현재 공부한 데이터는 정제된 데이터로 문제가 발생하지 않았으나, 실제로 작업할 데이터는 문제가 많을 수 있음
airports %>% count(alt, lon) %>% filter(n > 1)
## # A tibble: 0 x 3
## # ... with 3 variables: alt <dbl>, lon <dbl>, n <int>
복잡한 필터를 단순한 필터를로 분해하려는 경우에 종종 사용
행 전체에 동작하면서 모든 변수의 값을 비교함
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