nikkie-ftnextの日記

イベントレポートや読書メモを発信

CSVファイルが空行を含んだり、行ごとにフィールドの数が違ったりしても、Pythonのcsvモジュールは、読み込めます!

はじめに

小さな恋のうた♪ ならぬ小さな気づきの記事、nikkieです。

Pythoncsvモジュールに関する小さな気づき(ただしここ最近のエントリをつなぐもの)の共有です。

目次

結論

以下のようなTSVファイルが手元にあるとします。

2005  名詞  数 *   *   *   *   *   B-DAT
年 名詞  接尾  助数詞   *   *   *   年 ネン  ネン  I-DAT
7   名詞  数 *   *   *   *   *   I-DAT
た 助動詞   *   *   *   特殊・タ    基本形   た タ タ O
。 記号  句点  *   *   *   *   。 。 。 O

また  接続詞   *   *   *   *   *   また  マタ  マタ  O
  • 空行を含む
  • フィールドの数がまちまち
    • 1行目は9個
    • 2行目は11個

きれいなTSVではないから1行ずつ処理が必要と思われるかもしれませんが、なんと以下のコードで読み込めます!

動作環境:Python 3.10.9

>>> with open("example.tsv", encoding="utf8", newline="") as f:
...   reader = csv.reader(f, "excel-tab")
...   for row in reader:
...     print(len(row), row)
...
9 ['2005', '名詞', '数', '*', '*', '*', '*', '*', 'B-DAT']
11 ['年', '名詞', '接尾', '助数詞', '*', '*', '*', '年', 'ネン', 'ネン', 'I-DAT']
9 ['7', '名詞', '数', '*', '*', '*', '*', '*', 'I-DAT']
11 ['た', '助動詞', '*', '*', '*', '特殊・タ', '基本形', 'た', 'タ', 'タ', 'O']
11 ['。', '記号', '句点', '*', '*', '*', '*', '。', '。', '。', 'O']
0 []
11 ['また', '接続詞', '*', '*', '*', '*', '*', 'また', 'マタ', 'マタ', 'O']

空行は空のリストなので、if not row:のような形で検出できますね。

解説パート:過去エントリとのつながりを明かすんだよ

例にしたTSVファイルは、最近素振りした固有表現抽出のチュートリアルより。
https://github.com/Hironsan/IOB2Corpus/blob/master/hironsan.txt

csv.reader(f, "excel-tab")の第2引数はdialect引数を指定。
delimiter="\t"と同じです。
以下の記事でソースコードを眺めて得た知見です(「TSVファイル用のDialectの存在を知る」の部分)。

組み込み関数openの呼び出しのnewline=""CSVモジュールのドキュメントでもオススメの指定ですね。

csv.DictReaderソースコードを読んだときに、ヘッダーと1行の長さが合わない場合の処理がありましたが(restkey引数)、こういうケースに対応するためのものだったのか!

裏話:ChatGPTとおしゃべりしていて気付く

クォートで囲まずカンマ区切りの金額を載せてくるというドジっ子アピール1ですが、これ、ヘッダーとそれ以外の行でフィールド数違うけど読めてんじゃん!!

  • ヘッダーは4フィールド
  • ヘッダー以外は5フィールド(金額中のカンマで分割されちゃう)

このときはopennewline引数の挙動を理解したいという裏目的があり、この後は空行を含むCSVの生成もお願いしましたが、空行があっても全然読み込めます。

これが固有表現抽出チュートリアルで扱ったTSVにも適用できることに気づきました!
本エントリ、爆誕す!

固有表現抽出チュートリアル素振りコードのTSV読み込みの書き換えはこちら:
https://github.com/ftnext/ml-playground/commit/fb2055d5c0a16ff03c5742c674c1ef97ebbd5375

終わりに

フィールドの数がまちまちだったり空行を含んだりしていても、Pythoncsvモジュールで読み込めるという気づきをアウトプットしました。
フィールドの数が揃ったきれいめなCSVをふだん扱っている2ので見えていませんでしたが、きれいじゃないCSVも扱えるようにcsvモジュールはできていたんですね。

以前取り上げたこちらのツイートに、今の私は別の見方ができそうです。

csv.readerのドキュメントには

csv ファイルから読み込まれた各行は、文字列のリストとして返されます。

とあります。
また、デザインと歴史 FAQにリストは、

全て同じ型の可変数のオブジェクトを持ち

とあります。
CSVファイルの1行を表すのはいくつか(可変数個)の文字列なので、文字列のリストになっているのかもと思いました。
さらに詳しくはPEPや開発者メーリスを掘り下げることになりそうです。


  1. 氏の名誉のために言っておくと、そのあとはしれっと金額をダブルクォートで囲んできました。気付きがあったのかな
  2. フィールドの数が揃っている場合はこんな知見があります。