Python で 神CSV を読み取るときは God Slayer を使おう

Python パッケージ “God Slayer” を紹介します。

神CSV の読み取りを、標準パッケージの csv モジュールで実装すべきでない理由

神CSV の読み取りを普通に実装すると確実にコードは荒れます。
たとえば、次のような CSV を Python で読み込んでみましょう。

出典: 板橋区総務部総務課オープンデータより

CSV の中身は次のようになっています。

CSV の中身 前半
CSV の中身 前半
CSV の中身 後半
CSV の中身 後半

CSV からデータを取り出すときは、ヘッダーやフッターはもちろん、
合計行も不要でしょう。
(プログラムで必要に応じて各行を合計すればよいため)

欲しいデータは次の通りです。

['0', '4062', '2069', '1993']
['1', '4279', '2171', '2108']
['2', '4285', '2152', '2133']
['2', '4285', '2152', '2133']
['3', '4434', '2268', '2166']
['4', '4243', '2207', '2036']
['5', '4369', '2283', '2086']
['6', '4345', '2213', '2132']
['7', '4117', '2163', '1954']
['8', '4155', '2146', '2009']
['9', '4031', '2151', '1880']

--------中略--------

['95', '521', '119', '402']
['96', '375', '59', '316']
['97', '308', '51', '257']
['98', '187', '32', '155']
['99', '131', '23', '108']
['100', '91', '7', '84']
['101', '57', '5', '52']
['102', '43', '7', '36']

※ ここでは “103 歳以上”, “不詳者” の行は要件上不要として話を進めます

一方で、Python で CSV ファイルを読み込むときは、
通常、標準パッケージの csv モジュールを使います。

import csv


with open("/path/to/file.csv") as csvfile:
    spamreader = csv.reader(csvfile)
    for row in spamreader:
        print(row)

最もシンプルに実装すると、上記のようなコードになります。
しかし、このままの処理で 神CSV を読み込むと
不要な行も print() されてしまいます。

['7.住民基本台帳による年齢別男女別人口', '', '', '']
['年齢', '総数(人)', '男(人)', '女(人)']
['総数', '571357', '280363', '290994']
['0~4歳', '21303', '10867', '10436']
['0', '4062', '2069', '1993']
['1', '4279', '2171', '2108']
['2', '4285', '2152', '2133']

--------中略--------

['100~102', '191', '19', '172']
['100', '91', '7', '84']
['101', '57', '5', '52']
['102', '43', '7', '36']
['103歳以上', '53', '4', '49']
['不詳者', '-', '-', '-']
['令和2年1月1日現在', '', '', '']
['(注)法改正に伴い,外国人が含まれた数値となっている。', '', '', '']
['資料:区民文化部戸籍住民課', '', '', '']

実際の処理では print() 1行ではなく
各値の検証やデータベースへの登録、なんらかの変換等の処理を
実装したいでしょう。

しかし、神CSV の場合は
ヘッダーやフッター、中間行の読み飛ばし処理などを更に実装する必要があり、
確実にコードは荒れることになります。

繰り返しブロックの中はできる限りシンプルに保ちたいものです。

God Slayer を使って、必要な行だけの読み出しをシンプルに実装しよう

Python パッケージ “God Slayer” を使うと、次のことが実現できます:

  • ヘッダー行の次まで自動的に読み飛ばします
  • 中間行を自動的に読み飛ばします
  • フッター行の直前で読み取りを停止します

もうヘッダー、フッター、中間行を読み飛ばす処理を
自力で実装する必要はありません。

実装は次のようなかたちになります:

from pathlib import Path
from godslayer.god_slayer_factory import GodSlayerFactory


god_slayer_factory = GodSlayerFactory(
            header=["年齢", "総数(人)", "男(人)", "女(人)"],
            partition=[r"^(総数)|(\d*〜\d*歳?)$", r"^\d*$", r"^\d*$", r"^\d*$"],
            footer=[r"^\d*歳以上$", r"^\d*$", r"^\d*$", r"^\d*$"],
            encoding="shift_jis_2004",
)
god_slayer = god_slayer_factory.create(Path("/path/to/file.csv"))



for row in god_slayer:
    print(row)

先に読み飛ばすヘッダー、フッター、中間行を定義しているので、
繰り返しブロックはシンプルに実装できています。

出力データは次のとおりです:

['0', '4062', '2069', '1993']
['1', '4279', '2171', '2108']
['2', '4285', '2152', '2133']
['2', '4285', '2152', '2133']
['3', '4434', '2268', '2166']
['4', '4243', '2207', '2036']
['5', '4369', '2283', '2086']
['6', '4345', '2213', '2132']
['7', '4117', '2163', '1954']
['8', '4155', '2146', '2009']
['9', '4031', '2151', '1880']

--------中略--------

['95', '521', '119', '402']
['96', '375', '59', '316']
['97', '308', '51', '257']
['98', '187', '32', '155']
['99', '131', '23', '108']
['100', '91', '7', '84']
['101', '57', '5', '52']
['102', '43', '7', '36']

必要な行だけを取り出すことができました。

よくある質問

ファイルを開いたり閉じたりしなくてもいいの?

呼び出し側でファイルを開いたり閉じたりする必要はありません。

God Slayer の内部でファイルを開いていますが、
ファイルのオープンハンドラーは God Slayer のインスタンス変数が
スコープから外れると、自動的に閉じられます。

参考: Python Custom Iterator: Close a file on StopIteration – Stack Overflow

処理速度は速いの?

速度に関しても最大限追求しています。

フッターの認識に関しては、CSV ファイルの読み込みを始める前に
先に CSV ファイルを最終行から操作してフッターを認識するので、
処理中には各行をマッチングしたりはしていません。

中間行だけは、引数で指定すると各行でマッチング処理を行いますが、
中間行のパターンを引数で与えなければ
マッチング処理なしの繰り返し処理を DI します。

メモリをたくさん使いますか?

ジェネレーターを使って実装しているので、
ファイル内のすべての要素を一度に読み込むことはしません。
1 行に大量の要素が含まれていなければ心配ないでしょう。

仕様の説明

GodSlayerFactory の引数

header: Optional[List[str]]

初期値: None
引数を与えると、God Slayer がマッチする行の次の行まで自動的に読み飛ばします。

partition: Optional[List[str]]

初期値: None
引数を与えると、God Slayer がマッチする行を自動的に読み飛ばします。

footer: Optional[List[str]]

初期値: None
引数を与えると、God Slayerがマッチする行の直前で自動的に処理を停止します。

encoding: str

初期値: “utf-8”
CSV ファイルを開くときのエンコーディング形式を指定します。

God Slayer を使ってみよう

God Slayer は PyPI で公開しています。
https://pypi.org/project/godslayer/

pip コマンドでインストールできます:

pip install godslayer
God Slayer バッジ
タイトルとURLをコピーしました