1.5 데이터 저장하기

readr 패키지가 제공하는 주요 데이터 저장 함수는 write_로 시작하는데, 다음과 같은 함수들이 있다.

  • 쉼표로 구분된 파일로 저장 : write_csv(x, path, na = “NA,” append = FALSE, col_names = !append)
  • 임의의 구분자로 구분된 파일로 저장 : write_delim(x, path, delim = " “, na =”NA", append = FALSE, col_names = !append)
  • excel의 csv 파일로 저장 : write_excel_csv(x, path, na = “NA,” append = FALSE, col_names = !append)
  • 문자열을 파일로 저장 : write_file(x, path, append = FALSE)
  • 문자열 벡터를 파일로 저장, 요소 한 개를 한 줄로 저장 : write_lines(x, path, na = “NA,” append = FALSE)
  • 개체를 rds 파일로 저장 : write_rds(x, path, compress = c(“none,” “gz,” “bz2,” “xz”), …)
  • 탭으로 구분된 파일로 저장 : write_tsv(x, path, na = “NA,” append = FALSE, col_names = !append)

1.5.1 write_csv()

write_csv(challenge, "data1/out/challenge.csv")

challenge
## # A tibble: 2,000 x 2
##        x y         
##    <dbl> <date>    
##  1   404 NA        
##  2  4172 NA        
##  3  3004 NA        
##  4   787 NA        
##  5    37 NA        
##  6  2332 NA        
##  7  2489 NA        
##  8  1449 NA        
##  9  3665 NA        
## 10  3863 NA        
## # ... with 1,990 more rows
write_csv(challenge, "data1/out/challenge-2.csv")
read_csv("data1/out/challenge-2.csv")
## 
## -- Column specification --------------------------------------------------------
## cols(
##   x = col_double(),
##   y = col_logical()
## )
## Warning: 1000 parsing failures.
##  row col           expected     actual                        file
## 1001   y 1/0/T/F/TRUE/FALSE 2015-01-16 'data1/out/challenge-2.csv'
## 1002   y 1/0/T/F/TRUE/FALSE 2018-05-18 'data1/out/challenge-2.csv'
## 1003   y 1/0/T/F/TRUE/FALSE 2015-09-05 'data1/out/challenge-2.csv'
## 1004   y 1/0/T/F/TRUE/FALSE 2012-11-28 'data1/out/challenge-2.csv'
## 1005   y 1/0/T/F/TRUE/FALSE 2020-01-13 'data1/out/challenge-2.csv'
## .... ... .................. .......... ...........................
## See problems(...) for more details.
## # A tibble: 2,000 x 2
##        x y    
##    <dbl> <lgl>
##  1   404 NA   
##  2  4172 NA   
##  3  3004 NA   
##  4   787 NA   
##  5    37 NA   
##  6  2332 NA   
##  7  2489 NA   
##  8  1449 NA   
##  9  3665 NA   
## 10  3863 NA   
## # ... with 1,990 more rows

1.5.2 write_rds()

write_rds(challenge, "data1/out/challenge.rds")
read_rds("data1/out/challenge.rds")
## # A tibble: 2,000 x 2
##        x y         
##    <dbl> <date>    
##  1   404 NA        
##  2  4172 NA        
##  3  3004 NA        
##  4   787 NA        
##  5    37 NA        
##  6  2332 NA        
##  7  2489 NA        
##  8  1449 NA        
##  9  3665 NA        
## 10  3863 NA        
## # ... with 1,990 more rows

1.5.3 write_feather()

library(feather)
write_feather(challenge, "data1/out/challenge.feather")
read_feather("data1/out/challenge.feather")
## # A tibble: 2,000 x 2
##        x y         
##    <dbl> <date>    
##  1   404 NA        
##  2  4172 NA        
##  3  3004 NA        
##  4   787 NA        
##  5    37 NA        
##  6  2332 NA        
##  7  2489 NA        
##  8  1449 NA        
##  9  3665 NA        
## 10  3863 NA        
## # ... with 1,990 more rows

앞의 예에서 불러온 challenge.csv 데이터 세트를 data1/out 폴더에 저장해 보기로 한다.

challenge <- read_csv(
  readr_example("challenge.csv"), 
  col_types = cols(
    x = col_double(),
    y = col_date()
  )
)
tail(challenge)
## # A tibble: 6 x 2
##       x y         
##   <dbl> <date>    
## 1 0.805 2019-11-21
## 2 0.164 2018-03-29
## 3 0.472 2014-08-04
## 4 0.718 2015-08-16
## 5 0.270 2020-02-04
## 6 0.608 2019-01-06

readr에는 디스크에 데이터를 다시 기록하는 데 유용한 함수인 write_csv()write_tsv() 가 있다. 두 함수 모두 다음 동작을 통해 출력 파일이 올바르게 다시 읽힐 수 있게 한다. - 항상 UTF-8로 문자열을 인코딩한다. - 날짜날짜-시간ISO 8601 형식으로 저장하여 어디에서든 쉽게 파싱될 수 있게 한다.

또한, CSV 파일을 엑셀로 내보내려면 write_excel_csv() 를 사용하면 된다. 이는 파일의 시작 부분에 특수 문자(‘byte order mark’)를 작성하여, UTF-8 인코딩을 사용하고 있음을 엑셀에 전달한다.

인수

  • 가장 중요한 인수x (저장할 데이터프레임)와 path (그 데이터프레임을 저장할 위치)이다.
  • 결측값을 지정하는 인수, na=기존 파일에 첨부할지를 지정하는 인수 append= 도 있다.
write_csv(challenge, "data1/out/challenge.csv")

1.5.4 read_csv()read_rds()의 비교

CSV 로 저장하면 데이터의 유형 정보가 없어진다는 것에 유의하라. 즉, plain text 파일로 저장이 된다.

challenge
## # A tibble: 2,000 x 2
##        x y         
##    <dbl> <date>    
##  1   404 NA        
##  2  4172 NA        
##  3  3004 NA        
##  4   787 NA        
##  5    37 NA        
##  6  2332 NA        
##  7  2489 NA        
##  8  1449 NA        
##  9  3665 NA        
## 10  3863 NA        
## # ... with 1,990 more rows
write_csv(challenge, "data1/out/challenge-2.csv")
challenge1 <- read_csv("data1/out/challenge-2.csv")
## 
## -- Column specification --------------------------------------------------------
## cols(
##   x = col_double(),
##   y = col_logical()
## )
## Warning: 1000 parsing failures.
##  row col           expected     actual                        file
## 1001   y 1/0/T/F/TRUE/FALSE 2015-01-16 'data1/out/challenge-2.csv'
## 1002   y 1/0/T/F/TRUE/FALSE 2018-05-18 'data1/out/challenge-2.csv'
## 1003   y 1/0/T/F/TRUE/FALSE 2015-09-05 'data1/out/challenge-2.csv'
## 1004   y 1/0/T/F/TRUE/FALSE 2012-11-28 'data1/out/challenge-2.csv'
## 1005   y 1/0/T/F/TRUE/FALSE 2020-01-13 'data1/out/challenge-2.csv'
## .... ... .................. .......... ...........................
## See problems(...) for more details.
str(challenge1)
## tibble [2,000 x 2] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
##  $ x: num [1:2000] 404 4172 3004 787 37 ...
##  $ y: logi [1:2000] NA NA NA NA NA NA ...
##  - attr(*, "problems")= tibble [1,000 x 5] (S3: tbl_df/tbl/data.frame)
##   ..$ row     : int [1:1000] 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 ...
##   ..$ col     : chr [1:1000] "y" "y" "y" "y" ...
##   ..$ expected: chr [1:1000] "1/0/T/F/TRUE/FALSE" "1/0/T/F/TRUE/FALSE" "1/0/T/F/TRUE/FALSE" "1/0/T/F/TRUE/FALSE" ...
##   ..$ actual  : chr [1:1000] "2015-01-16" "2018-05-18" "2015-09-05" "2012-11-28" ...
##   ..$ file    : chr [1:1000] "'data1/out/challenge-2.csv'" "'data1/out/challenge-2.csv'" "'data1/out/challenge-2.csv'" "'data1/out/challenge-2.csv'" ...
##  - attr(*, "spec")=
##   .. cols(
##   ..   x = col_double(),
##   ..   y = col_logical()
##   .. )
  • x 컬럼은 실수형으로 불러왔으나, y 컬럼은 logical 형으로 불러왔다.

이런 이유로 중간 결과를 캐싱하기에 CSV 파일을 신뢰할 수 없다.

불러올 때마다 열 사양을 다시 만들어야 한다. 즉, parsing을 해 주어야 한다.

여기에는 두 가지 대안이 있다.

  1. write_rds()read_rds() 는 베이스 함수인 readRDS()saveRDS() 의 래퍼 함수들이다. 이들은 RDS 라는 R 의 커스텀 바이너리 형식으로 데이터를 저장한다.
   write_rds(challenge, "data1/out/challenge.rds")
   challenge2 <- read_rds("data1/out/challenge.rds")
  • write_rds() 함수로 challenge 데이터 세트를 data 폴더에 challenge.rds에 export 했다.
  • read_rds() 함수를 이용하여 data1/out/challenge.rds 파일을 challenge2 변수로 import 했다.

challenge2의 데이터 구조를 확인해 보자.

   str(challenge2)
## tibble [2,000 x 2] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
##  $ x: num [1:2000] 404 4172 3004 787 37 ...
##  $ y: Date[1:2000], format: NA NA ...
##  - attr(*, "spec")=
##   .. cols(
##   ..   x = col_double(),
##   ..   y = col_date(format = "")
##   .. )
  • str(challenge2)으로 challenge2의 데이터 구조를 확인해 보면, 컬럼들의 데이터 타입이 실수형과 날짜형으로 되어 있음을 알 수 있다. 즉, parsing이 필요없다.
  1. feather 패키지는 다른 프로그래밍 언어와 공유할 수 있는 빠른 바이너리 파일 형식을 구현한다.
   library(feather)
   write_feather(challenge, "data1/out/challenge.feather")
   challenge3 <- read_feather("data1/out/challenge.feather")
   str(challenge3)
## tibble [2,000 x 2] (S3: tbl_df/tbl/data.frame)
##  $ x: num [1:2000] 404 4172 3004 787 37 ...
##  $ y: Date[1:2000], format: NA NA ...
  • x 컬럼과 y 컬럼의 데이터 타입을 잘 읽어 들였다.

featherRDS 보다 대체적으로 빠르며 R 외부에서도 사용할 수 있다.

RDS 는 리스트-열을 지원하지만 feather 는 현재 지원하지 않는다.

따라서, R에서 처리한 데이터 세트는 단순히 csv 파일이 아닌 RDS 파일로 저장하는 것이 좋다. {-}