"Life is too short, You need python"
Pandas에서 read_csv 수행 시 pandas.parser.CParserError: Error tokenizing data. C error: Expected ...... 과 같은 오류 메세지를 간혹 볼 수 있습니다. 쉼표로 분리될 때 각 행별로 분리되는 갯수가 달라서 발생하는 오류로 해결할 수 있는 방법에 대해 알아보겠습니다.
에러( Error ) 소개
ParserError:
파일 내용을 구문 분석하는 동안 발생한 오류로 인해 발생하는 예외입니다. 이것은 read_csv 또는 read_html 과 같은 함수 가 파일의 내용을 구문 분석할 때 발생하는 오류에 대해 발생하는 일반 오류입니다.
Error tokenizing data:
csv file이 쉼표로 분리될 때 각 행(라인)별로 분리되는 갯수가 달라서(더 많아서) 발생하는 오류입니다.
에러 메세지는 아래와 같이 나오는데 해석하자면
pandas.errors.ParserError: Error tokenizing data. C error: Expected 9 fields in line 4, saw 10
기대 단어는 9개인데 4번째 라인에서 10개의 단어가 보인다는 의미입니다. 이는 Pandas가 csv 파일을 읽어 데이터프레임을 만들 때 구분자(' ' - 공백, ',' - 쉼표 등...)로 분리하게 되는데 columns을 구성하는 단어의 갯수(빈 문자열 포함)가 달라서 발생하는 문제 입니다.
오류 상세
import pandas as pd
file_path = 'd:/pylife/pandas/parser_error_example.csv'
df = pd.read_csv(file_path)
print(df)
정상적인 csv 파일의 출력은 아래와 같습니다.
No. Name Age English Math Science Sum Avg. Grade
0 1 Asher 16 97 69 18 184 61 B
1 2 Bruno 17 70 69 53 192 64 B
2 3 Kade 18 60 97 93 250 83 A
3 4 Hendrix 14 76 46 11 133 44 C
4 5 Julian 15 19 43 30 92 30 D+
No.3 Sum과 Avg.사이 구분자(',')가 누락되어 결측치가 발생했습니다.
단어의 수가 부족할 경우 아래와 같이 결측치 표기가 되며 별도의 에러 메세지 출력은 되지 않습니다.
No. Name Age English Math Science Sum Avg. Grade
0 1 Asher 16 97 69 18 184 61 B
1 2 Bruno 17 70 69 53 192 64 B
2 3 Kade 18 60 97 93 250 83 A NaN
3 4 Hendrix 14 76 46 11 133 44 C
4 5 Julian 15 19 43 30 92 30 D+
반대로 아래와 같이 'Good'이라는 단어가 추가되어 다른 행들과 다르게 단어 수가 늘어나면 에러메세지를 출력합니다.
pandas.errors.ParserError: Error tokenizing data. C error: Expected 9 fields in line 4, saw 10
9개 단어를 기대하고 있었는데 4번째 줄에 10 단어라 파싱 오류를 발생시킨 것 입니다.
Solution 1 : error_bad_line option
이를 해결하기 위한 첫번째 방법으로
error_bad_line = False로 하여 오류가 발생한 줄을 제외하고 처리하도록 합니다.
df = pd.read_csv(file_path, error_bad_lines = False)
(출력 결과)
b'Skipping line 4: expected 9 fields, saw 10\n'
No. Name Age English Math Science Sum Avg. Grade
0 1 Asher 16 97 69 18 184 61 B
1 2 Bruno 17 70 69 53 192 64 B
2 4 Hendrix 14 76 46 11 133 44 C
3 5 Julian 15 19 43 30 92 30 D+
warn_bad_lines = False로 하면 skipping line에 대한 정보도 출력하지 않습니다.
df = pd.read_csv(file_path, error_bad_lines = False, warn_bad_lines = False)
(출력 결과)
No. Name Age English Math Science Sum Avg. Grade
0 1 Asher 16 97 69 18 184 61 B
1 2 Bruno 17 70 69 53 192 64 B
2 4 Hendrix 14 76 46 11 133 44 C
3 5 Julian 15 19 43 30 92 30 D+
하지만 여전히 FutureWarning은 발생합니다.
(FutureWarning: The warn_bad_lines argument has been deprecated and will be removed in a future version. Use on_bad_lines in the future.)
위에서 보셨듯 구분자가 많은 경우는 parameter를 통해 skip하거나 인지가 가능합니다. 반면 구분자가 더 적은 경우는 뒷 부분 column 값이 NaN 처리 되므로 이 부분 주의하셔야 합니다.
* 이런 경우를 대비해서 csv의 가장 마지막 컬럼에 임의의 값 "Z"을 추가해서 끝이 정상인지를 확인하고 마지막 검증 컬럼 값이 "Z"인 경우만 사용하는 등의 방법으로 필터링하여 사용
Solution 2 : for / append( csv → list → dataframe )
CSV 모듈을 사용하여 데이터를 pandas df로 다시 re-route하는 방법이 있습니다. 즉, csv 파일 안에 있는 각 셀 데이터들을 리스트로 받아와 데이터프레임에 다시 넣는 방식으로 깔끔하게 해결할 수 있습니다.
조금 더 근본적인 해결 방식으로 보입니다.
import csv
import pandas as pd
file_path = 'd:/pylife/pandas/parser_error_example.csv'
f = open(file_path,'rt')
reader = csv.reader(f)
#once contents are available, I then put them in a list
csv_list = []
for l in reader:
csv_list.append(l)
f.close()
#now pandas has no problem getting into a df
df = pd.DataFrame(csv_list)
print(df)
(출력 결과)
0 1 2 3 4 5 6 7 8 9
0 No. Name Age English Math Science Sum Avg. Grade None
1 1 Asher 16 97 69 18 184 61 B None
2 2 Bruno 17 70 69 53 192 64 B None
3 3 Kade 18 60 97 93 250 83 A Good
4 4 Hendrix 14 76 46 11 133 44 C None
5 5 Julian 15 19 43 30 92 30 D+ None
새로운 column이 생겼습니다.
마무리
ParserError: Error tokenizing data 오류의 해결 방안에 대해 알아보았습니다.