[Python3 / pandas] DataFrameで1行ずつのloop処理をしたいとき（速度比較）

# 概要

今回は、辞書型: `{ 'hoge': 'ほげ' }`
みたいなデータを`dataframe`の末尾に追加するときの速度比較を行いました。

なんと、実装方法によって**所要時間に1.5倍以上の差**がありました！！！(゜д゜;)
そんなもんじゃなかったです。**400倍**近い差が出ました(2019/05/13追記)

※最初の投稿の後、間違いに気づき次第ちょこちょこ修正しています。

# 経緯

この記事を見て、私も試してみようと思い立ちました。
[Pythonを速くしたいときにやったこと](https://qiita.com/shaka/items/f180ae4dc945dc7b9066)
色々試行錯誤されています。

# 内容説明

速度計測のため、ひたすら同じ処理を繰り返します。
今回比較する処理は、dataframeの末尾に辞書型のデータを連結する処理です。

やり方は５パターン考えてみました。

* 1レコードずつdataframeに追加
    １. `some_dataframe.loc[index] = 辞書`
    ２. `some_datafrmae.iloc[index] = 辞書` => エラーになるため以下略
    ３. `some_dataframe.append(pd.Series(辞書), ignore_index=True)`
    ４. `pd.concat([some_dataframe, pd.Dataframe(辞書, index=index)])`

* 辞書型配列を作って、配列をまとめてdataframe化(2019/05/13追加)
    ５. `pd.concat([some_dataframe, pd.from_dict(辞書型配列)])`

1が一番わかりやすい！
3, 4, 5 はちょっとややこしいですね...！
どれが早いのでしょうか！！？(>_<)

# バージョン
| module | version  |
|:------:|:--------:|
| pandas | '0.19.2' or '0.24.2' どちらでも同様の結果 |

# ソースコード
```python
from time import time
import pandas as pd

# 連結しまくる辞書
some_dict = { 'hoge': 'ほげ', 'kindness': 'やさしさが大事' }

def loc_method():
    some_df = pd.DataFrame([], columns=some_dict.keys())
    for i in range(3000):
        some_df.loc[i] = some_dict
    return some_df

def append_series_method():
    some_df = pd.DataFrame([], columns=some_dict.keys())
    for i in range(3000):
        some_df = some_df.append(pd.Series(some_dict), ignore_index=True)
    return some_df

def concat_method():
    some_df = pd.DataFrame([], columns=some_dict.keys())
    for i in range(3000):
        some_df = pd.concat([some_df, pd.DataFrame(some_dict, index=[i])])
    return some_df

def from_dict_method():
    some_df = pd.DataFrame([], columns=some_dict.keys())
    dict_array = []
    for i in range(3000):
        dict_array.append(some_dict)
    some_df = pd.concat([some_df, pd.DataFrame.from_dict(dict_array)])
    return some_df
```

# 測定結果

```python
# locで結合した場合
start = time()
some_df = loc_method()
elapsed_time = time() - start
print('経過時間: {}sec'.format(elapsed_time))
>> 経過時間: 20.205236673355103sec

# appendで結合した場合
start = time()
some_df = append_series_method()
elapsed_time = time() - start
print('経過時間: {}sec'.format(elapsed_time))
>> 経過時間: 17.460779666900635sec

# concatで結合した場合
start = time()
some_df = concat_method()
elapsed_time = time() - start
print('経過時間: {}sec'.format(elapsed_time))
>> 経過時間: 11.338160991668701sec

# from_dictで辞書型配列をまとめてdataframe化し、その後くっつけた場合
start = time()
some_df = from_dict_method()
elapsed_time = time() - start
print('経過時間: {}sec'.format(elapsed_time))
>> 経過時間: 0.03905987739562988sec !!!!!
```
( д) ﾟ ﾟ 

~~まさかのややこしそうな方が圧倒的に早いという結果に....！
（一見して一番遅そう...）~~

~~多分、dataframe同士の方がやり取りも楽なんでしょうね！（適当）~~

(2019/05/13追記)
今までのは何だったんでしょう.....

1レコードずつdataframeに連結するのでなく、可能な限り辞書型を配列としてまとめておいて一気にdataframe化し、最後に1回だけ連結すると計算コストが低いみたいです。

どうしても1レコードずつdataframe化する必要があるなら、4の`concat`で連結する処理を使うのがよさそうです。


# 補足

#### 処理結果について
~~上の二つの処理では、得られる結果（some_df）が若干違いますのでご注意ください。
今回は速度比較が目的だったため、得られる結果が異なっていても大きな問題ではないと考え、そのままにしています。~~
どの処理を選んでも同じ結果が得られるようにソースコードを修正しました。  

結果は以下のようになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/327407/0e776fd4-0bf2-3c35-7819-00f823033f4c.png)

しゅーるですな...。

#### 番外編（2019/10/12追記）

1行ずつの追加ではありませんが、こっちの方が速度早いみたいでした。
内部処理の見直しが必要になりますが、速度面で不満を抱えているのであれば、使用してみる価値はあると思います。

```python
from time import time
import numpy as np
import pandas as pd

# 連結しまくる辞書
some_dict = { 'hoge': 'ほげ', 'kindness': 'やさしさが大事' }

def use_frompyfunc():
    df = pd.DataFrame([], columns=some_dict.keys())

    def func_add(target):
        return target
    function = np.frompyfunc(func_add, 1, 1)

    df['hoge'] = function(np.full(shape=3000, fill_value='ほげ'))
    df['kindness'] = function(np.full(shape=3000, fill_value='やさしさが大事'))
    return df

start = time()
some_df = use_frompyfunc()
elapsed_time = time() - start
print('経過時間: {}sec'.format(elapsed_time))
>> 経過時間: 0.025362730026245117sec
```
参考： [[Python3] numpy, series, dataframeで、1列のデータをまとめて比較したい(loopを使わずに)](https://teratail.com/questions/213024)


# 関連記事

[[Python3 / numpy] pandasで並列演算するよりも、frompyfuncが高速？](https://qiita.com/siruku6/items/976d228797576234e839)