LoginSignup
20
24

More than 5 years have passed since last update.

複数csvを高速にdataframeで読み込みたかった話

Last updated at Posted at 2018-07-12

会社で複数のcsvファイルをdataframeとして読み込みする機会が多いため、処理時間をはかってみる。
テスト用の500行×500列の適当な乱数が入ったcsvを使用。

実行環境

OS:Windows10
CPU:ryzen2700x
メモリ:16GB
python3.6.4

1csvをpd.read_csv読み込み(Cエンジン)

import time
import pandas as pd


def main():
    t1 = time.time()
    # 関数呼び出し
    bench1()

    t2 = time.time()
    elapsed_time = t2-t1
    print(elapsed_time)


# デフォルト
def bench1():
    base_file = r"C:\Users\hoge\Desktop\csv_read_bench\testdata\001.csv"
    df = pd.read_csv(base_file)


if __name__ == '__main__':
    main()

結果(単位は秒)

1回目 2回目 3回目
0.097 0.095 0.097

前座として1ファイルの読み込み速度を計測。
pandasライブラリのread_csv結構速い。

読み込みエンジンをpythonにする

def main():
    t1 = time.time()
    # 関数呼び出し
    bench2()
    t2 = time.time()
    elapsed_time = t2-t1
    print(elapsed_time)


# engineをpythonにする
def bench2():
    base_file = r"C:\Users\hoge\Desktop\csv_read_bench\testdata\001.csv"
    df = pd.read_csv(base_file, engine="python")


if __name__ == '__main__':
    main()

結果(単位は秒)

1回目 2回目 3回目
0.251 0.257 0.258

ファイルパスに日本語が入ってたりするとCエンジンではエラー吐くため
Pythonエンジンで計測。Cエンジンに比べると少し遅くなった。

読み込み列を指定

def main():
    t1 = time.time()
    # 関数呼び出し
    bench3()

    t2 = time.time()
    elapsed_time = t2-t1
    print(elapsed_time)


# 読み込み列を指定
def bench3():
    read_cols = [0, 1, 2]
    base_file = r"C:\Users\hoge\Desktop\csv_read_bench\testdata\001.csv"
    df = pd.read_csv(base_file, engine="python", usecols=read_cols)


if __name__ == '__main__':
    main()

結果(単位は秒)

1回目 2回目 3回目
0.105 0.103 0.104

読み込み列を一部に指定するとそれなりに上がる。
しかし、読み込み列が500列から3列に変わったことで劇的に高速化とはいかないみたいだ。

100csvを読み込ませる

import glob


def main():
    csv_search_path = r"C:\Users\hoge\Desktop\csv_read_bench\testdata\*.csv"
    csv_path_ary = glob.glob(csv_search_path)

    t1 = time.time()
    # 関数呼び出し
    bench4(csv_path_ary)

    t2 = time.time()
    elapsed_time = t2-t1
    print(elapsed_time)


# 100個のcsvをdataframeに格納
def bench4(csv_path_ary):
    for csv_path in csv_path_ary:
        tmp_df = pd.read_csv(csv_path, engine="python")

        if 'df' in locals():
            df = pd.concat([df, tmp_df])
        else:
            df = tmp_df


if __name__ == '__main__':
    main()

結果(単位は秒)

1回目 2回目 3回目
29.95 29.80 29.89

本題、100ファイルを1ファイルずつ読み込んでconcatで結合。
forで処理してるからか、予想通り遅い。

並列化してみる

from multiprocessing import Pool
import os

def main():
    csv_search_path = r"C:\Users\hoge\Desktop\csv_read_bench\testdata\*.csv"
    csv_path_ary = glob.glob(csv_search_path)

    t1 = time.time()
    # 関数呼び出し
    bench5(csv_path_ary)

    t2 = time.time()
    elapsed_time = t2-t1
    print(elapsed_time)


# 並列化してみる
def bench5(csv_path_ary):
    p = Pool(os.cpu_count())
    df = pd.concat(p.map(bench5_multi, csv_path_ary))
    # p.close()


# 並列化読み込み
def bench5_multi(csv_path):
    return pd.read_csv(csv_path, engine="python")


if __name__ == '__main__':
    main()

結果(単位は秒)

1回目 2回目 3回目
4.720 4.658 4.806

multiprocessingで並列化したらだいぶ速くなった。
凄い楽に並列化が出来る。

並列時のCエンジンも計測

from multiprocessing import Pool
import os

def main():
    csv_search_path = r"C:\Users\hoge\Desktop\csv_read_bench\testdata\*.csv"
    csv_path_ary = glob.glob(csv_search_path)

    t1 = time.time()
    # 関数呼び出し
    bench6(csv_path_ary)

    t2 = time.time()
    elapsed_time = t2-t1
    print(elapsed_time)


# 並列化してみる
def bench6(csv_path_ary):
    p = Pool(os.cpu_count())
    df = pd.concat(p.map(bench6_multi_C, csv_path_ary))
    # p.close()


# 並列化読み込み(C engine)
def bench6_multi_C(csv_path):
    return pd.read_csv(csv_path)


if __name__ == '__main__':
    main()

結果(単位は秒)

1回目 2回目 3回目
2.732 2.720 2.722

最後にCエンジンで並列化の時間を計測。
100ファイル読み込むのに3秒なら結構速いのかなぁ。

20
24
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
20
24