여러 가지 방법으로 데이터세트나 데이터프레임을 불러오는 방법을 알아봤었다. 이제 불러온 데이터들을 합치는 방법을 알아보자.
데이터분석 업무를 할 때, 한 개의 데이터 테이블만 가지고 하는 경우는 매우 드물다. 여러 테이블들을 합쳐서 사용해야 하는데, 그때 사용하는 함수 중 하나가 join() 함수이다. join() 함수 외에도 다른 방법이 있긴 하다. mapvalue() 함수를 사용하는 것인데, 다른 포스팅에서 다루도록 하겠다.
키 값 : Primary Key 와 Foreign Key
join()을 할 때 반드시 알아야 할 개념이 있다. Primary Key와 Foreign Key이다. 기본키 및 외래키로 번역이 되는데, 그냥 영어로 자체로 보는 게 개념을 이해하는데 더 도움이 되므로, 영어를 사용하겠다.
- Primary Key : 기초가 되고, 기본이 되는 항목이다. 중복되지 않고, 다른 테이블에서도 그대로 사용될 수 있는 주요 식별자 역할을 한다. 즉, primary key를 사용해서 다른 테이블들을 연결한다.
- Foreign Key : 다른 데이터테이블에서 primary key와 연결되는 항목이다.
아래 예제를 보면서 다시 한 번 설명하도록 하겠다.
join의 종류
join()함수를 하기 전에 알아야 할게 하나 더 있다. 바로 어떤 타입의 join을 사용할지를 이해하고 있어야 한다. 이는 R 뿐만 아니라, SQL 등에서도 동일한 개념이다. 예전에 중학 수학의 첫 시작이 집합이었는데 요즘은 어떤지 모르겠다. 집합에서 교집합, 부분집합, 합집합 등이 있었는데, 그 내용들을 떠올리며 보자. 한 가지 더 추가될 게 있다면, 어디를 기준으로 할 것인가이다. 즉, 데이터테이블 A와 B가 있을 때, A의 Primary Key를 사용해서 B와 합치는 상황을 가정해 보자.
- left_join(): A에는 B에 있지 않은 primary Key값들도 존재할 수 있는데, 그걸 그대로 가져오면 Left Join이다. 즉, left(첫 테이블)이 우선권을 갖는다.
- right_join(): 반대로 B에도 A에 없는 foreign key값들이 존재할 수 있는데, 그걸 그대로 가져오면 right join이다. 즉, right(두번째 테이블)이 우선권을 갖는다.
- Inner_join(): 만약 A나 B에 공통으로 있는 primary Key에 해당되는 것들만 남긴다면 inner join이다. 즉, 교집합이다.
- full_join(): 만약 공통으로 있지 않더라도, A와 B의 primary key로 연결하고, 연결이 안된 값들도 전부 가져온다면 full join이다. 즉, 합집합이다.
데이터베이스 및 패키지 준비하기
이제 본격적으로 예제를 통해 알아보도록 하자. 예제로 사용할 데이터베이스를 우선 불러오자. 다시 Sean Lahman Baseball Database를 사용하려 한다. 이를 위해서는 지난 포스팅에서 언급한 'Lahman' 패키지를 활용하도록 하자.
https://double-d.tistory.com/27
R: 데이터프레임 불러오기 1 - CSV파일, R기본 데이터세트
오랜만에 이전 포스팅에 가 보았다. (https://double-d.tistory.com/18) 데이터베이스 예제로 Sean Lahman의 Baseball Database를 소개한 글이었다. 링크로 남겨두었던 웹사이트에 가보니 연결이 되지 않는다. 구
double-d.tistory.com
People 테이블에서 류현진 선수 찾기
최근 류현진 선수가 한화 이글스로 돌아온다는 뉴스가 연일 나왔다. 한화 이글스의 팬으로서 매우 반가운 일이다. 기사가 나오고 있는 김에 류현진 선수의 메이저리그 기록을 한 번 찾아보자. Lahman 패키지에서 기본으로 제공하는 'People'이라는 데이터테이블에 사람 이름이 있었던 듯하니 더 자세히 알아보자.
‘People‘에 어떤 데이터가 있는지 알아보는 방법은 여러가지이지만 가장 많이 사용하는 방법 중 하나는 'glimpse()'함수이다. 이 함수를 사용하면 데이터프레임에 어떤 항목이 있고, 각 항목은 어떤 데이터타입이며 어떤 값들이 들어가 있는지 쉽게 알아볼 수 있다. 따라서 단순히 항목들의 이름을 확인할 수 있는 ‘colnames()’ 함수나 테이블 형태로 보여주는 ‘View()’ 함수보다 더 자주 사용된다.
library(Lahman)
# Checking column name with last name
People %>% glimpse()
위를 실행시키면 아래와 같이 나온다. 선수 ID, 생년월일, 출생지, 사망연도, 체중 등등 많은 정보가 나온다.
> People %>% glimpse()
Rows: 20,676
Columns: 26
$ playerID <chr> "aardsda01", "aaronha01", "aaronto01", "aasedo01", "abadan01", "abadfe01", "abadijo01", "abbated01", "abbeybe01", "abbeych01", "abbotco01"…
$ birthYear <int> 1981, 1934, 1939, 1954, 1972, 1985, 1850, 1877, 1869, 1866, 1995, 1862, 1874, 1951, 1972, 1967, 1969, 1968, 1886, 1967, 1927, 1850, 1980, …
$ birthMonth <int> 12, 2, 8, 9, 8, 12, 11, 4, 11, 10, 9, 3, 10, 2, 8, 9, 6, 2, 9, 9, 7, 1, 7, 1, 9, 10, 3, 2, 8, 10, 6, 3, 10, 11, 7, 9, 3, 4, 5, 1, 4, 11, 4…
$ birthDay <int> 27, 5, 5, 8, 25, 17, 4, 15, 11, 14, 20, 16, 22, 16, 17, 19, 2, 18, 5, 15, 31, 2, 15, 30, 23, 30, 6, 1, 28, 4, 17, 2, 3, 9, 4, 26, 11, 22, …
$ birthCountry <chr> "USA", "USA", "USA", "USA", "USA", "D.R.", "USA", "USA", "USA", "USA", "USA", "USA", "USA", "USA", "USA", "USA", "USA", "USA", "USA", "USA…
$ birthState <chr> "CO", "AL", "AL", "CA", "FL", "La Romana", "PA", "PA", "VT", "NE", "CA", "OH", "OH", "AR", "GA", "MI", "OH", "MA", "PA", "CA", "OH", "OK",…
$ birthCity <chr> "Denver", "Mobile", "Mobile", "Orange", "Palm Beach", "La Romana", "Philadelphia", "Latrobe", "Essex", "Falls City", "San Diego", "Portage…
$ deathYear <int> NA, 2021, 1984, NA, NA, NA, 1905, 1957, 1962, 1926, NA, 1930, 1935, NA, NA, NA, NA, NA, 1933, NA, 1993, 1939, NA, 2006, NA, 2001, 2004, 19…
$ deathMonth <int> NA, 1, 8, NA, NA, NA, 5, 1, 6, 4, NA, 2, 6, NA, NA, NA, NA, NA, 4, NA, 5, 11, NA, 2, NA, 11, 12, 12, 6, 2, NA, 2, NA, 12, NA, NA, NA, NA, …
$ deathDay <int> NA, 22, 16, NA, NA, NA, 17, 6, 11, 27, NA, 13, 11, NA, NA, NA, NA, NA, 13, NA, 20, 11, NA, 19, NA, 16, 16, 5, 23, 8, NA, 25, NA, 5, NA, NA…
$ deathCountry <chr> NA, "USA", "USA", NA, NA, NA, "USA", "USA", "USA", "USA", NA, "USA", "USA", NA, NA, NA, NA, NA, "USA", NA, "USA", "USA", NA, "USA", NA, "U…
$ deathState <chr> NA, "GA", "GA", NA, NA, NA, "NJ", "FL", "VT", "CA", NA, "MI", "CA", NA, NA, NA, NA, NA, "DC", NA, "OH", "PA", NA, "CA", NA, "NC", "NC", "K…
$ deathCity <chr> NA, "Atlanta", "Atlanta", NA, NA, NA, "Pemberton", "Fort Lauderdale", "Colchester", "San Francisco", NA, "Ottawa Lake", "Los Angeles", NA,…
$ nameFirst <chr> "David", "Hank", "Tommie", "Don", "Andy", "Fernando", "John", "Ed", "Bert", "Charlie", "Cory", "Dan", "Fred", "Glenn", "Jeff", "Jim", "Kur…
$ nameLast <chr> "Aardsma", "Aaron", "Aaron", "Aase", "Abad", "Abad", "Abadie", "Abbaticchio", "Abbey", "Abbey", "Abbott", "Abbott", "Abbott", "Abbott", "A…
$ nameGiven <chr> "David Allan", "Henry Louis", "Tommie Lee", "Donald William", "Fausto Andres", "Fernando Antonio", "John W.", "Edward James", "Bert Wood",…
$ weight <int> 215, 180, 190, 190, 184, 235, 192, 170, 175, 169, 220, 190, 180, 200, 190, 200, 180, 200, 165, 185, 195, NA, 215, 190, 185, 210, 215, 170,…
$ height <int> 75, 72, 75, 75, 73, 74, 72, 71, 71, 68, 74, 71, 70, 78, 74, 75, 71, 76, 69, 75, 74, NA, 75, 70, 73, 74, 76, 72, 72, 74, 73, 72, 74, 69, 72…
$ bats <fct> R, R, R, R, L, L, R, R, R, L, R, R, R, R, R, L, R, L, L, R, L, NA, R, R, R, R, R, L, R, R, R, L, L, R, R, R, L, R, R, R, R, B, R, R, R, R,…
$ throws <fct> R, R, R, R, L, L, R, R, R, L, R, R, R, R, L, L, R, L, R, R, L, NA, R, R, R, L, R, L, R, L, R, L, R, R, R, R, R, R, R, R, R, R, R, R, R, R,…
$ debut <chr> "2004-04-06", "1954-04-13", "1962-04-10", "1977-07-26", "2001-09-10", "2010-07-28", "1875-04-26", "1897-09-04", "1892-06-14", "1893-08-16"…
$ finalGame <chr> "2015-08-23", "1976-10-03", "1971-09-26", "1990-10-03", "2006-04-13", "2021-10-01", "1875-06-10", "1910-09-15", "1896-09-23", "1897-08-19"…
$ retroID <chr> "aardd001", "aaroh101", "aarot101", "aased001", "abada001", "abadf001", "abadj101", "abbae101", "abbeb101", "abbec101", "abboc001", "abbod…
$ bbrefID <chr> "aardsda01", "aaronha01", "aaronto01", "aasedo01", "abadan01", "abadfe01", "abadijo01", "abbated01", "abbeybe01", "abbeych01", "abbotco01"…
$ deathDate <date> NA, 2021-01-22, 1984-08-16, NA, NA, NA, 1905-05-17, 1957-01-06, 1962-06-11, 1926-04-27, NA, 1930-02-13, 1935-06-11, NA, NA, NA, NA, NA, 1…
$ birthDate <date> 1981-12-27, 1934-02-05, 1939-08-05, 1954-09-08, 1972-08-25, 1985-12-17, 1850-11-04, 1877-04-15, 1869-11-11, 1866-10-14, 1995-09-20, 1862-…
‘nameLast’라는 항목이 성이므로 류현진의 성인 ‘Ryu’로 필터링해 보자. 류현진 선수 외에도 류재국 선수도 같이 나온다. 일단 류현진 선수에 집중하기 위해 ‘bbrefID’인 ‘ryuhy01’을 적어두자. 그리고 이 'bbrefID'가 primary Key로 활용될 것이다.
여담이지만, 미국을 포함한 서구 사회에서는 출생지를 중요하게 생각한다. 어느 관공서를 가든 출생증명서를 제출해야 하고, 대부분의 문서에 출생도시를 기입해야 한다. 류재국 선수의 경우 ‘birthState’는 서울인데 ‘birthCity’는 충청도이다. 틀린 정보이지만 이해가 된다. 한국은 출생지를 기입할 일이 거의 없으니, 그냥 생각나는 대로 대충 적은 듯하다.
류현진 선수의 데이터만 필터링하기 위해서는 아래와 같이 조건을 하나 더 추가하자.
People %>%
filter(nameLast ==‘Ryu’,
bbrefID == ‘ryuhy01’)
아래는 실행 시 콘솔에서 나오는 내용이다.
> People %>%
+ filter(nameLast == 'Ryu',
+ bbrefID == 'ryuhy01')
playerID birthYear birthMonth birthDay birthCountry birthState birthCity deathYear deathMonth deathDay deathCountry deathState deathCity nameFirst nameLast
1 ryuhy01 1987 3 25 South Korea Incheon Incheon NA NA NA <NA> <NA> <NA> Hyun Jin Ryu
nameGiven weight height bats throws debut finalGame retroID bbrefID deathDate birthDate
1 Hyun Jin 250 75 R L 2013-04-02 2022-06-01 ryu-h001 ryuhy01 <NA> 1987-03-25
Pitching에서 류현진 선수 찾기
류현진 선수는 투수이므로 투수 관련 테이블에서 찾아보자. 정확한 테이블 이름을 알 수 없을 때는 아래와 같이 'LahmanData' 테이블을 확인해 보면 된다.
콘솔에 표시되는 내용은 아래와 같다. 'LahmanData'테이블에는 'Lahman' 패키지에 있는 데이터세트에 관한 정보가 들어있다.
- 'file' : 각 데이터세트 이름
- 'class': 각 데이터세트의 데이터타입,
- 'nobs': Number of Observations, 행의 수
- 'nvar' : Number of Variable, 칼럼 또는 항목의 수
- ' title': 데이터세트의 Full name
> LahmanData
file class nobs nvar title
1 AllstarFull data.frame 5454 8 AllstarFull table
2 Appearances data.frame 110423 21 Appearances table
3 AwardsManagers data.frame 179 6 AwardsManagers table
4 AwardsPlayers data.frame 6531 6 AwardsPlayers table
5 AwardsShareManagers data.frame 425 7 AwardsShareManagers table
6 AwardsSharePlayers data.frame 6879 7 AwardsSharePlayers table
7 Batting data.frame 110495 22 Batting table
8 BattingPost data.frame 15879 22 BattingPost table
9 CollegePlaying data.frame 17350 3 CollegePlaying table
10 Fielding data.frame 147080 18 Fielding table
11 FieldingOF data.frame 12028 6 FieldingOF table
12 FieldingOFsplit data.frame 34563 18 FieldingOFsplit table
13 FieldingPost data.frame 15063 17 FieldingPost data
14 HallOfFame data.frame 4191 9 Hall of Fame Voting Data
15 HomeGames data.frame 3195 9 HomeGames table
16 Managers data.frame 3684 10 Managers table
17 ManagersHalf data.frame 93 10 ManagersHalf table
18 Parks data.frame 255 6 Parks table
19 People data.frame 20370 26 People table
20 Pitching data.frame 49430 30 Pitching table
21 PitchingPost data.frame 6308 30 PitchingPost table
22 Salaries data.frame 26428 5 Salaries table
23 Schools data.frame 1207 5 Schools table
24 SeriesPost data.frame 367 9 SeriesPost table
25 Teams data.frame 2985 48 Teams table
26 TeamsFranchises data.frame 120 4 TeamFranchises table
27 TeamsHalf data.frame 52 10 TeamsHalf table
'Pitching'이라는 테이블이 왠지 투수와 관련된 테이블 것 같으므로 'glimpse()' 함수를 사용해서 확인해 보자.
Pitching %>%
glimpse()
선수를 필터링하기 위해서 'playerID'를 쓰면 될 것처럼 보이므로, People 테이블에서 류현진 선수의 'bbrefID' 였던 'ryuhy01'로 필터링해 보겠다.
Pitching %>%
filter(playerID == ‘ryuhy01’)
콘솔에 나오는 결괏값은 아래와 같다. 데뷔한 해인 2013년부터 Lahman 패키지에서 제공하는 가장 최신 데이터인 2021년까지의 데이터가 들어있다.
> Pitching %>%
+ filter(playerID == 'ryuhy01')
playerID yearID stint teamID lgID W L G GS CG SHO SV IPouts H
1 ryuhy01 2013 1 LAN NL 14 8 30 30 2 1 0 576 182
2 ryuhy01 2014 1 LAN NL 14 7 26 26 0 0 0 456 152
3 ryuhy01 2016 1 LAN NL 0 1 1 1 0 0 0 14 8
4 ryuhy01 2017 1 LAN NL 5 9 25 24 0 0 1 380 128
5 ryuhy01 2018 1 LAN NL 7 3 15 15 0 0 0 247 68
6 ryuhy01 2019 1 LAN NL 14 5 29 29 1 1 0 548 160
7 ryuhy01 2020 1 TOR AL 5 2 12 12 0 0 0 201 60
8 ryuhy01 2021 1 TOR AL 14 10 31 31 1 1 0 507 170
ER HR BB SO BAOpp ERA IBB WP HBP BK BFP GF R SH SF GIDP
1 64 15 49 154 0.252 3.00 4 5 1 0 783 0 67 7 3 26
2 57 8 29 139 0.257 3.38 2 2 3 0 631 0 60 6 2 12
3 6 1 2 4 0.364 11.57 1 0 0 0 24 0 6 0 0 0
수상이력 확인
혹시 류현진 선수가 메이저리그에서 수상이력이 있는지 알아보자. 조금 전에 확인했던 LahmanData에서 ‘AwardsPlayers’라는 테이블이 있다.
AwardsPlayers %>%
filter(playerID == ‘ryuhy01’)
아쉽게도 류현진 선수는 수상이력이 없다. 실제로 2013년부터 잘 던져오다가, 2015년 5월에 왼쪽 어깨 수술을 받고 한동안 등판하지 못했다. 2019년, 2020년에 연속으로 사이영상 후보로 올랐으나, 수상하지는 못했다. 2020년에는 토론토로 팀을 옮긴 해였는데, 팀 성적이 부진했다. 다행히 좌완 투수에게 주어지는 워렌 스판 상을 수상하였으나, 'AwardsPlayers' 테이블에는 해당 기록이 안 나온다.
류현진 선수 연봉확인
류현진 선수가 올해 한화로 복귀하면서 받은 연봉은 8년 170억 원이라고 알려져 있다. 미화로 하면 1,200만 불 정도이니 KBO에서는 적지 않은 돈이다. 메이저 리그에서는 얼마나 받았었을지 확인해 보자.
Salaries %>%
filter(playerID == ‘ryuhy01’)
아래와 같은 결괏값을 얻을 수 있다. 아쉽게도 토론토에 간 이후의 연봉 정보가 나오지 않는다. 참고로, 기사에 따르면 2013년 다저스와의 계약은 6년 3,600만 달러, 2020년 토론토와 계약은 4년 8,000달러로 계약했다. 계약 마지막 해였던, 작년 토론토로부터 받은 연봉은 2,000만 달러로 메이저리그 전체 4%에 해당하는 공동 46위에 해당하는 금액이다.
> Salaries %>%
+ filter(playerID == "ryuhy01")
yearID teamID lgID playerID salary
1 2013 LAN NL ryuhy01 3333333
2 2014 LAN NL ryuhy01 4333000
3 2016 LAN NL ryuhy01 7833333
일단 있는 연봉 정보만 활용하자.
데이터테이블 합치기
이제 류현진 선수가 있는 3 테이블을 합쳐보자. Salaries와 Pitching은 두 개의 Primary Key 값으로 연결될 수 있다. yearID와 playerID이다. People과 Pitching은 Primary Key 값으로 bbrefID와 playerID를 사용할 수 있다. 키 값의 이름은 다르지만 하나로 보고 사용하자.
People 테이블에서 류현진 선수의 데이터만을 필터링해서 첫 번째 데이터세트로 사용한다. 그러면 Pitching이나 Salaries 테이블에서는 류현진 선수 외의 데이터는 전부 필요 없으니 left_join() 함수를 사용해서 다른 선수/감독 등의 데이터는 남겨두지 않도록 하자. 불필요한 항목들도 정리하고, 류현진 선수임을 알아볼 수 있는 기본 정보와, 년도, 팀, 승, 패, 연봉만 남기자.
People %>%
filter(nameLast == 'Ryu',
bbrefID == 'ryuhy01')%>%
left_join(Pitching, by = c("bbrefID" = "playerID")) %>%
left_join(Salaries) %>%
select(playerID, nameFirst, nameLast, yearID, teamID, W, L, salary)
위에 대한 결괏값은 아래와 같다.
> People %>%
+ filter(nameLast == 'Ryu',
+ bbrefID == 'ryuhy01')%>%
+ left_join(Pitching, by = c("bbrefID" = "playerID")) %>%
+ left_join(Salaries) %>%
+ select(playerID, nameFirst, nameLast, yearID, teamID, W, L, salary)
Joining with `by = join_by(playerID, yearID, teamID, lgID)`
playerID nameFirst nameLast yearID teamID W L salary
1 ryuhy01 Hyun Jin Ryu 2013 LAN 14 8 3333333
2 ryuhy01 Hyun Jin Ryu 2014 LAN 14 7 4333000
3 ryuhy01 Hyun Jin Ryu 2016 LAN 0 1 7833333
4 ryuhy01 Hyun Jin Ryu 2017 LAN 5 9 NA
5 ryuhy01 Hyun Jin Ryu 2018 LAN 7 3 NA
6 ryuhy01 Hyun Jin Ryu 2019 LAN 14 5 NA
7 ryuhy01 Hyun Jin Ryu 2020 TOR 5 2 NA
8 ryuhy01 Hyun Jin Ryu 2021 TOR 14 10 NA
9 ryuhy01 Hyun Jin Ryu 2022 TOR 2 0 NA
추가로 알아볼 함수 cbind/ rbind
데이터테이블 병합을 할 수 있는 다른 방법 중 하나이다. rbind는 항목의 개수와 순서가 동일할 경우 하나의 테이블 밑에 다른 테이블을 그대로 붙일 때 사용한다. 즉, Primary Key가 필요 없다. 비슷한 개념으로 cbind는 행이 같은 테이블들을 붙일 때 사용한다.
- rbind() : row bind
- cbind() : column bind
애초에 다른 테이블로 작업하지 않고, 하나의 테이블을 가지고 가공한 여러 결과를 붙일 때 사용한다. 또는 다른 테이블에서 나왔어도 목적 테이블의 형태가 동일할 때 붙이기 위해서 사용하기도 한다.
'R' 카테고리의 다른 글
데이터프레임 불러오기 5 - 엑셀 파일 (1) | 2024.02.27 |
---|---|
데이터프레임 불러오기 4 - CSV 파일 복습 및 심화 (1) | 2024.02.26 |
데이터프레임 불러오기 3 - 데이터베이스에서 직접 불러오기: DBI & RSQLite (1) | 2024.02.25 |
데이터프레임 불러오기 2 - 데이터베이스에서 직접 불러오기: RODBC (0) | 2024.02.22 |
데이터프레임 불러오기 1 - CSV파일, R기본 데이터세트 (1) | 2024.02.12 |