記事のディレクトリ
「Pythonのデータの科学ハンドブック」の研究ノート
高性能パンダ:クエリへのeval()()
クエリ()とのeval()デザインの動機:複雑な代数
numpyのとパンダは、高速ベクトル化操作をサポート。例えば、
それは、2つの配列次加算することができます。
import numpy as np
rng = np.random.RandomState(42)
x = rng.rand(1000000)
y = rng.rand(1000000)
%timeit x + y
4.01 ms ± 44.2 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
総合的にはとても速く、通常のPythonのループやリストよりもやって
多く:
%timeit np.fromiter((xi + yi for xi, yi in zip(x, y)), dtype=x.dtype, count=len(x))
563 ms ± 50.2 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
複雑な代数式(化合物の発現)を処理するときしかし、この操作の効率が問題が
、例えば、以下の式比較的低いです。
mask = (x > 0.5) & (y < 0.5)
numpyのは、各サブ代数式を算出するので、この計算は、と等価であるので。
tmp1 = (x > 0.5)
tmp2 = (y < 0.5)
mask = tmp1 & tmp2
つまり、明示的にメモリを割り当てるために、各中間処理が必要です。xとyの配列の数ならば
グループが非常に大きいので、計算時間とメモリ消費量の多くを取るでしょう。
import numexpr
mask_numexpr = numexpr.evaluate('(x > 0.5) & (y < 0.5)')
np.allclose(mask, mask_numexpr)
True
利点は、それが、代数式の計算における一時的アレイ不要Numexpr点に起因する
メモリの全てとの大きな配列を処理する場合は特に、計算は、numpyのより効率的です。パンダのeval()とクエリ()ツールは、実際にNumexprをベースに
実装を。
()ハイパフォーマンスコンピューティングpandas.evalと
パンダのeval()関数文字列データフレームの性能は代数的操作を実現して
、例えばデータフレーム数は、次のように:
import pandas as pd
nrows, ncols = 100000, 100
rng = np.random.RandomState(42)
df1, df2, df3, df4 = (pd.DataFrame(rng.rand(nrows, ncols))
for i in range(4))
あなたはパンダと4データフレームを計算するための一般的な方法を使用する場合は、書くことができます:
%timeit df1 + df2 + df3 + df4
116 ms ± 2.23 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
これは、計算され、同じ結果とpd.eval代数文字列を取得することができます。
%timeit pd.eval('df1 + df2 + df3 + df4')
53.7 ms ± 1.36 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
evalの()のバージョンは、早く通常の代数的方法(と少ないメモリ消費量)の二倍で
、結果は同じです。
np.allclose(df1 + df2 + df3 + df4,
pd.eval('df1 + df2 + df3 + df4'))
True
pd.eval()サポートされる演算子
多くの操作をサポートするためのパンダv0.16バージョン、pd.eval()を起動します。これらを実証するため
の操作を、整数型のデータフレームを作成します。
df1, df2, df3, df4, df5 = (pd.DataFrame(rng.randint(0, 1000, (100, 3)))
for i in range(5))
- 算術演算子。
pd.eval()は、例えば、全ての算術演算子をサポートしています。
result1 = -df1 * df2 / (df3 + df4) - df5
result2 = pd.eval('-df1 * df2 / (df3 + df4) - df5')
np.allclose(result1, result2)
True
- 比較演算子。
pd.eval()は代数的連鎖を含む比較演算子のすべて、サポートしています
(チェーン式):
result1 = (df1 < df2) & (df2 <= df3) & (df3 != df4)
result2 = pd.eval('df1 < df2 <= df3 != df4')
np.allclose(result1, result2)
True
- ビット演算子。
pd.eval()サポート&(と)と|(または)対立演算子:
result1 = (df1 < 0.5) & (df2 < 0.5) | (df3 < df4)
result2 = pd.eval('(df1 < 0.5) & (df2 < 0.5) | (df3 < df4)')
np.allclose(result1, result2)
True
加えて、また使用することができる等リテラルブール代数のどので:
result3 = pd.eval('(df1 < 0.5) and (df2 < 0.5) or (df3 < df4)')
np.allclose(result1, result3)
True
- オブジェクトのプロパティとインデックス。
pd.eval()オブジェクトのプロパティをobj.attr文法によって得ることができる
抵抗、シンタックスは、オブジェクトOBJ [インデックス]でインデックスされます。
result1 = df2.T[0] + df3.iloc[1]
result2 = pd.eval('df2.T[0] + df3.iloc[1]')
np.allclose(result1, result2)
True
- その他の操作。
関数呼び出し、条件文をサポートしていません()現在pd.evalにループし
、より複雑な操作。あなたはこれらの操作をしたい場合はNumexprの手段により実施することが可能
になりました。
DataFrame.eval()操作で列間を達成するために
pd.eval()関数は、トップパンダであるため、これデータフレームがある
のeval()メソッドは、同様の操作を行うことができます。eval()メソッドを使用する利点は、によって可能であり
、例えば、列名を計算します。
df = pd.DataFrame(rng.rand(1000, 3), columns=['A', 'B', 'C'])
df.head()
A | B | C | |
---|---|---|---|
0 | 0.375506 | 0.406939 | 0.069938 |
1 | 0.069087 | 0.235615 | 0.154374 |
2 | 0.677945 | 0.433839 | 0.652324 |
3 | 0.264038 | 0.808055 | 0.347197 |
4 | 0.589161 | 0.252418 | 0.557789 |
pd.evalが以前に()が記述した場合、次の代数3列により計算することができます。
result1 = (df['A'] + df['B']) / (df['C'] - 1)
result2 = pd.eval("(df.A + df.B) / (df.C - 1)")
np.allclose(result1, result2)
True
DataFrame.eval()メソッドは、単純な代数列名によって達成することができます。
result3 = df.eval('(A + B) / (C - 1)')
np.allclose(result1, result3)
True
列名は、代数的表現を計算するために、ここで使用される変数と、同じ結果が正確であることに注意してください。
- 新しいコンピューティング機能を含めることに加えて、DataFrame.eval()は以前に記載して、DataFrame.eval()は新規作成することができ
、カラムを。データフレームは、また、上記を証明するために使用される、列名は「A」、「B」及び「C」です。
df.head()
A | B | C | |
---|---|---|---|
0 | 0.375506 | 0.406939 | 0.069938 |
1 | 0.069087 | 0.235615 | 0.154374 |
2 | 0.677945 | 0.433839 | 0.652324 |
3 | 0.264038 | 0.808055 | 0.347197 |
4 | 0.589161 | 0.252418 | 0.557789 |
新しいカラム(df.evalと「D」)を作成し、他の列の計算に割り当てることができます
値:
df.eval('D = (A + B) / C', inplace=True)
df.head()
A | B | C | D | |
---|---|---|---|---|
0 | 0.375506 | 0.406939 | 0.069938 | 11.187620 |
1 | 0.069087 | 0.235615 | 0.154374 | 1.973796 |
2 | 0.677945 | 0.433839 | 0.652324 | 1.704344 |
3 | 0.264038 | 0.808055 | 0.347197 | 3.087857 |
4 | 0.589161 | 0.252418 | 0.557789 | 1.508776 |
また、既存の列を変更することができます。
df.eval('D = (A - B) / C', inplace=True)
df.head()
A | B | C | D | |
---|---|---|---|---|
0 | 0.375506 | 0.406939 | 0.069938 | -0.449425 |
1 | 0.069087 | 0.235615 | 0.154374 | -1.078728 |
2 | 0.677945 | 0.433839 | 0.652324 | 0.374209 |
3 | 0.264038 | 0.808055 | 0.347197 | -1.566886 |
4 | 0.589161 | 0.252418 | 0.557789 | 0.603708 |
- ローカル変数を使用してDataFrame.eval()
DataFrame.eval()メソッドは、@記号のPythonで使用されるローカル変数サポートし
、次のように、量を:
column_mean = df.mean(1)
result1 = df['A'] + column_mean
result2 = df.eval('A + @column_mean')
np.allclose(result1, result2)
True
@ログインはあなたの柔軟性を許可「これは代わりに、列名の変数名である」と述べた
2つのリソースの「名前空間」(列の名前空間とPythonオブジェクトの名前を使用する
代数名前空間を計算します)。@記号のみができる、という注意
pandas.evalでDataFrame.eval()メソッドではなく、()関数を使用
pandas.eval()関数はのみ(パイソン)を取得することができますので、使用中の名前の
コンテンツのスペースを。
DataFrame.query()方法
呼ばれる他の器具、に基づいてデータフレーム列代数計算
例えば、クエリ():
result1 = df[(df.A < 0.5) & (df.B < 0.5)]
result2 = pd.eval('df[(df.A < 0.5) & (df.B < 0.5)]')
np.allclose(result1, result2)
True
以前のDataFrame.evalを()を導入し、これはデータフレームを持つ列される
代数が作成されますが、DataFrame.eval()構文を使用することはできません。しかし、このために
フィルタリング演算の種類、あなたは()メソッドを照会することができます:
result2 = df.query('A < 0.5 and B < 0.5')
np.allclose(result1, result2)
True
この処理方法のシンタックスよりも良好な性能を計算することに加えて、より良いマスク代数構文よりも
溶液。なお、クエリ()メソッドは、@記号によって支持されているローカル変数を指します。
Cmean = df['C'].mean()
result1 = df[(df.A < Cmean) & (df.B < Cmean)]
result2 = df.query('A < @Cmean and B < @Cmean')
np.allclose(result1, result2)
True
パフォーマンスは、機会を使用することにしました
これら2つの関数は二つの側面を検討する必要が使用するかどうかを検討する場合:計算時間とメモリ消費者の
消費量、およびメモリ消費量をより重要な要因です。ただ、前述のように、それぞれの関与
代数やパンダ複合のデータフレームnumpyの配列は、一時的な配列を持つことになります
例えば:
x = df[(df.A < 0.5) & (df.B < 0.5)]
これは基本的に同等に:
tmp1 = df.A < 0.5
tmp2 = df.B < 0.5
tmp3 = tmp1 & tmp2
x = df[tmp3]
お使いのシステムメモリ(通常はいくつかの単語のギターよりも大きな一時的なデータフレームのメモリ要件場合
セクション)、それは、使用のeval()とクエリ()代数表現するのが最善です。あなたは、次の使用可能
メモリ消費変数について、おそらく推定する方法を:
df.values.nbytes
32000
パフォーマンスの面では、あなたが最大のシステムメモリを使用しない場合でも、evalの()の計算速度が
速く、従来の方法よりも。今のシステムのCPU暫定データフレームのパフォーマンスのボトルネックになり
L1とL2キャッシュ間のコントラストが-キャッシュはゆっくりと異なるキャッシュ間を回避一時的に移動する十分な大きさの、その後のeval()である場合は
、ファイルを。実際には、私はと共通の計算方法を発見したのeval /クエリ計算
の計算時間の差が常に小さいの配列に対処するので、明らかに、一般的な方法ではありません
が、より速く!利点はeval /クエリ方法は、時々 、構文、主にメモリに保存され
、より簡潔な。