오랜만에 이전 포스팅에 가 보았다. (https://double-d.tistory.com/18) 데이터베이스 예제로 Sean Lahman의 Baseball Database를 소개한 글이었다. 링크로 남겨두었던 웹사이트에 가보니 연결이 되지 않는다. 구글링을 해보니 다행히 웹사이트는 그대로 있다. 다만 공유하는 데이터베이스 형태가 줄어들었다. 예전에는 sql light버전 및 다양한 형태로 제공되었는데, 이제는 MS Access 형태와 CSV만 제공된다.
윈도우 사용자의 경우 MS Access 파일인 .mdb를 다운받아 사용할 수 있다. 하지만 맥 사용자는 CSV 파일만 사용이 가능하다.
이번 글에서는 업데이트된 Sean Lahman의 웹사이트 기준으로 데이터를 R 로 불러오는 여러 방법에 대해서 알아보자. 이는 기존에 데이터베이스 형태로 제공되는 데이터프레임을 R로 불러오는 방법이며, 추후에 다른 글에서 직접 작성한 엑셀 파일로부터 불러오는 방법, RODBC 패키지를 통해서 쿼리를 사용한 데이터 불러오는 방법 등을 다루겠다.
준비: 데이터베이스 다운로드
SeanLahman.com
Sean Lahman, journalist and baseball data guru
www.seanlahman.com
위의 Sean Lahman 웹사이트에 들어가면, 언급한 바와 같이 두 가지 형태의 데이터베이스를 다운로드할 수 있다. 이 중에서 'comma-delimited version' 을 클릭하면 baseballdatabank-2023.1.zip을 다운로드 할 수 있다.
압축을 풀면 contrib, core, upstream 폴더가 나오고 각 폴더 안에 .csv형태의 데이터 테이블들을 볼 수 있다.
데이터 불러오기 방법 1: CSV파일 하나씩 불러오기
가장 원시적이고 무식한 방법은 하나씩 read_csv() 함수로 불러들이는 것이다. 예를 들어, 'core' 폴더에 있는 People과 Managers라는 데이터 테이블을 데이터프레임으로 불러들일 경우, 아래와 같이 한다.
People <- read_csv("data\core\People.csv")
Managers <- read_csv("data\core\Managers.csv")
직관적이고, 어쩌면 전체 데이터 테이블을 잘 모르는 상황에서 하나씩 확인하며 추가하기에는 좋을 수도 있다. 하지만 원하는 테이블이 여러 개라면 더 쉬운 방법도 있지 않을까?
데이터 불러오기 방법 2: map()함수 사용
경로값을 지정하고, map()함수로 반복해서 돌리는 방법이 있다. dir_ls()함수는 directory에 있는 파일들의 경로를 리스트로 만들어 준다. 리스트에 파일명을 포함한 경로들이 생성되고, map()함수가 지정된 함수인 read_csv()함수를 각 경로의 파일명들에 대해서 실행시켜 준다.
이를 위해서는 'fs'와 'purrr' 패키지가 설치되어야 한다. 함수의 설치는 install.packages() 함수를 통해 할 수 있고, 설치된 후에는 'library()' 함수를 통해서 해당 패키지 안에 있는 함수들을 활성화해줘야 한다.
# Import multiple CSV files
library(fs)
library(purrr)
csv_core_dir <- "data\baseballdatabank-2023.1\core"
baseball_data_list <- csv_core_dir %>%
dir_ls() %>%
map(
.f = function (path){
read.csv(path)
}
)
폴더 경로는 ""를 쓴 후에 'Tab'키를 누르면 현재 R 의 경로에서 하나씩 선택해 들어갈 수 있다. 참고로 되도록이면 R Project를 생성한 폴더 안에 'Data' 폴더나 'Source' 폴더를 만들어서 데이터베이스 파일을 미리 넣어 놓길 권장한다. 이에 대해서는 추후에 다른 포스팅 글을 통해 안내하도록 하겠다.
위에서 'csv_core_dir'은 csv파일들이 저장되어 있는 폴더 경로이다. 'dir_ls()'에 경로값을 입력해 주면 해당 경로 안에 있는 파일들의 full path로 list를 만들어 준다. 예를 들어, 위의 core폴더 안에 People.csv에 대해서는 'data\baseballdatabank-2023.1\core\People.csv', Managers.csv에 대해서는 'data\baseballdatabank-2023.1\core\Managers.csv' 등으로 만든 후, List 타입의 데이터로 경로값들을 변환시켜 준다. map()함수가 List에 있는 값들을 하나씩 불러들여 적용할 함수에 입력값으로 반복적으로 하나씩 넣어주는 역할을 한다. map()함수에 대해서도 다른 글을 통해 추가적으로 설명하도록 하겠다.
결과물인 'baseball_data_list' 를 콘솔에서 실행시켜보면 아래와 같이 각각의 데이터프레임을 요소로 갖는 List임을 확인할 수 있다. 아래는 그 결과값 화면의 일부이다.
> baseball_data_list
$`01_data/baseballdatabank-2023.1/core/AllstarFull.csv`
# A tibble: 5,516 × 8
playerID yearID gameNum gameID teamID lgID GP startingPos
<chr> <dbl> <dbl> <chr> <chr> <chr> <dbl> <dbl>
1 gomezle01 1933 0 ALS193307060 NYA AL 1 1
2 ferreri01 1933 0 ALS193307060 BOS AL 1 2
3 gehrilo01 1933 0 ALS193307060 NYA AL 1 3
4 gehrich01 1933 0 ALS193307060 DET AL 1 4
5 dykesji01 1933 0 ALS193307060 CHA AL 1 5
6 cronijo01 1933 0 ALS193307060 WS1 AL 1 6
7 chapmbe01 1933 0 ALS193307060 WS1 AL 1 7
8 simmoal01 1933 0 ALS193307060 CHA AL 1 8
9 ruthba01 1933 0 ALS193307060 NYA AL 1 9
10 averiea01 1933 0 ALS193307060 CLE AL 1 NA
# ℹ 5,506 more rows
# ℹ Use `print(n = ...)` to see more rows
$`01_data/baseballdatabank-2023.1/core/Appearances.csv`
# A tibble: 112,106 × 21
yearID teamID lgID playerID G_all GS G_batting G_defense G_p G_c G_1b G_2b G_3b G_ss G_lf G_cf G_rf G_of G_dh G_ph G_pr
<dbl> <chr> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 1871 TRO NA abercda01 1 1 1 1 0 0 0 0 0 1 0 0 0 0 0 0 0
2 1871 RC1 NA addybo01 25 25 25 25 0 0 0 22 0 3 0 0 0 0 0 0 0
3 1871 CL1 NA allisar01 29 29 29 29 0 0 0 2 0 0 0 29 0 29 0 0 0
4 1871 WS3 NA allisdo01 27 27 27 27 0 27 0 0 0 0 0 0 0 0 0 0 0
5 1871 RC1 NA ansonca01 25 25 25 25 0 5 1 2 20 0 1 0 0 1 0 0 0
6 1871 FW1 NA armstbo01 12 12 12 12 0 0 0 0 0 0 0 11 1 12 0 0 0
7 1871 RC1 NA barkeal01 1 1 1 1 0 0 0 0 0 0 1 0 0 1 0 0 0
8 1871 BS1 NA barnero01 31 31 31 31 0 0 0 16 0 15 0 0 0 0 0 0 0
9 1871 FW1 NA barrebi01 1 1 1 1 0 1 0 0 1 0 0 0 0 0 0 0 0
10 1871 BS1 NA barrofr01 18 17 18 18 0 0 0 1 0 0 13 0 4 17 0 0 0
# ℹ 112,096 more rows
# ℹ Use `print(n = ...)` to see more rows
$`01_data/baseballdatabank-2023.1/core/Batting.csv`
# A tibble: 112,184 × 22
playerID yearID stint teamID lgID G AB R H `2B` `3B` HR RBI SB CS BB SO IBB HBP SH SF GIDP
<chr> <dbl> <dbl> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 abercda01 1871 1 TRO NA 1 4 0 0 0 0 0 0 0 0 0 0 NA NA NA NA 0
2 addybo01 1871 1 RC1 NA 25 118 30 32 6 0 0 13 8 1 4 0 NA NA NA NA 0
'baseball_data_list'라는 List 안에 있는 하나의 데이터 프레임만 뽑아보려면 어떻게 하면 될까? 만약 10번째 파일인 'Managers.csv' 파일의 데이터프레임을 보고 싶다면 어떻게 할까?
# Show the 10th dataframe in the 'baseball_data_list'
baseball_data_list[10]
List의 10번째 요소값인 Managers.csv 데이터프레임만 뽑아내면 아래와 같은 결과화면을 볼 수 있다.
> baseball_data_list[10]
$`01_data/baseballdatabank-2023.1/core/Managers.csv`
# A tibble: 3,718 × 10
playerID yearID teamID lgID inseason G W L rank plyrMgr
<chr> <dbl> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <chr>
1 wrighha01 1871 BS1 NA 1 31 20 10 3 Y
2 woodji01 1871 CH1 NA 1 28 19 9 2 Y
3 paborch01 1871 CL1 NA 1 29 10 19 8 Y
4 lennobi01 1871 FW1 NA 1 14 5 9 8 Y
5 deaneha01 1871 FW1 NA 2 5 2 3 8 Y
6 fergubo01 1871 NY2 NA 1 33 16 17 5 Y
7 mcbridi01 1871 PH1 NA 1 28 21 7 1 Y
8 hastisc01 1871 RC1 NA 1 25 4 21 9 Y
9 pikeli01 1871 TRO NA 1 4 1 3 6 Y
10 cravebi01 1871 TRO NA 2 25 12 12 6 Y
# ℹ 3,708 more rows
# ℹ Use `print(n = ...)` to see more rows
참고로 리스트 안에 있는 데이터프레임의 목록을 보기 위해서는 R Studio의 오른쪽에 있는 Environment에서 'baseball_data_list'를 클릭해 보면 된다. 아래는 클릭했을 때 볼 수 있는 목록이다.
데이터 불러오기 방법 3: map()으로 불러온 데이터프레임들을 합쳐서 하나로 만든 후 사용하기
우선 위에서 설명한 map()함수를 사용하는 방법까지는 똑같다. 'baseball_data_list' 까지 만들었다면, list에 있는 데이터프레임들을 그냥 전부 붙여 버려서 'baseball_data_tbl'을 만들자. 즉, map()함수로 만들어낸 list에서 하나씩 목록을 확인하며 필요한 걸 뽑아내기 귀찮다면 이 방법을 사용할 수 있다.
baseball_data_tbl <- baseball_data_list %>%
set_names(dir_ls(csv_core_dir)) %>%
bind_rows(.id ="file_path")
위의 방법으로 다 붙여 버렸을 때의 단점이 있다. 열이 모든 데이터프레임에 있는 열 숫자만큼 늘어나서 필요한 항목의 값을 찾기 번거로워질 수 있다. 이럴 때는 reduce(full_join) 등을 써서 열의 숫자를 최대한 줄이는 방법도 부분적으로 가능하다. 관련한 내용은 다른 글에서 다루도록 하겠다.
> baseball_data_tbl
# A tibble: 546,875 × 143
file_path playerID yearID gameNum gameID teamID lgID GP startingPos G_all GS G_batting G_defense
<chr> <chr> <dbl> <dbl> <chr> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 01_data/bas… gomezle… 1933 0 ALS19… NYA AL 1 1 NA NA NA NA
2 01_data/bas… ferreri… 1933 0 ALS19… BOS AL 1 2 NA NA NA NA
3 01_data/bas… gehrilo… 1933 0 ALS19… NYA AL 1 3 NA NA NA NA
4 01_data/bas… gehrich… 1933 0 ALS19… DET AL 1 4 NA NA NA NA
5 01_data/bas… dykesji… 1933 0 ALS19… CHA AL 1 5 NA NA NA NA
6 01_data/bas… cronijo… 1933 0 ALS19… WS1 AL 1 6 NA NA NA NA
7 01_data/bas… chapmbe… 1933 0 ALS19… WS1 AL 1 7 NA NA NA NA
8 01_data/bas… simmoal… 1933 0 ALS19… CHA AL 1 8 NA NA NA NA
9 01_data/bas… ruthba01 1933 0 ALS19… NYA AL 1 9 NA NA NA NA
10 01_data/bas… averiea… 1933 0 ALS19… CLE AL 1 NA NA NA NA NA
# ℹ 546,865 more rows
# ℹ 130 more variables: G_p <dbl>, G_c <dbl>, G_1b <dbl>, G_2b <dbl>, G_3b <dbl>, G_ss <dbl>, G_lf <dbl>,
# G_cf <dbl>, G_rf <dbl>, G_of <dbl>, G_dh <dbl>, G_ph <dbl>, G_pr <dbl>, stint <dbl>, G <dbl>, AB <dbl>,
# R <dbl>, H <dbl>, `2B` <dbl>, `3B` <dbl>, HR <dbl>, RBI <dbl>, SB <dbl>, CS <dbl>, BB <dbl>, SO <dbl>,
# IBB <dbl>, HBP <dbl>, SH <dbl>, SF <dbl>, GIDP <dbl>, round <chr>, POS <chr>, InnOuts <dbl>, PO <dbl>,
# A <dbl>, E <dbl>, DP <dbl>, PB <dbl>, WP <dbl>, ZR <dbl>, Glf <dbl>, Gcf <dbl>, Grf <dbl>, TP <dbl>,
# year.key <dbl>, league.key <chr>, team.key <chr>, park.key <chr>, span.first <date>, span.last <date>, …
# ℹ Use `print(n = ...)` to see more rows, and `colnames()` to see all variable names
>
데이터 불러오기 방법 4: Lahman 패키지 불러와서 사용하기
사실 Lahman Baseball database 같은 경우에는 R에서 패키지로 데이터세트를 제공한다. 워낙 유명한 무료 데이터소스이기 때문이다. 이 경우, 다른 R Package와 같이, 설치 후에 library()함수로 불러오면 된다. 아래는 People이라는 데이터프레임을 불러오는 방법이다.
People 외에 어떤 데이터프레임임 있는지 확인하려면 패키지에 대한 정보를 확인하면 된다. '?Lahman'을 입력하고 실행하면 패키지에 대한 모든 정보가 데이터프레임까지 포함해서 나온다. 원하는 데이터프레임에 대한 설명까지 확인하면, 해당 데이터프레임의 이름을 입력하고 실행해 주면 된다.
install.packages("Lahman")
library(Lahman)
?Lahman
people
People 데이터프레임에 있는 인물정보는 이름의 알파벳 순서로 정렬되어 있다. 코딩 몇 줄을 추가하여 어린 순서대로 상위 10명을 확인해보자.
People %>%
arrange(desc(birthYear)) %>%
slice(1:10)
결괏값은 아래와 같다.
> People %>%
+ arrange(desc(birthYear)) %>%
+ slice(1:10)
playerID birthYear birthMonth birthDay birthCountry birthState birthCity deathYear deathMonth deathDay
1 alvarfr01 2001 11 19 Venezuela Miranda Guatire NA NA NA
2 francwa01 2001 3 1 D.R. Peravia Bani NA NA NA
3 grissva01 2001 1 5 USA FL Orlando NA NA NA
4 harrimi04 2001 3 7 USA GA DeKalb NA NA NA
5 hendegu01 2001 6 29 USA AL Montgomery NA NA NA
6 tovarez01 2001 8 1 Venezuela Aragua Maracay NA NA NA
7 abramcj01 2000 10 3 USA GA Alpharetta NA NA NA
8 ariasga01 2000 2 27 Venezuela Aragua La Victoria NA NA NA
9 carroco02 2000 8 21 USA WA Seattle NA NA NA
10 casastr01 2000 1 15 USA FL Miami NA NA NA
deathCountry deathState deathCity nameFirst nameLast nameGiven weight height bats throws
1 <NA> <NA> <NA> Francisco Alvarez Francisco Javier 242 70 R R
2 <NA> <NA> <NA> Wander Franco Wander Samuel 189 70 B R
3 <NA> <NA> <NA> Vaughn Grissom Vaughn Anthony 210 75 R R
4 <NA> <NA> <NA> Michael Harris Michael Machion 195 72 L L
5 <NA> <NA> <NA> Gunnar Henderson Gunnar Randal 210 74 L R
6 <NA> <NA> <NA> Ezequiel Tovar Ezequiel Jesus 162 72 R R
7 <NA> <NA> <NA> CJ Abrams Paul Christopher 185 74 L R
8 <NA> <NA> <NA> Gabriel Arias Gabriel Alejandro 217 73 R R
9 <NA> <NA> <NA> Corbin Carroll Corbin Franklin 165 70 L L
10 <NA> <NA> <NA> Triston Casas Triston Ray 252 76 L R
debut finalGame retroID bbrefID deathDate birthDate
1 2022-09-30 2022-10-05 alvaf001 alvarfr01 <NA> 2001-11-19
2 2021-06-22 2022-10-04 franw002 francwa01 <NA> 2001-03-01
3 2022-08-10 2022-10-05 grisv001 grissva01 <NA> 2001-01-05
4 2022-05-28 2022-10-04 harrm004 harrimi04 <NA> 2001-03-07
5 2022-08-31 2022-10-05 hendg002 hendegu01 <NA> 2001-06-29
6 2022-09-23 2022-10-05 tovae001 tovarez01 <NA> 2001-08-01
7 2022-04-08 2022-10-04 abrac001 abramcj01 <NA> 2000-10-03
8 2022-04-20 2022-10-05 ariag002 ariasga01 <NA> 2000-02-27
9 2022-08-29 2022-10-05 carrc005 carroco02 <NA> 2000-08-21
10 2022-09-04 2022-10-05 casat001 casastr01 <NA> 2000-01-15
데이터를 확인한 김에 하나 더 해보자. Join함수를 통해서 선수 Managers.cvs 와 People.csv 파일을 하나로 합쳐보자. join함수에 대해서는 추후의 글을 통해 다시 한번 설명하도록 하겠다.
Managers %>%
filter(yearID > 2021,
rank == 1) %>%
left_join(People %>% select(playerID, nameFirst, nameLast, nameGiven))
위의 코드에 대한 결과값은 아래와 같다.
> Managers %>%
+ filter(yearID > 2021,
+ rank == 1) %>%
+ left_join(People %>% select(playerID, nameFirst, nameLast, nameGiven))
Joining with `by = join_by(playerID)`
playerID yearID teamID lgID inseason G W L rank plyrMgr nameFirst nameLast nameGiven
1 francte01 2022 CLE AL 1 162 92 70 1 N Terry Francona Terry Jon
2 bakerdu01 2022 HOU AL 1 162 106 56 1 N Dusty Baker Johnnie B.
3 booneaa01 2022 NYA AL 1 162 99 63 1 N Aaron Boone Aaron John
4 roberda07 2022 LAN NL 1 162 111 51 1 N Dave Roberts David Ray
5 showabu99 2022 NYN NL 1 162 101 61 1 N Buck Showalter William Nathaniel
6 marmool99 2022 SLN NL 1 162 93 69 1 N Oliver Marmol Oliver Jose
>
추가 정보: R 기본제공 데이터세트
참고로 Lahman패키지 외에, R에서 패키지 형태로 제공하는 데이터세트는 많이 있다. 업데이트가 덜 된 것 같지만, 아래 링크를 통해 더 많은 데이터세트를 확인해 보자.
https://stat.ethz.ch/R-manual/R-devel/library/datasets/html/00Index.html
R: The R Datasets Package
The R Datasets Package Documentation for package ‘datasets’ version 4.4.0 Help Pages A B C D E F H I J L M N O P Q R S T U V W ability.cov Ability and Intelligence Tests airmiles Passenger Miles on Commercial US Airlines, 1937-1960 AirPassengers Monthl
stat.ethz.ch
'R' 카테고리의 다른 글
데이터프레임 불러오기 4 - CSV 파일 복습 및 심화 (1) | 2024.02.26 |
---|---|
데이터프레임 불러오기 3 - 데이터베이스에서 직접 불러오기: DBI & RSQLite (1) | 2024.02.25 |
데이터프레임 불러오기 2 - 데이터베이스에서 직접 불러오기: RODBC (0) | 2024.02.22 |
R 속에서 SQL 사용하기 (0) | 2022.11.10 |
R? 데이터 분석을 위해 선택할 언어 (0) | 2022.05.13 |