【Python】CSVでヘッダーをスキップして読み込むコード例

PythonパイソンCSVシーエスブイ ファイルを読み込むときに、不要なヘッダーをスキップするコードれいです。

標準の csv モジュールで、効率的にスキップする方法です。

ネクスト関数 next(csv.reader(f)) で読み捨ててから、for ぶんにかけます。

これは、ヘッダーのスキップを済ませてから、読み込みの for 文にかけるアプローチになります。

for 文の中で、毎回スキップ判定をする必要がなかったので、効率的でした。

そのコード例と実行結果です。

pandasパンダス ライブラリの pandas.read_csv() で CSV を読み込む場合は、スキップのための引数ひきすうが使えました。
pandas マニュアル
pandas.read_csv(skiprows=None, skipfooter=0, skip_blank_lines=True)
スポンサーリンク

コード例

CSV のヘッダーをスキップして読み込むコード例です。

Python の細かい説明は、記事の後半に書きました。

"""CSV の『不要なヘッダー行』をスキップして読み込む Python コード例"""
import pathlib
import csv

def main():
    """メイン関数"""

    # (1/5) CSV ファイルのファイルパスを指定します。
    data_csv = pathlib.Path(r'F:\apps\datas\EdinetcodeDlInfo.csv')

    # (2/5) 読み込み結果を入れるリストを作成します。
    datas = []

    # (3/5) CSV を開きます。
    with data_csv.open('r', encoding='cp932', newline='') as f:

        # (4/5) 不要なヘッダー行を『スキップ』します。
        # next() 関数に csv.reader() を渡して、読み捨てます。
        h = next(csv.reader(f))

        # (5/5) CSV の続きを読み込みます。
        # next(csv.reader()) を使ったことで、f の内部で位置が進んでいます。
        # なので、そのまま for 文にかけると、スキップした続きから
        # 読み込むことができます。
        for c in csv.reader(f):
            # リストに追加
            datas.append(c)

    # 結果の一部を表示します。
    print(f'スキップした行:\n{h}\n')

    print('リストの最初の行、2行目、3行目、最後の行 (先頭3列分):')
    print(datas[0][0:3])
    print(datas[1][0:3])
    print(datas[2][0:3])
    print('...')
    print(datas[-1][0:3])
    return

if __name__ == '__main__':
    main()

CSV ファイルの内容

CSV ファイルは、金融庁きんゆうちょうが公開しているEDINETエディネットコードリストのCSVを使いました。

取得方法(ダウンロード方法)は、以下の記事に書きました。

上場企業じょうじょうきぎょうリストを取得【EDINETコード・証券コード】

それでそのコードリストですが、以下のような CSV でした。

『ダウンロード実行日, ~』の1行目は要らないので、スキップしたい。

CSVファイル: EdinetcodeDlInfo.csv
1行目: ダウンロード実行日,2019年10月27日現在,件数,9736件 ← スキップしたい
2行目: EDINETコード,提出者種別,上場区分,連結の有無,資本金, ...
3行目: "E00004","内国法人・組合","上場","有","1491"," 5月31日", ...
4行目: "E00006","内国法人・組合","上場","有","13500"," 5月31日", ...
... 
9738行目: "E35300","内国法人・組合(有価証券報告書等の提出義務者以外)", ...

実行結果

実行結果です。

スキップした行:
['ダウンロード実行日', '2019年10月27日現在', '件数', '9736件']

リストの最初の行、2行目、3行目、最後の行 (先頭3列分):
['EDINETコード', '提出者種別', '上場区分']
['E00004', '内国法人・組合', '上場']
['E00006', '内国法人・組合', '上場']
...
['E35300', '内国法人・組合(有価証券報告書等の提出義務者以外)', '']

意図いとした通り、リストに『ダウンロード実行日』の行がありません。

らないヘッダー行のスキップ成功です。

3種類のスキップ方法

next() 関数を含めて、3種類の方法を紹介します。

# 1. next() 関数を使う方法。
h = next(csv.reader(f))

# 2. for 文を使う方法。
for h in csv.reader(f):
    # 最初の行を読み込んで、すぐに break する。
    break

# 3. for 文と enumerate() 関数を使う方法。
for (n, h) in enumerate(csv.reader(f), start=1):
    # もし 1 行目に達していたら break する。
    if n >= 1:
        break

いずれかの方法でスキップしたあとに、ふたたび csv.reader(f) を for 文にかけることで、必要な行から読み込むことができました。

どのアプローチをとるべきか?

単純に先頭行をスキップしたいだけなら、1番目の方法が良かったです。複数の行をスキップしたいときでも、その行数だけ next(csv.reader(f)) を書けば、スキップできました。

しかしながら、3行も4行もスキップしたいとなると、3番目のアプローチがおすすめです。

イニュームレート関数 enumerate() で回数を数えて、ブレーク文 break で抜けるアプローチです。

Python マニュアル
組み込み関数 enumerate(iterable, start=0)

next(csv.reader(f)) を何回も書かなくて済むので、簡単です。

それと、先頭の1行だけをスキップするなら、2番目の方法もありえます。

ですが、それなら next(csv.reader(f)) でスキップしたほうが、簡単です。

1番目と3番目の方法が、おすすめです。

毎回スキップ判定するアプローチ

スキップしたい行が CSV ファイルの途中にあるときは、やはり『毎回スキップ判定をする』必要がありました。

スキップを済ませてから読み込む』というアプローチではなく、『判定しながら読み込む』感じになりました。

for 文の中に if 文を持ち込むので、わずかに遅くなりました。

ですが、複雑なスキップを行うときには、有効なアプローチです。

速度比較

CSV の読み込みで、ヘッダー行(最初の1行)をスキップして読み込むコードを、2種類書きました。

スキップを済ませてから読み込む』方法と、『毎回スキップ判定する』方法です。

その実行時間をはかりました。

計測コード

実行時間の計測コードです。100回の CSV 読み込みを、for 文で5回実行しています。

CPU: Core i5-3470S @2.90GHz を 3.60 GHz で使っているのですが、100回の読み込み作業に5秒くらいかかっていました。それを、for 文で5回実行させました。

予想です。

スキップを済ませてから読み込むほうが、if 文を使わない分、速いと思われます。

それをたしかめます。

"""CSV の読み込み速度比較『最初の行をスキップして読み込む』"""
import pathlib
import csv
import timeit

def main():
    """メイン関数"""

    # CSV ファイルパス
    data_csv = pathlib.Path(r'F:\apps\datas\EdinetcodeDlInfo.csv')

    # 繰り返し回数 100回
    n = 100

    # グローバル変数の辞書とローカル変数の辞書を取得
    g = globals()
    g.update(locals())

    for i in range(1, 6):
        print(f'{n} 回読み込み {i} 回目')
        p1 =timeit.timeit('proc1(data_csv)', number=n, globals=g)
        print(f'{p1:.5f} sec: スキップを済ませてから読込')

        p2 =timeit.timeit('proc2(data_csv)', number=n, globals=g)
        print(f'{p2:.5f} sec: 毎回スキップ判定して読込\n')
    return


# 1. スキップを済ませてから読み込むパターン
def proc1(data_csv):
    datas = []
    with data_csv.open('r', encoding='cp932', newline='') as f:
        next(csv.reader(f))
        for c in csv.reader(f):
            datas.append(c)
    return


# 2. 毎回スキップ判定するパターン
def proc2(data_csv):
    datas = []
    with data_csv.open('r', encoding='cp932', newline='') as f:
        for (n, c) in enumerate(csv.reader(f)):
            if n != 0:
                datas.append(c)
    return


if __name__ == '__main__':
    main()

timeit の説明

タイムイット timeit は、時間を計るときに良く使うモジュールです。

Python マニュアル
timeit — 小さなコード断片の実行時間計測
timeit.timeit(stmt=’pass’, setup=’pass’, timer=<default timer>, number=1000000, globals=None)

globals() と locals() は、timeit() の外で定義している『ファイルパス』と『関数 proc1(), proc2()』を使うために使用しました。

Python マニュアル
組み込み関数 globals()

組み込み関数 locals()

実行結果

時間を計った結果です。

100回の読み込みにかかった合計時間です(5回分)。

100 回読み込み 1 回目
5.05402 sec: スキップを済ませてから読込
5.05782 sec: 毎回スキップ判定して読込

100 回読み込み 2 回目
4.98333 sec: スキップを済ませてから読込
5.06986 sec: 毎回スキップ判定して読込

100 回読み込み 3 回目
4.98201 sec: スキップを済ませてから読込
5.06837 sec: 毎回スキップ判定して読込

100 回読み込み 4 回目
4.98614 sec: スキップを済ませてから読込
5.06514 sec: 毎回スキップ判定して読込

100 回読み込み 5 回目
4.98013 sec: スキップを済ませてから読込
5.06190 sec: 毎回スキップ判定して読込

予想通り、『スキップを済ませてから読み込む』方が、速かったです。

微々びびたる差ではありましたが、短い時間で完了していました。

解説

コード例の細かいところの解説です。

なぜ pathlib.Path() を使うのか?⇒ コードが書きやすいから

ファイルパスの管理に、パスリブモジュール pathlib を使う理由です。

使ってみたら、従来の osオーエス モジュールを使うよりも、コードが簡単になったからであります。

os.path の関数を、2重、3重に重ねる必要がなくなりました。

ただ、今回のコード例だと『1つのCSVファイル』しかなかったので、『普通の文字列』と『普通の open() 関数』でも十分でした。

しかしながら、実際のプログラムだと、『おやフォルダ』を取得したり、『結果の出力ファイル』なども使ったりします。それらのパスを生成するときに、以前は os.path の関数を、2重、3重に重ねて書く必要がありました。そういった悩みを、pathlibパスリブ モジュールは解消してくれました。

Python の開発スピードを上げて、さらに高度な開発をしたいと思ったときに、パスリブ pathlib はとても有効でした。

pathlib.Path() の簡単な使用例を書きました。

pathlib でファイルとフォルダの パス作成 ⇒ 存在確認 ⇒ 作成 ⇒ 削除 を行うコード例【Python】

pathlib.Path() の中の r'' は何か?⇒ ロー ストリングス (raw strings)

アルファベットのアール r からはじまる文字列は、『ロー ストリングス (raw strings)』と呼ばれるものでした。ほかにも『raw 文字列 (r-strings)』と呼ばれていました。

Python マニュアル
字句じく解析かいせき リテラル
文字列およびバイト列リテラル

デザインと歴史 FAQエフエーキュー
なぜ raw 文字列 (r-strings) はバックスラッシュで終わってはいけないのですか?

コード例では、パスの区切り文字のバックスラッシュを、1つで済ませるために使いました。

r'' で文字列で書くと、エスケープのために、わざわざバックスラッシュを2重に書く必要がなくなったのです。

ほかにも、正規表現せいきひょうげんのパターンを書くときに良く使いました。

Python マニュアル
re — 正規表現操作

print() の中の f'{変数}' は何か?⇒ フォーマット済み文字列リテラル

アルファベットのエフ f から始まる文字列は、『フォーマット済み文字列リテラル (f-string)』と呼ばれるものでした。

Python マニュアル
フォーマット済み文字列リテラル
(Python 3.6 から使用可能)

テキストの中に、変数を自然に埋め込むことができて、とても便利でした。

next() 関数とは?⇒ for 文の1回分みたいなもの

next() 関数は、for 文の1回分みたいなものでした。

Python マニュアル
組み込み関数 next(iterator[, default])

h = next(csv.reader(f)) の戻り値ですが、for 文の時と同じものが返りましたし、もどが不要なら、変数 h に入れなくても OK でした。

ところで、ファイルを最後まで読み込んだ後に、さらに next() を使うと、例外の『ストップイテレーション StopIteration』が出ました。

StopIterationストップ イテレーション を出さずに、もう一度先頭から読み込みたいときは、シークメソッド f.seek(0) を使うとできました。

変数 f は、open() 関数で受け取ったファイルオブジェクトです。

Python マニュアル
入力と出力 ファイルオブジェクトのメソッド
f.seek(offset, whence)

open() の newline='' ⇒ csv モジュールの説明で指示があった

csv.reader() の説明のところで、そのような指示があったので、そのようにしました。

Python マニュアル
csv — CSV ファイルの読み書き
モジュールコンテンツ
csv.reader(csvfile, dialect=’excel’, **fmtparams)

組み込み関数 open()
newline 引数のドキュメント

オープン関数 Open() で CSV ファイルを開くときに、特に理由がなければ、newline='' をつけておくのが良いと思います。

タイトルとURLをコピーしました