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
不幸的是,只能逐列进行迭代。#1 楼
熊猫的最新版本现在包括用于遍历行的内置函数。for index, row in df.iterrows():
# do some logic here
或者,如果您希望更快地使用
itertuples()
但是,unutbu建议使用numpy函数以避免对行进行迭代将产生最快的代码。
评论
请注意,迭代非常慢(它将每一行转换为一系列,可能会干扰您的数据类型)。当您需要迭代器时,最好使用itertuples
– joris
15年7月29日在15:46
BTW itertuples返回命名元组(docs.python.org/3/library/…),因此您可以使用row.high或getattr(row,'high')按名称访问每一列
–seanv507
16年4月17日在18:51
请注意,根据当前文档,“您永远不要修改要迭代的内容。不能保证在所有情况下都能正常工作。根据数据类型,迭代器将返回副本而不是视图,并且对其进行写入没有效果。”
–viddik13
16 Dec 7'在18:50
@joris。我完全同意您的观点,迭代比迭代快约100倍。
– GoingMyWay
17年7月7日在9:24
fjsj:既然问题是关于“循环”和“效率”的,我提供了一个回答,回答了问题的两个部分。除非绝对必要,否则任何人都不应在数据帧上循环。
–尼克·克劳福德(Nick Crawford)
19年1月9日在1:08
#2 楼
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(...)
,并且考虑如何在整个操作中执行计算整个数组(或数据框),而不是逐行。
评论
我同意这是最好的方法,这就是我通常对简单操作所做的事情。但是,在这种情况下,这是不可能的,因为生成的操作会变得非常复杂。具体来说,我正在尝试回溯交易策略。例如。如果价格在30天内达到新的低点,则我们可能想购买股票,并在满足特定条件时退出,这需要就地模拟。这个简单的例子仍然可以通过向量化完成,但是,交易策略越复杂,使用向量化的可能性就越小。
–木偶
2011-10-20 15:16
您必须更详细地说明您要执行的确切计算。它可以帮助您首先以任何方式编写代码,然后对其进行概要分析和优化。
–unutbu
2011-10-20 15:19
顺便说一下,对于某些计算(尤其是那些不能表示为对整个数组的操作的计算),使用Python列表的代码可能比使用numpy数组的等效代码更快。
–unutbu
2011-10-20 15:35
我同意向量化是在可能的情况下的正确解决方案,尽管有时迭代算法是唯一的方法。
–韦斯·麦金尼
2011年10月21日在16:15
最近的评论,但我发现尝试对列进行完整计算有时很难编写和调试。考虑中间计算列,使调试和理解计算更加容易。我们发现即使最复杂的逻辑也可以像这样实现,同时仍然避免循环。
–乔普
2014年9月22日上午11:27
#3 楼
就像之前提到的那样,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
结果:消耗,但对我来说是快速的。
恕我直言,这里有一些优点和缺点: ():比.iterrows()快,但是返回索引和行项目,ir [0]是索引。
zip:最快,但无法访问该行的索引。
EDIT 2020/11/10
值得一提的是,这里提供了一些其他替代产品的更新基准(使用MacBookPro 2,4 GHz Intel Core i9 8核32 Go 2667 MHz DDR4的性能)
=“ lang-py prettyprint-override”>
import sys
import tqdm
import time
import pandas as pd
B = []
t = pd.DataFrame({'a': range(0, 10000), 'b': range(10000, 20000)})
for _ in tqdm.tqdm(range(10)):
C = []
A = time.time()
for i,r in t.iterrows():
C.append((r['a'], r['b']))
B.append({"method": "iterrows", "time": time.time()-A})
C = []
A = time.time()
for ir in t.itertuples():
C.append((ir[1], ir[2]))
B.append({"method": "itertuples", "time": time.time()-A})
C = []
A = time.time()
for r in zip(t['a'], t['b']):
C.append((r[0], r[1]))
B.append({"method": "zip", "time": time.time()-A})
C = []
A = time.time()
for r in zip(*t.to_dict("list").values()):
C.append((r[0], r[1]))
B.append({"method": "zip + to_dict('list')", "time": time.time()-A})
C = []
A = time.time()
for r in t.to_dict("records"):
C.append((r["a"], r["b"]))
B.append({"method": "to_dict('records')", "time": time.time()-A})
A = time.time()
t.agg(tuple, axis=1).tolist()
B.append({"method": "agg", "time": time.time()-A})
A = time.time()
t.apply(tuple, axis=1).tolist()
B.append({"method": "apply", "time": time.time()-A})
print(f'Python {sys.version} on {sys.platform}')
print(f"Pandas version {pd.__version__}")
print(
pd.DataFrame(B).groupby("method").agg(["mean", "std"]).xs("time", axis=1).sort_values("mean")
)
## Output
Python 3.7.9 (default, Oct 13 2020, 10:58:24)
[Clang 12.0.0 (clang-1200.0.32.2)] on darwin
Pandas version 1.1.4
mean std
method
zip + to_dict('list') 0.002353 0.000168
zip 0.003381 0.000250
itertuples 0.007659 0.000728
to_dict('records') 0.025838 0.001458
agg 0.066391 0.007044
apply 0.067753 0.006997
iterrows 0.647215 0.019600
评论
Python 3中的NB zip()返回一个迭代器,因此请使用list(zip())
–路易丝·马多克斯(Louis Maddox)
16-10-12在13:33
您不能使用t.index遍历索引吗?
–elPastor
16/12/22在2:54
这很棒;谢谢理查德。它仍然与Python 3.7+相关。从286秒(带拉链)到3.62(带拉链)。谢谢
–pacta_sunt_servanda
19年5月16日在12:48
我已经使用pandas重新运行了该基准测试。__version__ == 1.1.4,Python 3.7.9和全新的MacBookPro 2,4 GHz Intel Core i9 8核32 Go 2667 MHz DDR4,结果对于iterrows()甚至更糟:[0.6970570087432861,0.008062124252319336,0.0036787986755371094]
– ClementWalter
20 Nov 10在17:02
@ClementWalter,太好了!
–黄耀明(Richard Wong)
20 Nov 12'在6:43
#4 楼
您可以通过转置然后调用迭代项来遍历各行:为了在迭代算法中获得最佳性能,您可能想探索用Cython编写代码,因此可以执行以下操作:
for date, row in df.T.iteritems():
# do some logic here
我建议编写首先,请使用纯Python编写该算法,并确保它能够运行并观察其运行速度-如果运行速度不够快,只需花费最少的工作即可将其转换为Cython,从而获得与手工编码的C / C ++差不多的速度。
评论
我也推荐Cython。我在构建回测引擎时也遇到了类似的问题,并且提速了1000倍。然后,我将其与多处理库相结合,这是一个非常不错的组合。
–vgoklani
2012年10月7日在12:31
根据@NickCrawford的答案,此答案需要更新以包括新的df.iterrows()。
–LondonRob
2014年6月6日下午16:14
如果要迭代特定列+1,则df.T.iteritems()是一个很好的解决方案,而不是使用df.iterrows()
– ALH
15-10-25在10:37
给出错误:def my_algo(ndarray [object] dates,ndarray [float64_t] opn,^ SyntaxError:无效语法
–astro123
19年4月1日,下午3:54
#5 楼
您有以下三种选择:按索引(最简单):
>>> for index in df.index:
... print ("df[" + str(index) + "]['B']=" + str(df['B'][index]))
具有迭代次数(最常用):
>>> for index, row in df.iterrows():
... print ("df[" + str(index) + "]['B']=" + str(row['B']))
使用itupuples(最快):
>>> 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
来源:Neuro-networks.io
#6 楼
在注意到尼克·克劳福德的答案后,我检查了iterrows
,但发现它产生了(索引,系列)元组。不确定哪种方法最适合您,但是我最终对问题使用了itertuples
方法,该方法产生了(index,row_value1 ...)元组。 ,系列)元组。评论
您可以执行dict(row)之类的操作,以使行可以搜索到列
–卡斯特
13-10-16在22:36
我还发现在我的用例中itertuples快得多(10倍),因为没有创建Series对象。
–卡米尔·辛迪
2014年6月11日12:51
仅供参考:从0.13.1开始不推荐使用iterkv
– JS。
2015年9月9日23:06
iterrows():将DataFrame的行作为(索引,系列)对进行迭代...。itertuples():将DataFrame的行作为值的元组进行迭代。作为iterrows(),这要快得多,并且在大多数情况下,最好使用它来遍历DataFrame的值。
–红豌豆
2015年11月5日下午5:21
#7 楼
只是一个很小的补充,如果您具有应用于单个列的复杂功能,则也可以执行应用:generate / pandas.DataFrame.apply.html
df[b] = df[a].apply(lambda col: do stuff with col here)
评论
x可能是列名和行变量的一个令人困惑的名称,尽管我同意apply是最简单的方法:)
–安迪·海登(Andy Hayden)
13-10-17在6:09
只需添加,apply也可以应用于多个列:df ['c'] = df [['a','b']]。apply(lambda x:用x [0]和x [1]进行填充在这里,axis = 1)
–奇妙
14年8月16日在13:18
可以应用代码中其他地方定义的功能吗?这样我们就可以引入更复杂的功能
–user308827
2014年11月9日15:28
是的,lambda函数可以使用任何类型的用户定义函数。请注意:如果您有一个大型数据框,则可能需要恢复为cython(在调用函数时,Python会有一些开销)
–卡斯特
2014年11月18日15:53
我将其重命名为x-> col。更好的名字
–smci
2015年2月5日在4:16
#8 楼
正如@joris指出的那样,iterrows
比itertuples
慢得多,而itertuples
比iterrows
慢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
#9 楼
当然,迭代数据帧的最快方法是通过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]
不是pythonic吗?当然。但是很快。
如果您想从循环中挤出更多果汁,您将需要使用cython。 Cython将使您获得巨大的加速(想想10倍至100倍)。为了获得最佳性能,请检查cython的内存视图。
#10 楼
另一个建议是,如果行的子集共享特征,则可以将groupby与矢量化计算结合使用。#11 楼
我相信遍历DataFrames的最简单,最有效的方法是使用numpy和numba。在这种情况下,在许多情况下,循环的速度可以与向量化操作差不多。如果不是numba,则简单的numpy可能是次佳选择。正如很多次提到的那样,您的默认值应该是向量化,但是由于任何原因,只要给出决定循环,此答案仅考虑有效循环。对于测试用例,让我们使用@DSM的计算示例百分比变化。这是一种非常简单的情况,实际上,您不会编写循环来计算它,但是这样可以为时序矢量化方法与循环提供合理的基线。 ,我们将它们计时在下面的更大数据集上。
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代码一样快。
其他详细信息:
未显示各种选项,例如iterrows,itetuples等,它们的数量级要慢一些,
这里的时间非常典型:numpy比pandas快,vectorized比循环快,但是将numba添加到numpy通常会大大加快numpy的速度。
除pandas选项之外的所有内容需要将DataFrame列转换为numpy数组。该转换包含在计时中。
定时中不包括定义/编译numpy / numba函数的时间,但是对于任何大型数据帧,时间通常可以忽略不计。
评论
您是否尝试编写函数并将其传递给df.apply()?引用unutbu,NumPy似乎支持向量化操作(加快NumPy数组速度的关键是立即对整个数组执行操作)。
这里的许多答案都是危险的,因为它们很乐意建议使用迭代方法,这些方法缓慢,笨拙,并且消耗的内存比他们应有的多。在可能的情况下,应该对操作进行向量化。此答案更详细地研究了向量化和性能注意事项。
这个问题是顺序迭代所特有的,这在金融中非常普遍,因为矢量化通常是不可能的。尼克·克劳福德(Nick Crawford)接受的答案不仅回答了这一问题,而且还警告在可能的情况下使用向量化。
@ cs95仅供参考,我刚刚重新打开(您已标记为重复)b / c我觉得这与另一个问题相似,但不完全相同,因为它具有特定的效率方面(效率不是唯一的考虑因素) ,也是因为我想添加答案!我个人认为应该链接但不要关闭它,但是由于我现在已经添加了一个答案,因此如果您强烈希望关闭它,我也不会反对。 (我对此和其他答案都同意,有很多过时的答案不再有用。)