What is the most efficient way to loop through dataframes with pandas?
我想对数据帧中的财务数据按顺序执行自己的复杂操作。
例如,我使用的是从雅虎财经(Yahoo Finance)获取的以下msft csv文件:
1 2 3 4 5 6 7 | 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 .... |
然后我执行以下操作:
1 2 3 4 5 6 7 8 9 | #!/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.. |
这是最有效的方法吗?考虑到熊猫对速度的关注,我假设必须有一些特殊的函数来以一种同样检索索引的方式迭代这些值(可能是通过生成器来提高内存效率)?不幸的是,
Panda的最新版本现在包括一个内置函数,用于遍历行。
1 2 3 | for index, row in df.iterrows(): # do some logic here |
或者,如果你想更快地使用
但是,unutbu建议使用numpy函数来避免遍历行,这将产生最快的代码。
熊猫是基于numpy数组的。使用numpy数组加速的关键是一次对整个数组执行操作,而不是逐行或逐项执行。
例如,如果
1 | pct_change = close[1:]/close[:-1] |
它将整个百分比变化数组计算为一条语句,而不是
1 2 3 | pct_change = [] for row in close: pct_change.append(...) |
因此,尝试完全避免python循环
和前面提到的一样,pandas对象在一次处理整个数组时效率最高。然而,对于那些真正需要循环访问熊猫数据框架来执行类似我这样的操作的人,我找到了至少三种方法来执行。我做了一个简短的测试,看看三个测试中哪一个最省时。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | 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 |
结果:
1 | [0.5639059543609619, 0.017839908599853516, 0.005645036697387695] |
这可能不是衡量时间消耗的最佳方法,但对我来说很快。
以下是一些优点和缺点:
- .iterrows():在单独的变量中返回索引和行项,但速度明显较慢
- .ITertuples():比.ITerRows()快,但返回索引和行项目,ir[0]是索引
- zip:最快,但无法访问行的索引
您可以通过转置并调用ITeritems来循环这些行:
1 2 | for date, row in df.T.iteritems(): # do some logic here |
在这种情况下,我不确定效率。为了在迭代算法中获得可能的最佳性能,您可能希望探索用Cython编写它,这样您就可以执行如下操作:
1 2 3 4 5 6 7 8 9 10 | 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中编写算法,确保它工作,看看它有多快——如果它不够快,用最少的工作将事情转换成Cython,以获得与手工编码C/C++一样快的东西。
我注意到尼克·克劳福德的回答后,查看了
还有
您有三种选择:
按索引(最简单):
1 2 | >>> for index in df.index: ... print ("df[" + str(index) +"]['B']=" + str(df['B'][index])) |
使用iTerrows(最常用):
1 2 | >>> for index, row in df.iterrows(): ... print ("df[" + str(index) +"]['B']=" + str(row['B'])) |
使用ITertuples(最快):
1 2 | >>> for row in df.itertuples(): ... print ("df[" + str(row.Index) +"]['B']=" + str(row.B)) |
三个选项显示如下:
1 2 3 4 5 6 7 | 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 |
资料来源:神经网络.io
正如一个小的添加,如果您有一个复杂的函数要应用于单个列,那么您也可以执行应用:
http://pandas.pydata.org/pandas-docs/dev/generated/pandas.dataframe.apply.html
1 | df[b] = df[a].apply(lambda col: do stuff with col here) |
正如@joris所指出的,
如果使用
1 2 3 4 5 6 7 8 9 10 11 | >>> 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 |
当然,在数据帧上迭代的最快方法是通过
1 2 3 4 5 6 7 8 9 | 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] |
不是Python?当然。但是很快。
如果你想挤出更多的果汁,你会想看看赛通。赛通将让你获得巨大的加速(想想10倍-100倍)。要获得最佳性能,请检查Cython的内存视图。
另一个建议是,如果行的子集共享特性(这允许您这样做),则将groupby与矢量化计算结合起来。