외부 데이터 불러오기
이 글은 2018~2019년에 작성한 R 기반 강의 노트를 옮긴 것입니다. 코드 일부는 현재 패키지 버전과 다를 수 있습니다.
이번 포스팅에서는 R 외부에 저장되어 있는 csv, txt, xlsx 파일을 읽어서 데이터프레임으로 변환하는 방법에 대해 살펴보겠습니다. R에서는 이 외에도 SAS나 SPSS 등 다양한 프로그램에서 생성된 데이터도 읽어올 수 있으니 관심있는 분들은 DataCamp의 데이터 불러오기 튜토리얼을 확인하기 바랍니다.
이번에 실습할 데이터는 지난 R Crawler 4 JavaScript 우회하기 포스팅에서 수집한 xlsx 파일입니다. 제가 이 파일을 csv와 txt 파일로도 변환하여 저의 GitHub에 올렸으니 필요한 분은 다운로드하기 바랍니다.
데이터를 불러올 때 해당 파일의 인코딩 방식을 확인하는 편이 좋습니다. OS마다 기본 인코딩 설정이 서로 다른데요. Windows는 EUC-KR, Mac은 UTF-8입니다. 파일의 인코딩 방식을 확인하지 않고 읽어오는 경우, 주로 한글이 깨져보이는 경우가 발생하는데 이는 인코딩 설정이 서로 달라서 그렇습니다. 그러므로 파일의 인코딩 방식을 먼저 확인하고 자신의 OS에 따라 인코딩 설정 인자를 추가해주면 에러 발생을 사전에 방지할 수 있습니다.
파일의 인코딩 방식을 확인하는 방법
readr 패키지의 guess_encoding() 함수를 이용하면 쉽게 확인할 수 있습니다. 로컬 컴퓨터에 저장되어 있는 파일은 물론, GitHub처럼 온라인에 저장되어 있는 파일도 URL을 입력하는 것만으로도 해당 파일의 인코딩 방식을 확인할 수 있습니다.
# csv 파일이 저장된 GitHub URL을 지정합니다.
# 로컬 컴퓨터에 저장되어 있는 경우도 가능합니다.
filepath <- 'https://raw.githubusercontent.com/HelloDataScience/HelloDataScience/main/assets/data/2017_Baseball_hitter_stat.csv'
# readr 패키지의 guess_encoding() 함수로 파일의 인코딩 방식을 확인합니다.
library(readr)
guess_encoding(file = filepath)
## # A tibble: 1 x 2
## encoding confidence
## <chr> <dbl>
## 1 UTF-8 1.
guess_encoding() 함수를 실행하면 대상 파일에서 추정되는 인코딩 방식과 그 확률값을 출력해줍니다. 따라서 결과를 보고 확률값이 가장 큰 인코딩 방식으로 정해서 사용하면 됩니다. 위 코드를 실행한 결과, UTF-8이 100%인 것으로 출력되었습니다. 그러므로 Windows 사용자는 외부 파일을 읽어올 때 인코딩 방식을 UTF-8으로 지정해주어야 합니다. 하지만 Mac 사용자는 굳이 인코딩 방식을 설정해 줄 필요가 없습니다. 반대로, guess_encoding() 함수 실행 결과로 EUC-KR이 가장 높은 확률값을 가졌다면 이번에는 Mac 사용자들이 인코딩 방식으로 EUC-KR을 설정해주어야 합니다. 물론 Windows 사용자들은 설정할 필요가 없구요. 인코딩에 대해서 익숙하지 않으면 상당히 혼란을 헷갈리는 부분이니 이번 기회에 꼭 확인하고 기억하기 바랍니다.
외부 데이터 불러오기 ① : csv 파일
csv 파일은 일반적으로 read.csv() 함수를 이용하여 읽습니다. 주요 인자 설정은 다음과 같습니다.
header = TRUE: 첫 번째 행을 컬럼명으로 인식합니다. 기본 설정이므로 생략해도 됩니다.sep = ',': 데이터 구분자를 콤마(,)로 지정합니다. 기본 설정이므로 생략해도 됩니다.stringsAsFactors = FALSE: TRUE로 하면, 문자열 벡터를 요인(Factor) 타입으로 불러입니다.fileEncoding = 'UTF-8': UTF-8으로 인코딩된 파일을 불러옵니다. OS에 상관없이 파일 인코딩 방식에 맞춰 불러와야 한글이 깨지지 않습니다.
우리는 이미 csv 파일의 인코딩 방식이 무엇인지 확인하였습니다. Windows 사용자들은 반드시 fileEncoding = 'UTF-8'을 지정하고, Mac 사용자는 생략해도 됩니다.
# csv 파일을 읽고, dataCsv에 할당합니다.
dataCsv <- read.csv(file = filepath,
stringsAsFactors = FALSE,
fileEncoding = "UTF-8")
# 첫 10행만 미리보기 합니다.
head(x = dataCsv, n = 10L)
## 순위 선수명 팀명 경기 타석 타수 안타 홈런 득점 타점 볼넷 삼진 도루
## 1 1 최정 SK 130 527 430 136 46 89 113 70 107 1
## 2 2 김재환* 두산 144 636 544 185 35 110 115 81 123 4
## 3 3 최형우 KIA 142 629 514 176 26 98 120 96 82 0
## 4 4 박건우 두산 131 543 483 177 20 91 78 41 64 20
## 5 5 로사리오 한화 119 510 445 151 37 100 111 50 61 10
## 6 6 나성범 NC 125 561 498 173 24 103 99 48 116 17
## 7 7 손아섭 롯데 144 667 576 193 20 113 80 83 96 25
## 8 8 김선빈 KIA 137 529 476 176 5 84 64 39 40 4
## 9 9 버나디나 KIA 139 621 557 178 27 118 111 41 112 32
## 10 10 박민우 NC 106 452 388 141 3 84 47 46 51 11
## BABIP 타율 출루율 장타율 OPS wOBA WAR
## 1 0.316 0.316 0.427 0.684 1.111 0.442 7.30
## 2 0.385 0.340 0.429 0.603 1.032 0.427 7.22
## 3 0.362 0.342 0.450 0.576 1.026 0.430 7.20
## 4 0.390 0.366 0.424 0.582 1.006 0.424 7.04
## 5 0.324 0.339 0.414 0.661 1.075 0.436 5.75
## 6 0.413 0.347 0.415 0.584 0.999 0.416 5.64
## 7 0.374 0.335 0.420 0.514 0.934 0.398 5.60
## 8 0.393 0.370 0.420 0.477 0.897 0.391 5.19
## 9 0.354 0.320 0.373 0.540 0.913 0.380 5.01
## 10 0.408 0.363 0.441 0.472 0.913 0.404 4.92
# 데이터의 구조를 파악합니다.
str(object = dataCsv)
## 'data.frame': 292 obs. of 20 variables:
## $ 순위 : int 1 2 3 4 5 6 7 8 9 10 ...
## $ 선수명: chr "최정" "김재환*" "최형우" "박건우" ...
## $ 팀명 : chr "SK" "두산" "KIA" "두산" ...
## $ 경기 : int 130 144 142 131 119 125 144 137 139 106 ...
## $ 타석 : int 527 636 629 543 510 561 667 529 621 452 ...
## $ 타수 : int 430 544 514 483 445 498 576 476 557 388 ...
## $ 안타 : int 136 185 176 177 151 173 193 176 178 141 ...
## $ 홈런 : int 46 35 26 20 37 24 20 5 27 3 ...
## $ 득점 : int 89 110 98 91 100 103 113 84 118 84 ...
## $ 타점 : int 113 115 120 78 111 99 80 64 111 47 ...
## $ 볼넷 : int 70 81 96 41 50 48 83 39 41 46 ...
## $ 삼진 : int 107 123 82 64 61 116 96 40 112 51 ...
## $ 도루 : int 1 4 0 20 10 17 25 4 32 11 ...
## $ BABIP : num 0.316 0.385 0.362 0.39 0.324 0.413 0.374 0.393 0.354 0.408 ...
## $ 타율 : num 0.316 0.34 0.342 0.366 0.339 0.347 0.335 0.37 0.32 0.363 ...
## $ 출루율: num 0.427 0.429 0.45 0.424 0.414 0.415 0.42 0.42 0.373 0.441 ...
## $ 장타율: num 0.684 0.603 0.576 0.582 0.661 0.584 0.514 0.477 0.54 0.472 ...
## $ OPS : num 1.11 1.03 1.03 1.01 1.07 ...
## $ wOBA : num 0.442 0.427 0.43 0.424 0.436 0.416 0.398 0.391 0.38 0.404 ...
## $ WAR : num 7.3 7.22 7.2 7.04 5.75 5.64 5.6 5.19 5.01 4.92 ...
csv 파일을 읽을 때 일반적으로 read.csv() 함수를 이용한다고 표현했었는데요. 그 이유는 다음에 소개할 read.table()로도 읽을 수 있기 때문입니다.
외부 데이터 불러오기 ② : txt 파일
txt 파일은 read.table() 함수를 이용하여 읽습니다. read.csv() 함수와 주요 인자 설정이 비슷하지만 일부 차이점이 있습니다.
header = TRUE: 기본 설정 값이FALSE이므로 첫 번째 행을 컬럼명으로 인식하려면 반드시 지정해주어야 합니다.sep = '\t': 데이터 구분자를 탭(tab)으로 지정합니다. 기본 설정이므로 생략해도 됩니다.stringsAsFactors = FALSE:read.csv()와 같습니다.fileEncoding = 'UTF-8':read.csv()와 같습니다.
# 불러올 파일이 저장된 경로를 지정합니다.
filepath <- 'https://raw.githubusercontent.com/HelloDataScience/HelloDataScience/main/assets/data/2017_Baseball_hitter_stat.txt'
# 불러올 파일의 인코딩 방식을 확인합니다.
guess_encoding(file = filepath)
## # A tibble: 1 x 2
## encoding confidence
## <chr> <dbl>
## 1 UTF-8 1.
# txt 파일을 읽고, dataTxt에 할당합니다.
dataTxt <- read.table(file = filepath,
header = TRUE,
stringsAsFactor = FALSE,
fileEncoding = 'UTF-8')
# 첫 10행만 미리보기 합니다.
head(x = dataTxt, n = 10L)
## 순위 선수명 팀명 경기 타석 타수 안타 홈런 득점 타점 볼넷 삼진 도루
## 1 1 최정 SK 130 527 430 136 46 89 113 70 107 1
## 2 2 김재환* 두산 144 636 544 185 35 110 115 81 123 4
## 3 3 최형우 KIA 142 629 514 176 26 98 120 96 82 0
## 4 4 박건우 두산 131 543 483 177 20 91 78 41 64 20
## 5 5 로사리오 한화 119 510 445 151 37 100 111 50 61 10
## 6 6 나성범 NC 125 561 498 173 24 103 99 48 116 17
## 7 7 손아섭 롯데 144 667 576 193 20 113 80 83 96 25
## 8 8 김선빈 KIA 137 529 476 176 5 84 64 39 40 4
## 9 9 버나디나 KIA 139 621 557 178 27 118 111 41 112 32
## 10 10 박민우 NC 106 452 388 141 3 84 47 46 51 11
## BABIP 타율 출루율 장타율 OPS wOBA WAR
## 1 0.316 0.316 0.427 0.684 1.111 0.442 7.30
## 2 0.385 0.340 0.429 0.603 1.032 0.427 7.22
## 3 0.362 0.342 0.450 0.576 1.026 0.430 7.20
## 4 0.390 0.366 0.424 0.582 1.006 0.424 7.04
## 5 0.324 0.339 0.414 0.661 1.075 0.436 5.75
## 6 0.413 0.347 0.415 0.584 0.999 0.416 5.64
## 7 0.374 0.335 0.420 0.514 0.934 0.398 5.60
## 8 0.393 0.370 0.420 0.477 0.897 0.391 5.19
## 9 0.354 0.320 0.373 0.540 0.913 0.380 5.01
## 10 0.408 0.363 0.441 0.472 0.913 0.404 4.92
# 데이터의 구조를 파악합니다.
str(object = dataTxt)
## 'data.frame': 292 obs. of 20 variables:
## $ 순위 : int 1 2 3 4 5 6 7 8 9 10 ...
## $ 선수명: chr "최정" "김재환*" "최형우" "박건우" ...
## $ 팀명 : chr "SK" "두산" "KIA" "두산" ...
## $ 경기 : int 130 144 142 131 119 125 144 137 139 106 ...
## $ 타석 : int 527 636 629 543 510 561 667 529 621 452 ...
## $ 타수 : int 430 544 514 483 445 498 576 476 557 388 ...
## $ 안타 : int 136 185 176 177 151 173 193 176 178 141 ...
## $ 홈런 : int 46 35 26 20 37 24 20 5 27 3 ...
## $ 득점 : int 89 110 98 91 100 103 113 84 118 84 ...
## $ 타점 : int 113 115 120 78 111 99 80 64 111 47 ...
## $ 볼넷 : int 70 81 96 41 50 48 83 39 41 46 ...
## $ 삼진 : int 107 123 82 64 61 116 96 40 112 51 ...
## $ 도루 : int 1 4 0 20 10 17 25 4 32 11 ...
## $ BABIP : num 0.316 0.385 0.362 0.39 0.324 0.413 0.374 0.393 0.354 0.408 ...
## $ 타율 : num 0.316 0.34 0.342 0.366 0.339 0.347 0.335 0.37 0.32 0.363 ...
## $ 출루율: num 0.427 0.429 0.45 0.424 0.414 0.415 0.42 0.42 0.373 0.441 ...
## $ 장타율: num 0.684 0.603 0.576 0.582 0.661 0.584 0.514 0.477 0.54 0.472 ...
## $ OPS : num 1.11 1.03 1.03 1.01 1.07 ...
## $ wOBA : num 0.442 0.427 0.43 0.424 0.436 0.416 0.398 0.391 0.38 0.404 ...
## $ WAR : num 7.3 7.22 7.2 7.04 5.75 5.64 5.6 5.19 5.01 4.92 ...
read.table() 함수로 csv 파일을 읽으려면 인자 설정을 어떻게 바꿔주어야 할까요? csv는 엑셀에서 열리는 파일 타입이지만 콤마(,)로 구분된 텍스트 파일이라는 것을 알고 있다면 txt 파일과 크게 다를 바가 없다는 것도 이해할 수 있을 것입니다. txt 파일은 탭(tab)으로 구분된 텍스트 파일이구요. 그러므로 read.table() 함수로 csv 파일을 읽으려면 sep = ','만 추가해주면 됩니다. 실험 정신이 강한 분들은 한 번씩 해보세요. 이 포스팅에서는 생략합니다.
외부 데이터 불러오기 ③ : xslx 파일
GitHub에서는 csv와 txt 타입의 파일을 raw 형태로 브라우저에서 렌더링해주기 때문에 화면에서 바로 파일의 형태를 확인할 수 있습니다. 하지만 xslx은 꽤 복잡한(?) 엑셀 파일이므로 브라우저에서 확인할 수 없으며, 다운로드한 후 로컬 컴퓨터에서 여는 것은 가능합니다. 따라서 앞에서 실습했듯이 GitHub URL을 이용하여 csv나 txt 파일을 불러온 것처럼 할 수 없으므로, 처음에 알려드린 GitHub Repository에서 xlsx 파일을 다운로드한 후 적당한 폴더에 저장하기 바랍니다. 저는 현재 작업경로에 data라는 폴더를 새로 만든 후 그 안에 xlsx 파일을 저장하였습니다.
# 아래 GitHub Repository에 접속한 후 xlsx 파일을 다운로드 합니다.
# https://github.com/HelloDataScience/HelloDataScience/blob/main/assets/data/2017_Baseball_hitter_stat.xlsx
# 현재 작업경로에 데이터를 저장할 폴더(data)가 있는지 확인하고 없으면 새로 만듭니다.
folder <- './data/'
if (dir.exists(paths = folder) == FALSE) dir.create(path = folder)
# 다운로드한 xlsx 파일을 탐색기(또는 파인더)에서 data 폴더로 이동시킨 후
# 해당 파일이 들어있는지 확인합니다.
list.files(path = folder, pattern = 'xlsx')
## [1] "2017_Baseball_hitter_stat.xlsx"
xlsx 파일이 data 폴더에 있는 것을 확인했으므로 이제 파일을 읽어옵니다. xlsx 파일은 readxl 패키지의 read_excel() 함수로 읽습니다. 주요 인자는 다음과 같습니다.
path: 파일이 저장되어 있는 폴더와 파일명을 지정합니다.sheet = NULL: 데이터가 저장된 sheet가 여러 개일 경우 해당 sheet 이름을 지정합니다.col_names = TRUE: 첫 번째 행을 컬럼명으로 인식합니다. 기본값이TRUE입니다.
# 필요 패키지를 불러옵니다.
library(readxl)
# xlsx 파일을 읽어, dataXls에 할당합니다.
dataXls <- read_excel(path = './data/2017_Baseball_hitter_stat.xlsx', sheet = NULL)
# 첫 10행만 미리보기 합니다.
head(x = dataXls, n = 10L)
## # A tibble: 10 x 20
## 순위 선수명 팀명 경기 타석 타수 안타 홈런 득점 타점 볼넷
## <dbl> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 1. 최정 SK 130. 527. 430. 136. 46. 89. 113. 70.
## 2 2. 김재환* 두산 144. 636. 544. 185. 35. 110. 115. 81.
## 3 3. 최형우 KIA 142. 629. 514. 176. 26. 98. 120. 96.
## 4 4. 박건우 두산 131. 543. 483. 177. 20. 91. 78. 41.
## 5 5. 로사리오 한화 119. 510. 445. 151. 37. 100. 111. 50.
## 6 6. 나성범 NC 125. 561. 498. 173. 24. 103. 99. 48.
## 7 7. 손아섭 롯데 144. 667. 576. 193. 20. 113. 80. 83.
## 8 8. 김선빈 KIA 137. 529. 476. 176. 5. 84. 64. 39.
## 9 9. 버나디나 KIA 139. 621. 557. 178. 27. 118. 111. 41.
## 10 10. 박민우 NC 106. 452. 388. 141. 3. 84. 47. 46.
## # ... with 9 more variables: 삼진 <dbl>, 도루 <dbl>, BABIP <dbl>,
## # 타율 <dbl>, 출루율 <dbl>, 장타율 <dbl>, OPS <dbl>, wOBA <dbl>,
## # WAR <dbl>
# 데이터의 구조를 파악합니다.
str(object = dataXls)
## Classes 'tbl_df', 'tbl' and 'data.frame': 292 obs. of 20 variables:
## $ 순위 : num 1 2 3 4 5 6 7 8 9 10 ...
## $ 선수명: chr "최정" "김재환*" "최형우" "박건우" ...
## $ 팀명 : chr "SK" "두산" "KIA" "두산" ...
## $ 경기 : num 130 144 142 131 119 125 144 137 139 106 ...
## $ 타석 : num 527 636 629 543 510 561 667 529 621 452 ...
## $ 타수 : num 430 544 514 483 445 498 576 476 557 388 ...
## $ 안타 : num 136 185 176 177 151 173 193 176 178 141 ...
## $ 홈런 : num 46 35 26 20 37 24 20 5 27 3 ...
## $ 득점 : num 89 110 98 91 100 103 113 84 118 84 ...
## $ 타점 : num 113 115 120 78 111 99 80 64 111 47 ...
## $ 볼넷 : num 70 81 96 41 50 48 83 39 41 46 ...
## $ 삼진 : num 107 123 82 64 61 116 96 40 112 51 ...
## $ 도루 : num 1 4 0 20 10 17 25 4 32 11 ...
## $ BABIP : num 0.316 0.385 0.362 0.39 0.324 0.413 0.374 0.393 0.354 0.408 ...
## $ 타율 : num 0.316 0.34 0.342 0.366 0.339 0.347 0.335 0.37 0.32 0.363 ...
## $ 출루율: num 0.427 0.429 0.45 0.424 0.414 0.415 0.42 0.42 0.373 0.441 ...
## $ 장타율: num 0.684 0.603 0.576 0.582 0.661 0.584 0.514 0.477 0.54 0.472 ...
## $ OPS : num 1.11 1.03 1.03 1.01 1.07 ...
## $ wOBA : num 0.442 0.427 0.43 0.424 0.436 0.416 0.398 0.391 0.38 0.404 ...
## $ WAR : num 7.3 7.22 7.2 7.04 5.75 5.64 5.6 5.19 5.01 4.92 ...
지금까지 3가지 타입의 데이터를 불러온 뒤 저장한 객체들이 서로 같은지 여부를 identical() 함수를 이용하여 확인해보겠습니다.
# csv 파일을 불러온 객체와 txt 파일을 블러온 객체가 같은지 확인합니다.
identical(dataCsv, dataTxt)
## [1] TRUE
# csv 파일을 불러온 객체와 xlsx 파일을 블러온 객체가 같은지 확인합니다.
identical(dataCsv, dataXls)
## [1] FALSE
csv와 txt 파일을 불러온 객체는 서로 같았으나, xlsx 파일을 불러온 객체와는 서로 다르다는 것을 확인할 수 있었습니다. 그 이유는 read_excel() 함수가 결과 객체를 데이터프레임(data.frame)이 아닌 티블(tibble) 형태로 반환하기 때문입니다. 티블은 데이터프레임의 일종이지만 데이터프레임이 가지고 있는 여러 가지 단점을 보완한 객체 타입입니다.[1]
이상으로 R 외부 데이터를 불러오는 방법에 대해 알아보았습니다.
[1] 티블에 대한 간략한 설명은 관련 비네트를 참조하시기 바랍니다. RStudio에서 vignette("tibble")을 입력한 후 실행하면 Help 창에서 관련 내용을 확인할 수 있습니다.