web-dev-qa-db-ja.com

パンダでデータフレームをループする最も効率的な方法は何ですか?

データフレーム内の財務データに対して、自分の複雑な操作を順番に実行したい。

たとえば、 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は、残念ながら列ごとに反復するだけです。

286
Muppet

最新バージョンのパンダには、行を反復処理するための組み込み関数が含まれています。

for index, row in df.iterrows():

    # do some logic here

あるいは、もっと早くしたい場合はitertuples()を使用してください。

しかし、行を繰り返し処理することを避けるために派手な関数を使用するというunutbuの提案は、最速のコードを生成します。

335
Nick Crawford

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(...)を完全に避け、行ごとではなく配列全体(またはデータフレーム)全体を操作して計算を実行する方法を検討してください。

146
unutbu

前述のように、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]

これはおそらく時間の消費を測定するための最善の方法ではありませんが、私にとっては早い方法です。

ここにいくつかの賛否両論があります。

  • .iterrows():インデックスと行の項目を別々の変数に返しますが、かなり遅くなります
  • .itertuples():.iterrows()より速いが、行項目と一緒にindexを返す、ir [0]はindex
  • Zip:最も速いが、行のインデックスにアクセスできない
80
Richard Wong

転置してから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++.

72
Wes McKinney

Nick Crawfordの answerに気付いた後、iterrowsをチェックアウトしましたが、(index、Series)タプルが生成されることがわかりました。どれがあなたにとって最もうまくいくかわからないが、私は私の問題のためにitertuplesメソッドを使うことになった、それは(index、row_value1 ...)タプルをもたらす。

(column、series)タプルを反復処理するiterkvもあります。

25
beardc

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

出典: neural-networks.io

24
FIfi

ちょっとした追加として、単一の列に適用する複雑な関数がある場合にも適用を行うことができます。

http://pandas.pydata.org/pandas-docs/dev/generated/pandas.DataFrame.apply.html

df[b] = df[a].apply(lambda col: do stuff with col here)
20
Carst

@joris が指摘したように、iterrowsitertuplesよりはるかに遅く、itertuplesiterrowsより約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
7
GoingMyWay

確かに、データフレームを反復処理する最も速い方法は、(あなたがするように)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のためのメモリビュー

6
Vlad

もう1つの提案は、行のサブセットがそれを可能にする特性を共有している場合、groupbyをベクトル化計算と組み合わせることです。

5
JoeCondron