CSVは見た目ほど単純ではない(意外とクセがある)
CSVファイルは要するにただのテキストです。行は改行で区切られ、各フィールドはカンマで区切られる。基本はそれだけ。ところが現実には、カンマを含むクォート付きの文字列、改行を含むフィールド、地域ごとの慣習の違い(カンマ区切り vs セミコロン区切り)、Excelで保存したときに先頭に付くBOMなど、細かい罠がたくさんあります。自前で line.split(",") を書くと、まずハマります。
そんなときに活躍するのが、Python標準の csv モジュールです。小〜中規模のファイルなら、これひとつでほぼ事足ります。
csv.reader でCSVを読み込む方法
csv.reader は、各行を文字列のリストとして返してくれます。
import csv
with open("people.csv", newline="") as f:
reader = csv.reader(f)
for row in reader:
print(row)
ハマりやすい細かいポイントをいくつか挙げておきます。
openには必ずnewline=""を渡す。改行コードの処理は csv モジュール側でやってくれます。これを指定しないと、Windows で空行が混ざることがあります。- 値はすべて文字列として読み込まれる。
"42"はint(...)で変換するまで文字列のままです。CSV に型の概念はありません。 - ヘッダー行も単なる1行のデータ。ヘッダー付きのファイルを扱うときは、最初の行を手動でスキップするか、
DictReaderに切り替えましょう。
ヘッダー行をスキップする
import csv
with open("people.csv", newline="") as f:
reader = csv.reader(f)
headers = next(reader) # pulls the first row out
for row in reader:
print(row)
next(reader) はイテレータを1つ進めて、その行を返します。
DictReader で辞書として読み込む
csv.DictReader は1行目をヘッダーとして扱い、それ以降の各行を辞書形式で返してくれます。
import csv
with open("people.csv", newline="") as f:
reader = csv.DictReader(f)
for row in reader:
print(row["name"], row["email"])
実務ではほぼ間違いなくこちらを選ぶことになります。カラム名がそのままドキュメント代わりになりますし、元ファイルの列順が入れ替わってもコードが壊れません。
ヘッダー行がないファイルを扱う場合は、fieldnames=["name", "email", ...] のように明示的に渡してください。
csv.writer でCSVに書き込む
csv.writer は、リスト形式の行データをCSVの1行に変換して書き出してくれます。
import csv
rows = [
["name", "age", "city"],
["Rosa", 30, "Lisbon"],
["Ada", 36, "London"],
]
with open("out.csv", "w", newline="") as f:
writer = csv.writer(f)
writer.writerows(rows)
writerow(row) は1行だけ書き込み、writerows(rows) はイテラブルをまとめて一気に書き込みます。どちらもフィールドにカンマ・クォート・改行が含まれていれば自動でクォートしてくれるので、こちらで気を使う必要はありません。
DictWriter で辞書をCSVに書き込む
データがすでに辞書形式になっているなら、DictWriter を使えば「リストに変換する」手間を省けます。
import csv
people = [
{"name": "Rosa", "age": 30, "city": "Lisbon"},
{"name": "Ada", "age": 36, "city": "London"},
]
with open("out.csv", "w", newline="") as f:
writer = csv.DictWriter(f, fieldnames=["name", "age", "city"])
writer.writeheader()
writer.writerows(people)
fieldnames 引数は、ヘッダーとカラムの順序の両方を決める役割を持ちます。辞書の中に fieldnames に含まれないキーがあった場合、黙って捨てられます(extrasaction="raise" を指定すれば例外を投げさせることも可能)。
区切り文字とクォート処理を変える
「CSV」と言ってもカンマ区切りだけとは限りません。ヨーロッパ圏のロケールでは ; がよく使われますし、タブ区切り(\t)のファイルや、| を使うシステムもあります。こういう場合は delimiter= 引数を渡してあげればOKです。
import csv
with open("data.tsv", newline="") as f:
reader = csv.reader(f, delimiter="\t")
for row in reader:
print(row)
変わったクォートルールを持つファイルを扱うときは、csv.register_dialect(...) で一度設定しておけば何度でも使い回せます。ただ、たいていのファイルならデフォルト設定に delimiter= を足すくらいで十分です。
文字コード(エンコーディング)
CSVファイルはあくまでテキストなので、必ずエンコーディングが絡んできます。最近の標準はUTF-8ですが、WindowsのExcelから出力されたファイルは cp1252 だったり、UTF-8のBOM付きだったりすることがあります。Pythonでcsvを読み込むときに文字化けを避けるには、エンコーディングを明示的に指定するのが鉄則です。
with open("data.csv", newline="", encoding="utf-8") as f:
reader = csv.DictReader(f)
...
UnicodeDecodeError が出たときは、指定したエンコーディングがファイルの実際のものと合っていません。よくある候補としては utf-8-sig(ExcelのBOM付きに対応)、cp1252、latin-1 あたりを順番に試してみましょう。
CSVの各行を使える型に変換する
CSVから読み込んだ値はすべて文字列になるので、型変換は自分でやる必要があります。
(StringIOを使えば実ファイルなしでサンプルを動かせます。実際のコードではopen(path)を使ってください)
日付、null許容の数値、10通りのスペルで書かれたtrue/falseなど、型まわりがややこしいCSVを扱うなら、pandasを検討しましょう。こうしたケースの大半に対応する作法があらかじめ組み込まれています。
pandasを使うべきタイミング
pandas.read_csv(path)はDataFrameを返します。次のような操作をしたくなった瞬間から、DataFrameこそが最適な構造になります。
- 行のフィルタ:
df[df["active"] == True] - 集計:
df.groupby("city")["age"].mean() - 別のテーブルとの結合
- ちょっとした整形をかけて書き出し
import pandas as pd
df = pd.read_csv("people.csv")
adults = df[df["age"] >= 18]
adults.to_csv("adults.csv", index=False)
小さくて単純な読み込みなら Pandas はオーバースペックですし、依存関係としてもかなり重めです(仮想環境に pip install する必要があります)。とはいえ、データらしいデータを扱う場面では、Python でデータ分析をする人がまず手に取るのが Pandas なのも事実です。
巨大な CSV ファイルをストリーム処理する
csv.reader はもともと遅延評価で、1 行ずつ読み込む作りになっています。最初に list(reader) でまとめて展開したりせず、そのままイテレートしていけば、ファイルサイズに関係なくメモリ使用量はほぼ一定に保てます。
import csv
with open("huge.csv", newline="") as f:
reader = csv.DictReader(f)
error_count = 0
for row in reader:
if row["status"] == "error":
error_count += 1
print(f"Found {error_count} errors.")
この書き方なら、行をリストに溜め込まない限り、10GBのファイルでも10KBのファイルと同じようにサクサク処理できます。
身につけておきたい習慣
- CSVを読み書きするときは、
openに必ずnewline=""を渡しましょう。 - ヘッダー付きのファイルなら
DictReader/DictWriterを使うのがおすすめ。数値インデックスより圧倒的に読みやすくなります。 - エンコーディングは明示的に指定を。特にExcelが吐いたファイルや日本語以外のソースを扱うときは要注意です(CSV 文字化け対策の基本)。
- 型変換は読み込みの段階で済ませておくと、後続のコードがラクになります。
- 単にデータを移すだけでなく「分析」したくなったら、そこで初めて pandas の出番です。
次のステップ
これでJSONもCSVも扱えるようになりました。実務で使うスキルとして最後に押さえておきたいのが、ネットワーク越しのデータ取得です。次のドキュメントでは requests ライブラリを使ったHTTPリクエストを見ていきます。
よくある質問
PythonでCSVファイルを読み込むには?
標準ライブラリの csv モジュールを使います。csv.reader は各行を文字列のリストとして返し、csv.DictReader は1行目をヘッダーとして扱い、各行を辞書として返してくれます。ファイルを開くときは改行コードが崩れないように newline='' を必ず指定してください: with open('data.csv', newline='') as f:。
PythonでCSVファイルを書き出すには?
書き込みモードで newline='' を付けて開いたファイルに csv.writer を組み合わせます。1行ずつなら writer.writerow([...])、まとめて書くなら writer.writerows([[...], [...]]) を呼び出します。辞書データを扱う場合は csv.DictWriter が便利で、ヘッダー出力も任せられます。
csvモジュールとpandas、どちらを使うべき?
ちょっと読み書きしたいだけ、1行ずつ処理したい、余計な依存を増やしたくない——そんな場面では csv で十分です。一方、フィルタリングやグループ集計、結合などの加工をしたい場合や、ベクトル演算の速さが効いてくる大きなファイルを扱うならpandasの出番です。ファイル自体はどちらでも扱えるので、「読み込んだ後に何をするか」で選ぶのがポイントです。
WindowsでCSVに空行が入ってしまうのはなぜ?
newline='' を付けずにファイルを開いているのが原因です。csv モジュールは自分で改行コードを書き出すのですが、この引数がないとWindowsでは余分な改行が追加されてしまいます。読み込み・書き込みどちらでも open(path, newline='') の形で開くようにしましょう。