使用pandas循环数据帧的最有效方法是什么?

Mup*_*pet 306 python performance for-loop dataframe pandas

我希望以顺序方式对数据框中的财务数据执行我自己的复杂操作.

例如,我使用从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

....
Run Code Online (Sandbox Code Playgroud)

然后我做以下事情:

#!/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..
Run Code Online (Sandbox Code Playgroud)

这是最有效的方式吗?鉴于对熊猫速度的关注,我认为必须有一些特殊的函数来迭代遍历值,同时也检索索引(可能通过生成器来节省内存)?df.iteritems遗憾的是,只能逐列迭代.

Nic*_*ord 353

最新版本的pandas现在包含一个用于迭代行的内置函数.

for index, row in df.iterrows():

    # do some logic here
Run Code Online (Sandbox Code Playgroud)

或者,如果你想更快地使用它 itertuples()

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

  • 请注意,`iterrows`非常慢(它将每一行转换为一系列,可能会弄乱您的数据类型).当你需要一个迭代器时,最好使用`itertuples` (48认同)
  • BTW itertuples返回命名元组(https://docs.python.org/3/library/collections.html#collections.namedtuple),因此您可以使用row.high或getattr(行,'高')按名称访问每列 (11认同)
  • 请注意,根据当前[docs](http://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.iterrows.html):"你应该**永远不要修改你正在迭代的东西不能保证在所有情况下都能正常工作.根据数据类型,迭代器返回一个副本而不是一个视图,写入它将不起作用." (7认同)
  • @乔里斯。我完全同意你的观点,`itertuples` 大约比 `iterrows` 大 100 倍。 (5认同)
  • itertuples(name=None) 甚至更快,因为它将生成普通元组而不是命名元组。请参阅这篇有趣的文章:https://medium.com/swlh/why-pandas-itertuples-is-faster-than-iterrows-and-how-to-make-it-even-faster-bc50c0edd30d (2认同)

unu*_*tbu 152

Pandas基于NumPy阵列.使用NumPy阵列加速的关键是一次性对整个阵列执行操作,而不是逐行或逐项执行.

例如,如果close是1-d数组,并且您希望每日百分比更改,

pct_change = close[1:]/close[:-1]
Run Code Online (Sandbox Code Playgroud)

这将整个百分比变化数组计算为一个语句,而不是

pct_change = []
for row in close:
    pct_change.append(...)
Run Code Online (Sandbox Code Playgroud)

因此,尝试for i, row in enumerate(...)完全避免Python循环,并考虑如何使用整个数组(或数据帧)作为整体的操作来执行计算,而不是逐行.

  • 我同意这是最好的方式,这也是我通常为简单操作所做的事情.但是,在这种情况下,这是不可能的,因为结果操作可能变得非常复杂.具体来说,我正在尝试回溯交易策略.例如,如果价格在30天内处于新的低点,那么我们可能想要买入股票并在满足某个条件时离开,这需要就地模拟.这个简单的例子仍然可以通过矢量化来完成,但是,交易策略越复杂,使用矢量化的可能性就越小. (36认同)
  • 我同意矢量化是可能的正确解决方案 - 有时候迭代算法是唯一的方法. (31认同)
  • 迟到的评论,但我发现尝试对列进行完整计算有时很难编写和调试.考虑中间计算列,使调试和理解计算变得更容易.已经发现即使是最复杂的逻辑也可以像这样实现,同时仍然避免循环. (6认同)
  • 顺便说一下,对于某些计算(特别是那些不能表示为整个数组上的操作的计算),使用Python列表的代码比使用numpy数组的等效代码更快. (5认同)
  • 您需要更详细地解释您尝试执行的确切计算.它可以帮助您以任何方式编写代码,然后对其进行分析和优化. (2认同)

Ric*_*ong 85

与前面提到的一样,pandas对象在一次处理整个数组时效率最高.然而对于那些真正需要循环通过pandas DataFrame来执行某些事情的人,比如我,我发现至少有三种方法可以做到这一点.我做了一个简短的测试,看看三者中哪一个最耗时.

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
Run Code Online (Sandbox Code Playgroud)

结果:

[0.5639059543609619, 0.017839908599853516, 0.005645036697387695]
Run Code Online (Sandbox Code Playgroud)

这可能不是衡量时间消耗的最佳方法,但它对我来说很快.

以下是一些利弊恕我直言:

  • .iterrows():在单独的变量中返回索引和行项,但速度明显较慢
  • .itertuples():比.iterrows()快,但返回索引和行项,ir [0]是索引
  • zip:最快,但无法访问该行的索引

  • 你不能用`t.index` 来循环索引吗? (4认同)
  • 注意 Python 3 `zip()` 返回一个迭代器,所以使用 `list(zip())` (3认同)
  • 这很棒; 谢谢理查德。它仍然与 Python 3.7+ 相关。从 iterrows 的 286 秒到 zip 的 3.62 秒。谢谢 (2认同)

Wes*_*ney 73

您可以通过转置然后调用iteritems来遍历行:

for date, row in df.T.iteritems():
   # do some logic here
Run Code Online (Sandbox Code Playgroud)

在这种情况下,我不确定效率.为了在迭代算法中获得最佳性能,您可能希望探索在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
Run Code Online (Sandbox Code Playgroud)

我建议首先在纯Python中编写算法,确保它的工作原理并查看速度有多快 - 如果速度不够快,将事物转换为Cython就像这样,只需要很少的工作就可以得到与手工编码C一样快的东西/ C++.

  • 我也推荐Cython; 我正在研究构建我的回测引擎的类似问题,而且我获得了1,000倍的加速.然后我将它与多处理库结合起来,这是一个非常好的组合. (9认同)
  • 根据@ NickCrawford的回答,这个答案需要更新以包含新的`df.iterrows()`. (6认同)

Fif*_*ifi 31

你有三个选择:

索引(最简单):

>>> for index in df.index:
...     print ("df[" + str(index) + "]['B']=" + str(df['B'][index]))
Run Code Online (Sandbox Code Playgroud)

使用iterrows(最常用):

>>> for index, row in df.iterrows():
...     print ("df[" + str(index) + "]['B']=" + str(row['B']))
Run Code Online (Sandbox Code Playgroud)

使用itertuples(最快):

>>> for row in df.itertuples():
...     print ("df[" + str(row.Index) + "]['B']=" + str(row.B))
Run Code Online (Sandbox Code Playgroud)

三个选项显示如下:

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
Run Code Online (Sandbox Code Playgroud)

资料来源:neural-networks.io


bea*_*rdc 25

iterrows在注意到尼克克劳福德的答案之后检查了一下,但发现它产生(索引,系列)元组.不确定哪个最适合你,但我最终使用该itertuples方法解决了我的问题,产生了(index,row_value1 ...)元组.

还有iterkv,它遍历(列,系列)元组.

  • 我还发现迭代在我的用例中要快得多(10x),因为没有创建Series对象. (4认同)

Car*_*rst 20

只是作为一个小小的补充,如果您具有应用于单个列的复杂函数,也可以执行应用:

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

df[b] = df[a].apply(lambda col: do stuff with col here)
Run Code Online (Sandbox Code Playgroud)

  • 只是要添加,`apply`也可以应用于多个列:`df ['c'] = df [['a','b']].apply(lambda x:用x [0]和x做东西[1]这里,轴= 1)` (6认同)

Goi*_*Way 10

正如@joris所指出的那样,iterrows比它慢得多itertuples,itertuples并且大约是100倍iterrows,并且我在DataFrame中测试了两种方法的速度,结果是5027505记录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
Run Code Online (Sandbox Code Playgroud)


Vla*_*lad 7

可以肯定的是,迭代数据帧的最快方法是通过df.values(如您所做的那样)或分别访问每个列来访问基础的numpy ndarray df.column_name.values。由于您也想访问索引,因此可以使用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]
Run Code Online (Sandbox Code Playgroud)

不是pythonic吗?当然。但是很快。

如果您想从循环中挤出更多汁,则需要使用cython。Cython将使您获得巨大的加速(想想10倍至100倍)。为了获得最佳性能,请检查cython的内存视图


Joe*_*ron 5

另一个建议是,如果行的子集共享特征,则可以将groupby与矢量化计算结合使用。