web-dev-qa-db-ja.com

Pandas:Dropna後のインプレースの名前変更に特有のパフォーマンス低下

pandas issues の問題としてこれを報告しました。その間、他の人が同様の問題に遭遇した場合に備えて、他の時間を節約することを期待して、これをここに投稿します。

最適化が必要なプロセスをプロファイリングすると、列の名前を変更しないとx120だけパフォーマンス(実行時間)が向上することがわかりました。プロファイリングは、これがガベージコレクションに関連していることを示します(以下を参照)。

さらに、dropnaメソッドを使用しないことにより、期待されるパフォーマンスが回復します。

次の短い例は、係数x12を示しています。

import pandas as pd
import numpy as np

inplace = True

%%timeit
np.random.seed(0)
r,c = (7,3)
t = np.random.Rand(r)
df1 = pd.DataFrame(np.random.Rand(r,c), columns=range(c), index=t)
indx = np.random.choice(range(r),r/3, replace=False)
t[indx] = np.random.Rand(len(indx))
df2 = pd.DataFrame(np.random.Rand(r,c), columns=range(c), index=t)
df = (df1-df2).dropna()
## inplace rename:
df.rename(columns={col:'d{}'.format(col) for col in df.columns}, inplace=True)

100ループ、最高3:ループあたり15.6 ms

%%prunの最初の出力行:

ncalls tottime percall cumtime percall filename:lineno(関数)

1  0.018 0.018 0.018 0.018 {gc.collect}

inplace = False

%%timeit
np.random.seed(0)
r,c = (7,3)
t = np.random.Rand(r)
df1 = pd.DataFrame(np.random.Rand(r,c), columns=range(c), index=t)
indx = np.random.choice(range(r),r/3, replace=False)
t[indx] = np.random.Rand(len(indx))
df2 = pd.DataFrame(np.random.Rand(r,c), columns=range(c), index=t)
df = (df1-df2).dropna()
## avoid inplace:
df = df.rename(columns={col:'d{}'.format(col) for col in df.columns})

1000ループ、最高3:ループあたり1.24 ms

ドロップナを避ける

予想されるパフォーマンスは、dropnaメソッドを回避することで回復します。

%%timeit
np.random.seed(0)
r,c = (7,3)
t = np.random.Rand(r)
df1 = pd.DataFrame(np.random.Rand(r,c), columns=range(c), index=t)
indx = np.random.choice(range(r),r/3, replace=False)
t[indx] = np.random.Rand(len(indx))
df2 = pd.DataFrame(np.random.Rand(r,c), columns=range(c), index=t)
#no dropna:
df = (df1-df2)#.dropna()
## inplace rename:
df.rename(columns={col:'d{}'.format(col) for col in df.columns}, inplace=True)

1000ループ、最高3:ループあたり865 µs

%%timeit
np.random.seed(0)
r,c = (7,3)
t = np.random.Rand(r)
df1 = pd.DataFrame(np.random.Rand(r,c), columns=range(c), index=t)
indx = np.random.choice(range(r),r/3, replace=False)
t[indx] = np.random.Rand(len(indx))
df2 = pd.DataFrame(np.random.Rand(r,c), columns=range(c), index=t)
## no dropna
df = (df1-df2)#.dropna()
## avoid inplace:
df = df.rename(columns={col:'d{}'.format(col) for col in df.columns})

1000ループ、最高3:ループあたり902 µs

33
eldad-a

これはgithubの説明のコピーです。

保証なしがあり、inplace操作が実際に高速である。多くの場合、それらは実際にはコピーで機能する同じ操作ですが、トップレベルの参照が再割り当てされます。

この場合のパフォーマンスの違いの理由は次のとおりです。

(df1-df2).dropna()呼び出しは、データフレームのスライスを作成します。新しい操作を適用すると、これがSettingWithCopyチェックをトリガーします。これは、couldがコピーであるためです(多くの場合、そうではありません)。

このチェックでは、ガベージコレクションを実行して、一部のキャッシュ参照を消去し、コピーであるかどうかを確認する必要があります。残念ながらpython構文はこれを不可避にします。

最初にコピーを作成するだけでは、これを実現することはできません。

df = (df1-df2).dropna().copy()

inplace操作が続くと、以前と同じようにパフォーマンスが向上します。

私の個人的な意見:私neverインプレース操作を使用します。構文は読みにくく、利点はありません。

41
Jeff