什么是通过pandas循环数据框的最有效的方法?
我想以顺序的方式对数据框中的财务数据执行我自己的复杂操作。
例如,我正在使用以下来自雅虎财经的 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
不幸的是只能逐列迭代。
pandas的最新版本现在包括一个内置函数来遍历行。
for index, row in df.iterrows(): # do some logic here
或者,如果你想更快地使用itertuples()
但是,unutbubuild议使用numpy函数来避免遍历行会产生最快的代码。
pandas是基于NumPy数组。 使用NumPy数组加速的关键是一次执行整个数组的操作,而不是逐行或逐项执行。
例如,如果close
是一个一维数组,并且您想要每天更改百分比,
pct_change = close[1:]/close[:-1]
这将计算整个百分比变化数组作为一个语句,而不是
pct_change = [] for row in close: pct_change.append(...)
因此,尽量避免Python的循环for i, row in enumerate(...)
完全for i, row in enumerate(...)
,并考虑如何对整个数组(或数据框)进行整体计算,而不是逐行计算。
您可以通过转置循环遍历行,然后调用iteritems:
for date, row in df.T.iteritems(): # do some logic here
在这种情况下,我不确定效率。 为了在迭代algorithm中获得最好的性能,您可能需要探索在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
我build议首先用纯Python编写algorithm,确保它能正常工作,看看它有多快 – 如果速度不够快,就用最less的工作将其转换成Cython,以便得到与手写代码C一样快的东西/ C ++。
就像之前提到的那样,当一次处理整个数组时,pandas对象是最有效的。 但是对于那些真正需要通过pandasDataFrame来执行某些操作的人来说,我发现至less有三种方法可以实现。 我做了一个简短的testing,看看三者中哪一个是最不耗时间的。
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():在单独的variables中返回索引和行项目,但速度明显较慢
- .itertuples():比.iterrows()更快,但返回索引和行项目,ir [0]是索引
- zip:最快,但不能访问该行的索引
在注意到尼克·克劳福德的答案后,我检查了iterrows
,但发现它产生了(索引,系列)元组。 不知道哪个最适合你,但我最终使用itertuples
方法为我的问题,这产生(索引,row_value1 …)元组。
还有iterkv
,它遍历(列,系列)元组。
就像一个小的添加,如果你有一个复杂的function,你可以申请一个单一的列:
http://pandas.pydata.org/pandas-docs/dev/generated/pandas.DataFrame.apply.html
df[b] = df[a].apply(lambda col: do stuff with col here)
另一个build议是将groupby和vector化计算结合起来,如果行的子集共享允许你这样做的特性的话。
正如@joris指出的那样, iterrows
比iterrows
慢了iterrows
, itertuples
大约比iterrows要iterrows
100倍,我testing了DataFrame中两个方法的速度,其中5027505logging结果是iterrows
,它是1200it / s, itertuples
是120000it /秒。
如果使用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