LoginSignup
29
24

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

Last updated at Posted at 2019-05-02

概要

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

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

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

経緯

この記事を見て、私も試してみようと思い立ちました。
Pythonを速くしたいときにやったこと
色々試行錯誤されています。

内容説明

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

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

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

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

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

バージョン

module version
pandas '0.19.2' or '0.24.2' どちらでも同様の結果

ソースコード

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

測定結果

# 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

しゅーるですな...。

番外編(2019/10/12追記)

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

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を使わずに)

関連記事

[Python3 / numpy] pandasで並列演算するよりも、frompyfuncが高速?

29
24
1

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
29
24