1.4 파일 파싱하기
이제까지 개별 벡터를 파싱하는 방법을 배웠으므로, 처음으로 돌아가서 readr을 이용하여 파일을 파싱하는 방법을 알아볼 차례이다. 이 절에서는 다음의 두 방법을 배운다.
readr이 각 열의 유형을 자동으로 추측하는 방법.- 기본 사양을 재정의하는 방법.
1.4.1 전략
readr은 휴리스틱 방법을 사용하여 각 열의 유형을 파악한다. 첫 번째 1000행을 읽고 (적절히 보수적인) 휴리스틱 방법을 사용하여 각 열의 유형을 찾는다.
guess_parser()(readr의 추정을 반환)와 parse_guess() (앞의 추정을 사용하여 열을 파싱)를 사용하여 문자형 벡터에 이 과정을 재현해볼 수 있다.
guess_parser("2010-10-01")## [1] "date"
guess_parser("15:01")## [1] "time"
guess_parser(c("TRUE", "FALSE"))## [1] "logical"
guess_parser(c("1", "5", "9"))## [1] "double"
guess_parser(c("12,352,561"))## [1] "number"
str(parse_guess("2010-10-10"))## Date[1:1], format: "2010-10-10"
사용해야 할 parse_*()의 *을 결정하기가 곤란한 경우 guess_parse() 함수를 활용하여 확인할 수 있다.
이 휴리스틱 방법은 다음 유형들을 각각 시도하여 일치하는 항목을 찾으면 멈춘다.
- 논리형: “
F,” “T,” “FALSE,” “TRUE”만 포함. - 정수형: 수치형 문자(와
-)만 포함. - 더블형: (
4.5e-5와 같은 숫자를 포함하는) 유효한 더블형만 포함. - 수치형: 내부에 그룹화 마크가 있는 유효한 더블형을 포함.
- 타임형:
time_format의 기본형식과 일치. - 데이트형:
date_format의 기본형식과 일치. - 데이트-시간형: ISO8601 날짜.
이러한 규칙 중 어느 것도 적용되지 않으면 해당 열은 문자열 벡터로 그대로 남는다.
1.4.2 문제점
큰 파일의 경우 다음과 같은 이유로 이러한 기본값이 항상 잘 작동하지는 않을 수 있다.
- 처음 1,000 행이 특수한 경우이어서
readr이 충분히 일반적이지 않은 유형으로 추측할 수 있다. 예를 들어 첫 번째 1,000개의 행에 정수만 있는 더블형 열이 있을 수 있다. - 열에 결측값이 많이 있을 수 있다. 첫 번째 1,000 개의 행에
NA만 있는 경우readr이 문자형 벡터로 추측했지만, 여러분은 좀 더 구체적으로 파싱하고 싶을 수 있다.
readr에는 이러한 두 가지 문제를 모두 보여주는 까다로운 CSV 예제 파일로 challenge.csv 파일이 포함되어 있다. 이 파일을 읽어 들이기 위해 readr_example() 함수를 이용하여 파일의 경로를 확인할 수 있다.
readr_example("challenge.csv")## [1] "C:/Program Files/R/R-4.0.3/library/readr/extdata/challenge.csv"
challenge.csv파일이 저장된 경로와 파일이름이 표시된다.- 참고로 패키지에 포함된 파일의 경로를 찾기 위해서는
readr_example()을 사용한다.
이를 read_csv()의 인수로 데이터 파일을 불러와서 challenge 변수에 대입하는 스크립트는 다음과 같다.
challenge <- read_csv(readr_example("challenge.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 'C:/Program Files/R/R-4.0.3/library/readr/extdata/challenge.csv'
## 1002 y 1/0/T/F/TRUE/FALSE 2018-05-18 'C:/Program Files/R/R-4.0.3/library/readr/extdata/challenge.csv'
## 1003 y 1/0/T/F/TRUE/FALSE 2015-09-05 'C:/Program Files/R/R-4.0.3/library/readr/extdata/challenge.csv'
## 1004 y 1/0/T/F/TRUE/FALSE 2012-11-28 'C:/Program Files/R/R-4.0.3/library/readr/extdata/challenge.csv'
## 1005 y 1/0/T/F/TRUE/FALSE 2020-01-13 'C:/Program Files/R/R-4.0.3/library/readr/extdata/challenge.csv'
## .... ... .................. .......... ................................................................
## See problems(...) for more details.
problems(challenge)## # A tibble: 1,000 x 5
## row col expected actual file
## <int> <chr> <chr> <chr> <chr>
## 1 1001 y 1/0/T/F/TRUE/F~ 2015-01~ 'C:/Program Files/R/R-4.0.3/library/rea~
## 2 1002 y 1/0/T/F/TRUE/F~ 2018-05~ 'C:/Program Files/R/R-4.0.3/library/rea~
## 3 1003 y 1/0/T/F/TRUE/F~ 2015-09~ 'C:/Program Files/R/R-4.0.3/library/rea~
## 4 1004 y 1/0/T/F/TRUE/F~ 2012-11~ 'C:/Program Files/R/R-4.0.3/library/rea~
## 5 1005 y 1/0/T/F/TRUE/F~ 2020-01~ 'C:/Program Files/R/R-4.0.3/library/rea~
## 6 1006 y 1/0/T/F/TRUE/F~ 2016-04~ 'C:/Program Files/R/R-4.0.3/library/rea~
## 7 1007 y 1/0/T/F/TRUE/F~ 2011-05~ 'C:/Program Files/R/R-4.0.3/library/rea~
## 8 1008 y 1/0/T/F/TRUE/F~ 2020-07~ 'C:/Program Files/R/R-4.0.3/library/rea~
## 9 1009 y 1/0/T/F/TRUE/F~ 2011-04~ 'C:/Program Files/R/R-4.0.3/library/rea~
## 10 1010 y 1/0/T/F/TRUE/F~ 2010-05~ 'C:/Program Files/R/R-4.0.3/library/rea~
## # ... with 990 more rows
challenge.csv파일은readr패키지가 제공하는 예제 데이터 세트임.read_csv()에서 열에 대한 데이터 타입을 지정하지 않아y컬럼의 값이 모두NA로 인식함.
두 가지가 출력되었다.
- 첫 번째 1,000 개의 행을 보고 생성된 열 상세 내용과 첫 다섯 개의 파싱 오류가 그것이다.
- 발생한 문제들을 ‘
problems()’ 로 명시적으로 추출하여 더 깊이 탐색하는 것은 좋은 방법이다.
1.4.3 문제 해결 전략
문제가 남아있지 않을 때까지 열 단위로 작업하는 것은 좋은 전략이다.
x 열에 파싱 문제가 많다는 것을 알 수 있다. 정수값 다음에 따라오는 문자가 있었던 것이다. 이는 더블형 파서를 사용해야 함을 암시한다.
이 호출을 수정하기 위해 먼저 열 사양을 복사하여 원래 호출에 붙여 넣어보라.
1.4.3.1 x 컬럼 타입을 정수로, 그리고 y컬럼을 문자형으로 지정
x는 정수형, y는 문자형으로 지정해 보기로 한다.
challenge <- read_csv(
readr_example("challenge.csv"),
col_types = cols(
x = col_integer(),
y = col_character()
)
)## Warning: 1000 parsing failures.
## row col expected actual file
## 1001 x no trailing characters 0.23837975086644292 'C:/Program Files/R/R-4.0.3/library/readr/extdata/challenge.csv'
## 1002 x no trailing characters 0.41167997173033655 'C:/Program Files/R/R-4.0.3/library/readr/extdata/challenge.csv'
## 1003 x no trailing characters 0.7460716762579978 'C:/Program Files/R/R-4.0.3/library/readr/extdata/challenge.csv'
## 1004 x no trailing characters 0.723450553836301 'C:/Program Files/R/R-4.0.3/library/readr/extdata/challenge.csv'
## 1005 x no trailing characters 0.614524137461558 'C:/Program Files/R/R-4.0.3/library/readr/extdata/challenge.csv'
## .... ... ...................... ................... ................................................................
## See problems(...) for more details.
1.4.3.2 x 컬럼 타입을 실수형 그리고 y컬럼을 문자형으로 지정
그런 다음 x 열의 유형을 ‘col_double()’로 조정할 수 있다.
challenge <- read_csv(
readr_example("challenge.csv"),
col_types = cols(
x = col_double(),
y = col_character()
)
)
tail(challenge)## # A tibble: 6 x 2
## x y
## <dbl> <chr>
## 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
첫 번째 문제는 해결되었지만, 마지막 몇 행을 보면 날짜가 문자형 벡터(<chr>)로 저장되었다.
1.4.3.3 x 컬럼 타입을 실수형 그리고 y컬럼을 날짜형으로 지정
y 열을 데이트형(col_date())으로 설정하여 이를 수정할 수 있다.
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
모든 parse_xyz() 함수는 해당하는 col_xyz() 함수를 가지고 있다. 데이터가 이미 R 의 문자형 벡터인 경우에는 parse_xyz() 를 사용하면 되고, readr 이 데이터를 불러오는 방법을 설정할 경우에는 col_xyz() 를 사용하면 된다.
col_types 를 항상 설정하여 readr 이 생성하는 출력물로부터 만들어 나가는 것을 강력히 추천한다. 이렇게 하면 일관되고 재현할 수 있는 데이터 불러오기 스크립트를 갖게 된다. 기본값으로 추측하여 데이터를 읽는다면 데이터 변경 시 readr 은 과거 설정으로 읽게 될 것이다. 정말로 엄격하게 하고 싶다면 stop_for_problems() 를 사용하라. 파싱 문제가 생기면 오류를 내며 스크립트를 중단할 것이다.
1.4.4 기타 전략
파일을 파싱하는 데 도움이 되는 몇 가지 다른 일반적인 전략이 있다.
- 앞의 예제에서 우리는 단지 운이 없었다. 즉, 기본값보다 한 행만 더 살펴보면 한 번에 정확하게 파싱할 수 있다.
challenge2 <- read_csv(readr_example("challenge.csv"), guess_max = 1001)##
## -- Column specification --------------------------------------------------------
## cols(
## x = col_double(),
## y = col_date(format = "")
## )
- 모든 열을 문자형 벡터로 읽으면 문제를 쉽게 진단할 수 있는 경우가 많다.
challenge2 <- read_csv(readr_example("challenge.csv"),
col_types = cols(.default = col_character())
)이 방법은 type_convert() 와 함께 사용하면 특히 유용한데, 이 함수는 휴리스틱한 파싱 방법을 데이터프레임의 문자형 열에 적용한다.
tribble() 함수의 활용
df <- tribble(
~x, ~y,
"1", "1.21",
"2", "2.32",
"3", "4.56"
)
df## # A tibble: 3 x 2
## x y
## <chr> <chr>
## 1 1 1.21
## 2 2 2.32
## 3 3 4.56
#####열 유형을 주의
type_convert(df)##
## -- Column specification --------------------------------------------------------
## cols(
## x = col_double(),
## y = col_double()
## )
## # A tibble: 3 x 2
## x y
## <dbl> <dbl>
## 1 1 1.21
## 2 2 2.32
## 3 3 4.56
- 매우 큰 파일을 읽는 경우,
n_max를 10,000 또는 100,000 과 같이 작은 숫자로 설정할 수 있다. 이렇게 하면 일반적인 문제를 해결하는 동시에 반복작업을 가속화할 수 있다. - 파싱에 중대한 문제가 있는 경우에는
read_lines()을 이용하여 라인으로 이루어진 문자형 벡터로 읽거나read_file()을 이용하여 길이가 1인 문자형 벡터로 읽는 것이 더 쉬울 수 있다. 그런 다음 나중에 배울 문자열 파싱 방법을 사용하여 좀 더 특이한 포맷을 파싱하면 된다.