問題の概要
Pythonをデータ分析に使用している場合、pd.read_csv()は最も頻繁に使用する関数でしょう。しかし、昨日まで動いていたスクリプトが突然、不可解なParserErrorでクラッシュすることがあります。通常、以下のようなエラーが表示されます。描述:
pandas.errors.ParserError: Error tokenizing data. C error: Expected 3 fields in line 12, saw 5
Pandasは構造に対して厳格です。通常、ヘッダーまたは最初の数行から列数を決定します。もし12行目に5つの列があり、ヘッダーで3列しか定義されていない場合、C言語で最適化されたパーサーは処理を停止します。単純に、余分な2つのデータをどこに配置すればよいか判断できないためです。
主な原因
- 紛れ込んだカンマ: 住所("123 Main St, Apt 4")のように、テキストフィールド内にカンマが含まれている場合、テキストが引用符で囲まれていないと、Pandasはそれを新しい列として誤認してしまいます。
- 破損した行: 自動エクスポートの不具合により、10万行のファイルの途中のランダムな行に、不要なタブや区切り文字が挿入されることがあります。
- セパレーターの不一致: Pandasが標準のカンマ(
,)を探しているのに対し、セミコロン区切り(;)のファイルを読み込もうとしている可能性があります。 - メタデータの混入: 一部のCSVには、ファイルの先頭や末尾にテーブル構造に従わない数行の説明テキストが含まれていることがあります。
解決方法
1. 問題のある行をスキップする(Pandas 1.3.0以降)
膨大なデータセットから数行失われても分析に支障がない場合は、Pandasに「不正な」行を無視するように指示します。最新バージョンのPandasでは、on_bad_linesパラメータを使用します。
import pandas as pd
# 壊れた行をスキップし、見つかった行ごとに警告を表示します
try:
df = pd.read_csv('data.csv', on_bad_lines='warn')
except Exception as e:
print(f"ファイルを読み込めませんでした: {e}")
# または、コンソールを汚さないように通知なしでスキップします:
# df = pd.read_csv('data.csv', on_bad_lines='skip')
2. 古いバージョンのPandasでの解決策
1.3.0より古いPandasを使用しているレガシーシステムの場合は、パラメータ名が少し異なります。代わりにerror_bad_linesとwarn_bad_linesを使用する必要があります。
import pandas as pd
# error_bad_linesをFalseに設定するとクラッシュを防げます
df = pd.read_csv('data.csv', error_bad_lines=False, warn_bad_lines=True)
3. 区切り文字を明示的に定義する
Pandasがセパレーターを誤認することでエラーが発生することがよくあります。ファイルがタブやセミコロンを使用している場合は、それを明示的に指定してください。また、sep=Noneを使用してPandasに推測させることもできます。
# カンマ、タブ、セミコロンのどれかをエンジンに自動検出させます
df = pd.read_csv('data.csv', sep=None, engine='python')
# または、パイプ記号区切りのファイルだとわかっている場合は手動で指定します
# df = pd.read_csv('data.csv', sep='|')
4. Pythonエンジンに切り替える
Pandasは非常に高速であるため、デフォルトでCエンジンを使用します。しかし、Pythonエンジンの方が柔軟性が高く、複雑なフォーマットをより適切に処理できます。数百万行のファイルでは大幅に遅くなりますが、パースの問題を解決できる可能性があります。
df = pd.read_csv('data.csv', engine='python', on_bad_lines='skip')
5. 手動での確認
エラーメッセージに「Expected 3 fields in line 12, saw 5」と表示されている場合、それは場所を示しています。VS CodeやNotepad++などのテキストエディタでファイルを開き、12行目に移動してください。おそらく以下のような状態になっています。
# 問題点: 住所内の余分なカンマにより4番目の列が作成されている
ID,Name,Address
1,John Doe,123 Main St, New York
# 修正方法: フィールドをダブルクォーテーションで囲む
1,John Doe,"123 Main St, New York"
データの検証
修正を適用した後、すべてが完璧だと決めつけないでください。以下の3つのコマンドを使用して、インポートしたデータの整合性を確認しましょう。
- 行数の確認:
df.shapeを実行します。1,000行を想定していたのに800行しかない場合、200行がスキップされたことがわかります。 - 欠損値の検索:
df.isnull().sum()を実行します。行のずれは、多くの場合、連鎖的なNaN(欠損値)を引き起こします。 - スポットチェック:
df.sample(10)を使用してランダムな行を確認し、データが実際に列ヘッダーと一致しているか確認します。
今後の予防策
今後この問題を避けるために、パイプラインを制御できるのであれば、生のCSVから脱却することを検討してください。ParquetやFeather形式を使用すると、データ型と構造が完全に保存されるため、紛れ込んだカンマを心配する必要がなくなります。どうしてもCSVを使用する必要がある場合は、エクスポートスクリプトですべての文字列フィールドに適切なクォーティング(引用符囲み)が適用されていることを確認してください。

