9 데이터 불러오기
9.1 들어가기
R 패키지가 제공하는 데이터를 이용하여 데이터 과학 도구를 익히는 것은 좋은 방법이다. 그렇지만 어느 시점에 이르러서는 학습을 중단하고 자신의 데이터로 작업해보고 싶어질 것이다. 이 장에서는 일반 텍스트 직사각형 파일을 R로 불러오는 방법을 배운다. 데이터 불러오기 맛보기만 할 뿐이지만, 여기에서 배우는 많은 원칙을 다른 형태의 데이터에도 적용할 수 있다. 이 장의 마무리에서 다른 유형의 데이터에 유용한 패키지 몇 가지를 소개한다.
9.2 시작하기
readr 함수 대부분은 플랫 파일을 데이터프레임으로 바꾸는 것과 연관이 있다.
read_csv()
는 쉼표로 구분된 파일을 읽고,read_tsv()
는 탭-구분 파일을 읽는다.read_delim()
은 임의의 구분자로 된 파일을 읽는다.read_fwf()
는 고정 너비 파일을 읽는다. 필드 너비는fwf_widths()
를 이용하여, 필드 위치는fwf_positions()
를 이용하여 지정할 수 있다.read_table()
은 고정 너비 파일의 일반적 변형 형태인 열이 공백으로 구분된 파일을 읽는다.read_log()
는 Apache 스타일의 로그 파일을 읽는다. (하지만read_log()
기반 구축되어 더 많은 유용한 도구를 제공하는 webreadr 도 확인하라.)
이 함수들은 문법이 모두 비슷하다. 하나를 익히면 나머지는 쉽게 사용할 수 있다.
이 장의 나머지 부분에서는 read_csv()
에 초점을 맞출 것이다. CSV 파일은 가장
일반적인 형태의 데이터 저장 형태일 뿐 아니라 read_csv()
를 이해하면 readr 의
다른 모든 함수에 쉽게 적용할 수 있다.
9.3 파일에서 데이터 읽어오기
열 이름들 (헤더행으로 불림) 이 한 행으로 있고 데이터 여섯 행이 있는 간단한 CSV 는 다음과 같이 생겼다.
#> Student ID,Full Name,favourite.food,mealPlan,AGE
#> 1,Sunil Huffmann,Strawberry yoghurt,Lunch only,4
#> 2,Barclay Lynn,French fries,Lunch only,5
#> 3,Jayendra Lyne,N/A,Breakfast and lunch,7
#> 4,Leon Rossini,Anchovies,Lunch only,
#> 5,Chidiegwu Dunkel,Pizza,Breakfast and lunch,five
#> 6,Güvenç Attila,Ice cream,Lunch only,6
,
가 열들을 구분한다는 것을 주목하라.
표 9.1 는 같은 데이터를 테이블로 표현하고 있다.
Student ID | Full Name | favourite.food | mealPlan | AGE |
---|---|---|---|---|
1 | Sunil Huffmann | Strawberry yoghurt | Lunch only | 4 |
2 | Barclay Lynn | French fries | Lunch only | 5 |
3 | Jayendra Lyne | N/A | Breakfast and lunch | 7 |
4 | Leon Rossini | Anchovies | Lunch only | NA |
5 | Chidiegwu Dunkel | Pizza | Breakfast and lunch | five |
6 | Güvenç Attila | Ice cream | Lunch only | 6 |
read_csv()
의 첫 번째 인수가 가장 중요한데 바로 읽으려고 하는 파일의 경로다.
heights <- read_csv("data/students.csv")
#> Rows: 6 Columns: 5
#> ── Column specification ────────────────────────────────────────────────────────
#> Delimiter: ","
#> chr (4): Full Name, favourite.food, mealPlan, AGE
#> dbl (1): Student ID
#>
#> ℹ Use `spec()` to retrieve the full column specification for this data.
#> ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
read_csv()
를 실행하면 각 열의 이름과 유형을 제공하는 열 사양이 화면 출력된다. 이는 readr 에서 중요한 부분이다. 24.2 섹션에서 다시 살펴보겠다.
인라인 CSV 파일을 넣을 수도 있다. 이것은 readr 로 실험해볼 때와 다른 사람들과 공유할 재현 가능한 예제를 만들 때 유용하다.
read_csv("a,b,c
1,2,3
4,5,6")
#> # A tibble: 2 × 3
#> a b c
#> <dbl> <dbl> <dbl>
#> 1 1 2 3
#> 2 4 5 6
두 경우 모두 read_csv()
는 데이터의 첫 번째 줄을 열 이름으로 사용한다. 이는 매우 일반적인 규칙이다. 이 동작을 조정해야 하는 경우는 두 가지이다.
-
파일 앞 부분에 메타 데이터 몇 라인이 있는 경우가 있다.
skip = n
을 사용하여 첫n
라인을 건너 뛸 수 있다. 또는comment = "#"
을 사용하여#
으로 시작하는 모든 라인들을 무시할 수 있다.read_csv("메타 데이터 첫번째 행 메타 데이터 두번째 행 x,y,z 1,2,3", skip = 2) #> Rows: 1 Columns: 3 #> ── Column specification ──────────────────────────────────────────────────────── #> Delimiter: "," #> dbl (3): x, y, z #> #> ℹ Use `spec()` to retrieve the full column specification for this data. #> ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message. #> # A tibble: 1 × 3 #> x y z #> <dbl> <dbl> <dbl> #> 1 1 2 3 read_csv("# 건너뛰고 싶은 주석 x,y,z 1,2,3", comment = "#") #> Rows: 1 Columns: 3 #> ── Column specification ──────────────────────────────────────────────────────── #> Delimiter: "," #> dbl (3): x, y, z #> #> ℹ Use `spec()` to retrieve the full column specification for this data. #> ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message. #> # A tibble: 1 × 3 #> x y z #> <dbl> <dbl> <dbl> #> 1 1 2 3
-
데이터에 열 이름이 없을 수 있다.
col_names = FALSE
를 사용하면read_csv()
가 첫 행을 헤드로 취급하지 않고 대신 X1에서 Xn까지 순차적으로 이름을 붙인다.read_csv("1,2,3\n4,5,6", col_names = FALSE) #> # A tibble: 2 × 3 #> X1 X2 X3 #> <dbl> <dbl> <dbl> #> 1 1 2 3 #> 2 4 5 6
(
"\n"
은 새 줄을 추가하는 편리한 단축키이다. 문자열기초에서 이 단축어와 문자열 이스케이프의 다른 유형에 대해 자세히 배운다.) 다른 방법으로는col_names
에 열 이름으로 사용할 문자형 벡터를 전달할 수도 있다.read_csv("1,2,3\n4,5,6", col_names = c("x", "y", "z")) #> Rows: 2 Columns: 3 #> ── Column specification ──────────────────────────────────────────────────────── #> Delimiter: "," #> dbl (3): x, y, z #> #> ℹ Use `spec()` to retrieve the full column specification for this data. #> ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message. #> # A tibble: 2 × 3 #> x y z #> <dbl> <dbl> <dbl> #> 1 1 2 3 #> 2 4 5 6
일반적으로 조정이 필요한 또 다른 옵션은 na
이다. 파일에서 결측값을 나타내는데 사용되는 값(들)을 지정한다.
```r
read_csv("a,b,c\n1,2,.", na = ".")
#> Rows: 1 Columns: 3
#> ── Column specification ────────────────────────────────────────────────────────
#> Delimiter: ","
#> dbl (2): a, b
#> lgl (1): c
#>
#> ℹ Use `spec()` to retrieve the full column specification for this data.
#> ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
#> # A tibble: 1 × 3
#> a b c
#> <dbl> <dbl> <lgl>
#> 1 1 2 NA
```
여기까지 배운 것들로 실제로 마주하게 될 CSV 파일의 75% 정도를 불러올 수
있다. 또한 탭으로 구분된 파일을 read_tsv()
를 사용하여, 혹은 고정간격 파일을
read_fwf()
를 사용하여 불러오는 데도 쉽게 적용할 수 있다. 더 복잡한 파일을 읽으려면
readr 이 각 열을 파싱하여 R 벡터로 바꾸는 방법에 대해 자세히 배워야한다.
9.3.1 첫 단계
students
데이터를 살펴보자.
In the favourite.food
열에, foot 아이템들과 문자 N/A
가 많이 있는데,이는 R 이 “not available (해당없음)” 으로 인식해야 하는 실제 NA
가 되었어야 하는 것이다.
na
인수를 사용해서 언급해야하는 내용이다.
students <- read_csv("data/students.csv", na = c("N/A", ""))
students
#> # A tibble: 6 × 5
#> `Student ID` `Full Name` favourite.food mealPlan AGE
#> <dbl> <chr> <chr> <chr> <chr>
#> 1 1 Sunil Huffmann Strawberry yoghurt Lunch only 4
#> 2 2 Barclay Lynn French fries Lunch only 5
#> 3 3 Jayendra Lyne <NA> Breakfast and lunch 7
#> 4 4 Leon Rossini Anchovies Lunch only <NA>
#> 5 5 Chidiegwu Dunkel Pizza Breakfast and lunch five
#> 6 6 Güvenç Attila Ice cream Lunch only 6
데이터를 읽어들였다면, 첫번째 단계는 일반적으로 이후 분석과정에서 다루기 쉽도록 변환과정이 포함된다.
예를들어, 읽어들인 students
파일에서 열이름들은 표준적이지 않은 방법으로 포맷되어 있다.
dplyr::rename()
으로 하나하나 이름을 바꾸거나, janitor::clean_names()
함수를 사용하여 한꺼번에 스네이크 케이스로 모두 변환할 수 있다.4
이 함수는 데이터프레임을 입력으로 변수명이 스네이크 케이스로 변환된 데이터프레임을 반환한다.
library(janitor)
students %>%
clean_names()
#> # A tibble: 6 × 5
#> student_id full_name favourite_food meal_plan age
#> <dbl> <chr> <chr> <chr> <chr>
#> 1 1 Sunil Huffmann Strawberry yoghurt Lunch only 4
#> 2 2 Barclay Lynn French fries Lunch only 5
#> 3 3 Jayendra Lyne <NA> Breakfast and lunch 7
#> 4 4 Leon Rossini Anchovies Lunch only <NA>
#> 5 5 Chidiegwu Dunkel Pizza Breakfast and lunch five
#> 6 6 Güvenç Attila Ice cream Lunch only 6
데이터를 읽은 후 또 하나의 일반적인 작업은 변수 유형을 고려하는 것이다.
예를 들어, meal_type
(급식유형) 는 가질 수 있는 값이 알려진 범주형 변수이다.
범주형 변수들과 작업하기 위해 R 에 있는 팩터형을 사용할 수 있다.
factor()
함수를 사용하여 이 변수를 팩터형으로 변환할 수 있다.
21 장에서 팩터형에 대해 더 자세히 배울 것이다.
students <- students %>%
clean_names() %>%
mutate(meal_plan = factor(meal_plan))
students
#> # A tibble: 6 × 5
#> student_id full_name favourite_food meal_plan age
#> <dbl> <chr> <chr> <fct> <chr>
#> 1 1 Sunil Huffmann Strawberry yoghurt Lunch only 4
#> 2 2 Barclay Lynn French fries Lunch only 5
#> 3 3 Jayendra Lyne <NA> Breakfast and lunch 7
#> 4 4 Leon Rossini Anchovies Lunch only <NA>
#> 5 5 Chidiegwu Dunkel Pizza Breakfast and lunch five
#> 6 6 Güvenç Attila Ice cream Lunch only 6
meal_type
변수의 값들은 변하지 않았지만 변수명 아래 표기된 변수 유형이 문자형 (<chr>
) 에서 팩터형 (<fct>
) 으로 바뀐 것을 주목하라.
데이터 분석으로 넘어가기 전에, age
열을 제대로 만들고 싶을 수 있다: 하나의 관측값이 수치형 5
대신 five
로 입력되어 있기 때문에, 이 변수는 문자형으로 현재 되어 있다.
이러한 문제를 바로잡는 것에 대해서는 25 장에서 논의할 것이다.
9.3.2 베이스 R과 비교
R 을 이전에 사용했던 사람이라면, 우리가 read.csv()
를 사용하지 않는 이유가 궁금할 것이다. 베이스 함수보다 readr 함수가 좋은 이유는 다음과 같다.
일반적으로 베이스 함수보다 훨씬 더(~10배) 빠르다. 오래 걸리는 작업은 진 행 표시줄을 통해 상황을 알 수 있다. raw speed로 작업하려면
data.table::fread()
를 사용해보라. 이 함수는 tidyverse 에 잘 어울리지는 않지만, 훨씬 더 빠를 수 있다.티블을 생성한다. 문자 벡터를 팩터형으로 변환하지도, 행 이름을 사용하거 나 열 이름을 변경하지도 않는다. 베이스 R 함수는 변환, 변경하기 때문에 불 편하다.
좀 더 재현 가능하다. 베이스 R 함수는 운영체제 및 환경 변수의 일부 동작을 상속하므로 자신의 컴퓨터에서 작동하는 불러오기 코드가 다른 사람의 컴퓨터에서 작동하지 않을 수 있다.
9.3.3 연습문제
필드가 “|” 로 분리된 파일을 읽으려면 어떤 함수를 사용하겠는가?
read_csv()
와read_tsv()
가 공통으로 가진 인수는file
,skip
,comment
외에 또 무엇이 있는가?read_fwf()
에서 가장 중요한 인수는 무엇인가?-
CSV 파일의 문자열에 쉼표가 포함되는 경우가 있다. 그것들이 문제를 일으 키지 않게 하려면
"
혹은'
와 같은 인용 문자로 둘러싸일 필요가 있다.read_csv()
는 인용 문자가"
라고 가정한다. 이를 변경하려면read_delim()
을 대신 사용하면 된다. 다음 텍스트를 데이터프레임으로 읽으려면 어떤 인수를 설정해야하는가?"x,y\n1,'a,b'" #> [1] "x,y\n1,'a,b'"
-
다음 각 인라인 CSV 파일에 어떤 문제가 있는지 확인하라. 코드를 실행하면 어떻게 되는가?
read_csv("a,b\n1,2,3\n4,5,6") #> Warning: One or more parsing issues, see `problems()` for details #> Rows: 2 Columns: 2 #> ── Column specification ──────────────────────────────────────────────────────── #> Delimiter: "," #> dbl (1): a #> #> ℹ Use `spec()` to retrieve the full column specification for this data. #> ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message. #> # A tibble: 2 × 2 #> a b #> <dbl> <dbl> #> 1 1 23 #> 2 4 56 read_csv("a,b,c\n1,2\n1,2,3,4") #> Warning: One or more parsing issues, see `problems()` for details #> Rows: 2 Columns: 3 #> ── Column specification ──────────────────────────────────────────────────────── #> Delimiter: "," #> dbl (2): a, b #> #> ℹ Use `spec()` to retrieve the full column specification for this data. #> ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message. #> # A tibble: 2 × 3 #> a b c #> <dbl> <dbl> <dbl> #> 1 1 2 NA #> 2 1 2 34 read_csv("a,b\n\"1") #> Rows: 0 Columns: 2 #> ── Column specification ──────────────────────────────────────────────────────── #> Delimiter: "," #> chr (2): a, b #> #> ℹ Use `spec()` to retrieve the full column specification for this data. #> ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message. #> # A tibble: 0 × 2 #> # … with 2 variables: a <chr>, b <chr> read_csv("a,b\n1,2\na,b") #> Rows: 2 Columns: 2 #> ── Column specification ──────────────────────────────────────────────────────── #> Delimiter: "," #> chr (2): a, b #> #> ℹ Use `spec()` to retrieve the full column specification for this data. #> ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message. #> # A tibble: 2 × 2 #> a b #> <chr> <chr> #> 1 1 2 #> 2 a b read_csv("a;b\n1;3") #> Rows: 1 Columns: 1 #> ── Column specification ──────────────────────────────────────────────────────── #> Delimiter: "," #> chr (1): a;b #> #> ℹ Use `spec()` to retrieve the full column specification for this data. #> ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message. #> # A tibble: 1 × 1 #> `a;b` #> <chr> #> 1 1;3
9.4 여러 파일에서 데이터 읽어오기
때로는 데이터가 하나의 파일에 포함되어 있지 않고 여러 파일들에 나누어져 있는 경우가 있다.
예를 들어, 각 월별 매출 데이터가, 다음과 같이 각 달마다 따로 있을 수 있다: 1 월은 01-sales.csv
, 2 월은 02-sales.csv
, 3 월은 03-sales.csv
.
read_csv()
을 사용하여 이러한 데이터를 한번에 읽고 서로 쌓아서 하나의 데이터프레임으로 만들 수 있다.
sales_files <- c("data/01-sales.csv", "data/02-sales.csv", "data/03-sales.csv")
read_csv(sales_files, id = "file")
#> Rows: 19 Columns: 6
#> ── Column specification ────────────────────────────────────────────────────────
#> Delimiter: ","
#> chr (1): month
#> dbl (4): year, brand, item, n
#>
#> ℹ Use `spec()` to retrieve the full column specification for this data.
#> ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
#> # A tibble: 19 × 6
#> file month year brand item n
#> <chr> <chr> <dbl> <dbl> <dbl> <dbl>
#> 1 data/01-sales.csv January 2019 1 1234 3
#> 2 data/01-sales.csv January 2019 1 8721 9
#> 3 data/01-sales.csv January 2019 1 1822 2
#> 4 data/01-sales.csv January 2019 2 3333 1
#> 5 data/01-sales.csv January 2019 2 2156 9
#> 6 data/01-sales.csv January 2019 2 3987 6
#> # … with 13 more rows
id
인수를 추가로 사용하여, 데이터소스파일을 표시하는 file
이라고 부르는 새로운 열을 데이터프레임에 추가했다.
이렇게 하면 관측값들의 원소스를 따라가며 살펴보는데 도움을 주는 식별 열들이 없는 경우에 특별히 도움을 준다.
읽어오고 싶은 파일들이 많은 경우, 파일이름들을 리스트로 작성하는 것이 귀찮을 수 있다.
fs 패키지의 dir_ls()
함수를 사용하여 파일이름에 패턴을 매칭하여 파일을 찾아올 수 있다.
9.5 파일에 쓰기
readr 에는 디스크에 데이터를 다시 기록하는 데 유용한 함수, write_csv()
와 write_tsv()
가 있다. 두 함수 모두 다음 동작을 통해 출력 파일이 올바르게 다시 읽힐 수 있게 한다.
- 항상 UTF-8 로 문자열을 인코딩한다.
- 날짜와 날짜-시간을 ISO 8601 형식으로 저장하여 어디에서든 쉽게 파싱될 수 있게 한다.
CSV 파일을 엑셀로 내보내려면 write_excel_csv()
를 사용하라. 이는 파일의 시작 부분에 특수 문자(‘byte order mark’)를 작성하여, UTF-8 인코딩을 사용하고 있음을 엑셀에 전달한다.
가장 중요한 인수는 x
(저장할 데이터프레임)와 path
(그 데이터프레임을 저장할 위치)이다. 결측값을 지정하는 인수, na
와 기존 파일에 첨부할지를 지정하는 인수 append
도 있다.
write_csv(students, "students.csv")
CSV 로 저장하면 유형 정보가 없어진다는 것에 유의하라.
students
#> # A tibble: 6 × 5
#> student_id full_name favourite_food meal_plan age
#> <dbl> <chr> <chr> <fct> <chr>
#> 1 1 Sunil Huffmann Strawberry yoghurt Lunch only 4
#> 2 2 Barclay Lynn French fries Lunch only 5
#> 3 3 Jayendra Lyne <NA> Breakfast and lunch 7
#> 4 4 Leon Rossini Anchovies Lunch only <NA>
#> 5 5 Chidiegwu Dunkel Pizza Breakfast and lunch five
#> 6 6 Güvenç Attila Ice cream Lunch only 6
write_csv(students, "students-2.csv")
read_csv("students-2.csv")
#> # A tibble: 6 × 5
#> student_id full_name favourite_food meal_plan age
#> <dbl> <chr> <chr> <chr> <chr>
#> 1 1 Sunil Huffmann Strawberry yoghurt Lunch only 4
#> 2 2 Barclay Lynn French fries Lunch only 5
#> 3 3 Jayendra Lyne <NA> Breakfast and lunch 7
#> 4 4 Leon Rossini Anchovies Lunch only <NA>
#> 5 5 Chidiegwu Dunkel Pizza Breakfast and lunch five
#> 6 6 Güvenç Attila Ice cream Lunch only 6
이런 이유로 중간 결과를 캐싱하기에 CSV 를 아주 신뢰할 수 없다. 불러올 때마다 열 사양을 다시 만들어야 한다. 두 가지 대안이 있다.
-
write_rds()
과read_rds()
는 베이스 함수인readRDS()
와saveRDS()
의 래퍼 함수들이다. 이들은 RDS 라는 R 의 커스텀 바이너리 형식으로 데이터를 저장한다.write_rds(students, "students.rds") read_rds("students.rds") #> # A tibble: 6 × 5 #> student_id full_name favourite_food meal_plan age #> <dbl> <chr> <chr> <fct> <chr> #> 1 1 Sunil Huffmann Strawberry yoghurt Lunch only 4 #> 2 2 Barclay Lynn French fries Lunch only 5 #> 3 3 Jayendra Lyne <NA> Breakfast and lunch 7 #> 4 4 Leon Rossini Anchovies Lunch only <NA> #> 5 5 Chidiegwu Dunkel Pizza Breakfast and lunch five #> 6 6 Güvenç Attila Ice cream Lunch only 6
-
feather 패키지는 다른 프로그래밍 언어와 공유할 수 있는 빠른 바이너리 파일 형식을 구현한다.
library(feather) write_feather(students, "students.feather") read_feather("students.feather")
feather 는 RDS 보다 대체적으로 빠르며 R 외부에서도 사용할 수 있다. RDS 는 리스트-열(?? 장에서 배울 것이다)을 지원하지만 feather 는 현재 지원하지 않는다.
9.6 기타 데이터 유형
다른 유형의 데이터를 R로 가져오려면 다음에 나열된 tidyverse 패키지로 시작 하는 것이 좋다. 이 패키지들은 완벽하지는 않지만 이들부터 시작하면 좋다. 직사각형 데이터에 대해 다음 패키지들이 있다.
readxl 은 엑셀 파일(
.xls
와.xlsx
)을 읽을 수 있다. 25 장에서 엑셀 스프레드시트에 저장된 데이터로 작업하는 것에 대해 더 살펴보라.googlesheets4 은 구글 시트를 읽는다. 25 장에서 구글 시트에 저장된 데이터로 작업하는 것에 대해 다룬다.
DBI 를 데이터베이스 특화 백엔드(예: RMySQL, RSQLite, RPostgreSQL 등)와 함께 사용하면 데이터베이스에 대해 SQL 쿼리를 실행하고 데이터프레임을 반환할 수 있다. ?? 장에서 데이터베이스 작업하는 것에 대해 더 살펴보자.
haven 은 SPSS, Stata, SAS 파일을 읽을 수 있다.
계층적 데이터의 경우, JSON 에는 Jeroen Ooms 가 개발한 jsonlite를 사용하고 XML에는 xml2 를 사용하면 된다. 이에 관한 좋은 예제는 Jenny Bryan 의 https://jennybc.github.io/purrr-tutorial 에서 볼 수 있다.
다른 파일 유형의 경우, R 데이터 가져오기/내보내기 매뉴얼 과 rio 패키지를 참고해보라.