データフレーム内の財務データに対して、自分の複雑な操作を順番に実行したい。
たとえば、 Yahoo Finance から取得した次のMSFT CSVファイルを使用しています。
Date,Open,High,Low,Close,Volume,Adj Close
2011-10-19,27.37,27.47,27.01,27.13,42880000,27.13
2011-10-18,26.94,27.40,26.80,27.31,52487900,27.31
2011-10-17,27.11,27.42,26.85,26.98,39433400,26.98
2011-10-14,27.31,27.50,27.02,27.27,50947700,27.27
....
それから私は次のことをします:
#!/usr/bin/env python
from pandas import *
df = read_csv('table.csv')
for i, row in enumerate(df.values):
date = df.index[i]
open, high, low, close, adjclose = row
#now perform analysis on open/close based on date, etc..
それが最も効率的な方法ですか?パンダのスピードを重視しているので、インデックスを取得するような方法で値を反復する特別な関数があるに違いないと思います(おそらくメモリ効率を上げるためにジェネレータを通して)。 df.iteritems
は、残念ながら列ごとに反復するだけです。
最新バージョンのパンダには、行を反復処理するための組み込み関数が含まれています。
for index, row in df.iterrows():
# do some logic here
あるいは、もっと早くしたい場合はitertuples()
を使用してください。
しかし、行を繰り返し処理することを避けるために派手な関数を使用するというunutbuの提案は、最速のコードを生成します。
PandasはNumPy配列に基づいています。 NumPy配列の処理速度を上げるための鍵は、行全体または項目ごとではなく、一度に配列全体に対して操作を実行することです。
たとえば、close
が1次元配列であり、日々のパーセント変化を望む場合、
pct_change = close[1:]/close[:-1]
これは、パーセント変化の配列全体を1つのステートメントとして計算するのではなく、計算します。
pct_change = []
for row in close:
pct_change.append(...)
そのため、Pythonのループfor i, row in enumerate(...)
を完全に避け、行ごとではなく配列全体(またはデータフレーム)全体を操作して計算を実行する方法を検討してください。
前述のように、pandasオブジェクトは配列全体を一度に処理するときに最も効率的です。しかし、私のように、何かを実行するためにパンダDataFrameを本当にループする必要がある人のために、私はそれをする少なくとも3つの方法を見つけました。私は、3つのうちどれが最も時間がかからないかを確かめるために短いテストをしました。
t = pd.DataFrame({'a': range(0, 10000), 'b': range(10000, 20000)})
B = []
C = []
A = time.time()
for i,r in t.iterrows():
C.append((r['a'], r['b']))
B.append(time.time()-A)
C = []
A = time.time()
for ir in t.itertuples():
C.append((ir[1], ir[2]))
B.append(time.time()-A)
C = []
A = time.time()
for r in Zip(t['a'], t['b']):
C.append((r[0], r[1]))
B.append(time.time()-A)
print B
結果:
[0.5639059543609619, 0.017839908599853516, 0.005645036697387695]
これはおそらく時間の消費を測定するための最善の方法ではありませんが、私にとっては早い方法です。
ここにいくつかの賛否両論があります。
転置してからiteritemを呼び出すことで、行をループできます。
for date, row in df.T.iteritems():
# do some logic here
その場合の効率についてはよくわかりません。反復アルゴリズムで最高のパフォーマンスを引き出すには、 Cython で記述してみてください。そうすれば、次のようになります。
def my_algo(ndarray[object] dates, ndarray[float64_t] open,
ndarray[float64_t] low, ndarray[float64_t] high,
ndarray[float64_t] close, ndarray[float64_t] volume):
cdef:
Py_ssize_t i, n
float64_t foo
n = len(dates)
for i from 0 <= i < n:
foo = close[i] - open[i] # will be extremely fast
私は最初に純粋なPythonでアルゴリズムを書くことをお勧めします。それが動作することを確かめ、それがどれくらい速いのかを確かめます/ C++.
Nick Crawfordの answerに気付いた後、iterrows
をチェックアウトしましたが、(index、Series)タプルが生成されることがわかりました。どれがあなたにとって最もうまくいくかわからないが、私は私の問題のためにitertuples
メソッドを使うことになった、それは(index、row_value1 ...)タプルをもたらす。
(column、series)タプルを反復処理するiterkv
もあります。
3つの選択肢があります。
index で(最も単純):
>>> for index in df.index:
... print ("df[" + str(index) + "]['B']=" + str(df['B'][index]))
iterrows (最もよく使われる)の場合:
>>> for index, row in df.iterrows():
... print ("df[" + str(index) + "]['B']=" + str(row['B']))
itertuples (最速)を使用します。
>>> for row in df.itertuples():
... print ("df[" + str(row.Index) + "]['B']=" + str(row.B))
3つのオプションは次のように表示されます。
df[0]['B']=125
df[1]['B']=415
df[2]['B']=23
df[3]['B']=456
df[4]['B']=189
df[5]['B']=456
df[6]['B']=12
ちょっとした追加として、単一の列に適用する複雑な関数がある場合にも適用を行うことができます。
http://pandas.pydata.org/pandas-docs/dev/generated/pandas.DataFrame.apply.html
df[b] = df[a].apply(lambda col: do stuff with col here)
@joris が指摘したように、iterrows
はitertuples
よりはるかに遅く、itertuples
はiterrows
より約100倍遅く、私は5027505レコードでDataFrameで両方のメソッドの速度をテストしました。結果はiterrows
に対するもので、1200it/sです。 itertuples
は120000it/sです。
itertuples
を使用する場合、forループ内のすべての要素はnamedtupleであるため、各列の値を取得するには、次のコード例を参照してください。
>>> df = pd.DataFrame({'col1': [1, 2], 'col2': [0.1, 0.2]},
index=['a', 'b'])
>>> df
col1 col2
a 1 0.1
b 2 0.2
>>> for row in df.itertuples():
... print(row.col1, row.col2)
...
1, 0.1
2, 0.2
確かに、データフレームを反復処理する最も速い方法は、(あなたがするように)df.values
を介して、またはそれぞれの列を別々にdf.column_name.values
を介してアクセスすることで、基礎となるnumpy ndarrayにアクセスすることです。インデックスにもアクセスしたいので、そのためにはdf.index.values
を使用できます。
index = df.index.values
column_of_interest1 = df.column_name1.values
...
column_of_interestk = df.column_namek.values
for i in range(df.shape[0]):
index_value = index[i]
...
column_value_k = column_of_interest_k[i]
ピトニックじゃない?もちろんです。しかし速いです。
もっとたくさんのジュースをループの外に絞りたいのであれば、 cython を調べます。 Cythonはあなたが非常にスピードアップすることを可能にするでしょう(10x-100xだと思います)。最大限のパフォーマンスチェックのために cythonのためのメモリビュー 。
もう1つの提案は、行のサブセットがそれを可能にする特性を共有している場合、groupbyをベクトル化計算と組み合わせることです。