我想对数据框架中的财务数据按顺序执行自己的复杂操作。

例如,我正在使用以下来自雅虎财经的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..

这是最有效的方法吗?考虑到在pandas中对速度的关注,我认为必须有一些特殊的函数以一种也检索索引的方式遍历值(可能通过生成器来提高内存效率)?df。不幸的是Iteritems只逐列迭代。


当前回答

你有三个选择:

按索引(最简单):

>>> 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))

有三个选项显示如下内容:

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

来源:alphons.io

其他回答

正如@joris指出的那样,iterrows比itertuples慢得多,itertuples比iterrows快大约100倍,我在一个有500万条记录的DataFrame中测试了这两种方法的速度,结果是iterrows的速度为1200it/s,而itertuples的速度为120000it/s。

如果使用itertuple,请注意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

看最后一个

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(round(time.time()-A,5))

C = []
A = time.time()
for ir in t.itertuples():
    C.append((ir[1], ir[2]))    
B.append(round(time.time()-A,5))

C = []
A = time.time()
for r in zip(t['a'], t['b']):
    C.append((r[0], r[1]))
B.append(round(time.time()-A,5))

C = []
A = time.time()
for r in range(len(t)):
    C.append((t.loc[r, 'a'], t.loc[r, 'b']))
B.append(round(time.time()-A,5))

C = []
A = time.time()
[C.append((x,y)) for x,y in zip(t['a'], t['b'])]
B.append(round(time.time()-A,5))
B

0.46424
0.00505
0.00245
0.09879
0.00209

我相信循环dataframe最简单有效的方法是使用numpy和numba。在这种情况下,循环在许多情况下可以近似地与向量化操作一样快。如果numba不是一个选项,那么普通numpy可能是下一个最佳选项。正如已经多次提到的,您的默认值应该是向量化的,但是这个答案仅仅考虑了有效的循环,无论出于什么原因决定了循环。

对于测试用例,让我们使用@DSM回答的计算百分比变化的示例。这是一个非常简单的情况,作为一个实际问题,你不会写一个循环来计算它,但这样它为时间向量化方法和循环提供了一个合理的基线。

让我们用一个小的DataFrame来设置这4种方法,下面我们将在一个更大的数据集上对它们进行计时。

import pandas as pd
import numpy as np
import numba as nb

df = pd.DataFrame( { 'close':[100,105,95,105] } )

pandas_vectorized = df.close.pct_change()[1:]

x = df.close.to_numpy()
numpy_vectorized = ( x[1:] - x[:-1] ) / x[:-1]
        
def test_numpy(x):
    pct_chng = np.zeros(len(x))
    for i in range(1,len(x)):
        pct_chng[i] = ( x[i] - x[i-1] ) / x[i-1]
    return pct_chng

numpy_loop = test_numpy(df.close.to_numpy())[1:]

@nb.jit(nopython=True)
def test_numba(x):
    pct_chng = np.zeros(len(x))
    for i in range(1,len(x)):
        pct_chng[i] = ( x[i] - x[i-1] ) / x[i-1]
    return pct_chng
    
numba_loop = test_numba(df.close.to_numpy())[1:]

下面是100,000行的DataFrame上的计时(使用Jupyter的%timeit函数执行的计时,为了便于阅读,折叠成摘要表):

pandas/vectorized   1,130 micro-seconds
numpy/vectorized      382 micro-seconds
numpy/looped       72,800 micro-seconds
numba/looped          455 micro-seconds

总结:对于简单的情况,比如这个例子,为了简单和可读性,可以使用(向量化的)pandas,为了速度,可以使用(向量化的)numpy。如果您确实需要使用循环,请使用numpy。如果numba可用,可以将其与numpy结合使用以获得更高的速度。在这种情况下,numpy + numba几乎和向量化numpy代码一样快。

其他细节:

Not shown are various options like iterrows, itertuples, etc. which are orders of magnitude slower and really should never be used. The timings here are fairly typical: numpy is faster than pandas and vectorized is faster than loops, but adding numba to numpy will often speed numpy up dramatically. Everything except the pandas option requires converting the DataFrame column to a numpy array. That conversion is included in the timings. The time to define/compile the numpy/numba functions was not included in the timings, but would generally be a negligible component of the timing for any large dataframe.

最新版本的pandas现在包含了一个用于遍历行的内置函数。

for index, row in df.iterrows():

    # do some logic here

或者,如果你想要更快,可以使用itertuples()

但是,unutbu建议使用numpy函数来避免遍历行,这会产生最快的代码。

在注意到Nick Crawford的答案后,我检查了iterrows,但发现它产生(index, Series)元组。不确定哪种方法最适合您,但我最终使用了itertuples方法来解决我的问题,该方法生成(index, row_value1…)元组。

还有iterkv,它遍历(column, series)元组。