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 변수에 대입하는 스크립트는 다음과 같다.
<- read_csv(readr_example("challenge.csv")) challenge
##
## -- 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
는 문자형으로 지정해 보기로 한다.
<- read_csv(
challenge 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()
’로 조정할 수 있다.
<- read_csv(
challenge 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()
)으로 설정하여 이를 수정할 수 있다.
<- read_csv(
challenge 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 기타 전략
파일을 파싱하는 데 도움이 되는 몇 가지 다른 일반적인 전략이 있다.
- 앞의 예제에서 우리는 단지 운이 없었다. 즉, 기본값보다 한 행만 더 살펴보면 한 번에 정확하게 파싱할 수 있다.
<- read_csv(readr_example("challenge.csv"), guess_max = 1001) challenge2
##
## -- Column specification --------------------------------------------------------
## cols(
## x = col_double(),
## y = col_date(format = "")
## )
- 모든 열을 문자형 벡터로 읽으면 문제를 쉽게 진단할 수 있는 경우가 많다.
<- read_csv(readr_example("challenge.csv"),
challenge2 col_types = cols(.default = col_character())
)
이 방법은 type_convert()
와 함께 사용하면 특히 유용한데, 이 함수는 휴리스틱한 파싱 방법을 데이터프레임의 문자형 열에 적용한다.
tribble()
함수의 활용
<- tribble(
df ~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인 문자형 벡터로 읽는 것이 더 쉬울 수 있다. 그런 다음 나중에 배울 문자열 파싱 방법을 사용하여 좀 더 특이한 포맷을 파싱하면 된다.