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

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


## 実行環境
OS:Windows10
CPU:ryzen2700x
メモリ:16GB
python3.6.4

## 1csvをpd.read_csv読み込み(Cエンジン)
```py3
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にする

```py3
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エンジンに比べると少し遅くなった。

## 読み込み列を指定
```py3
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を読み込ませる
```py3
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で処理してるからか、予想通り遅い。

## 並列化してみる
```py3
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エンジンも計測
```py3
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秒なら結構速いのかなぁ。