pandas df.loc[z,x]=y 如何提高速度?
Posted
技术标签:
【中文标题】pandas df.loc[z,x]=y 如何提高速度?【英文标题】:pandas df.loc[z,x]=y how to improve speed? 【发布时间】:2016-10-11 23:43:32 【问题描述】:我已经确定了一个 pandas 命令
timeseries.loc[z, x] = y
负责迭代中花费的大部分时间。现在我正在寻找更好的方法来加速它。循环甚至不包括 50k 个元素(生产目标是 ~250k 或更多),但已经需要一个悲伤的 20 秒。
这是我的代码(忽略上半部分,它只是计时助手)
def populateTimeseriesTable(df, observable, timeseries):
"""
Go through all rows of df and
put the observable into the timeseries
at correct row (symbol), column (tsMean).
"""
print "len(df.index)=", len(df.index) # show number of rows
global bf, t
bf = time.time() # set 'before' to now
t = dict([(i,0) for i in range(5)]) # fill category timing with zeros
def T(i):
"""
timing helper: Add passed time to category 'i'. Then set 'before' to now.
"""
global bf, t
t[i] = t[i] + (time.time()-bf)
bf = time.time()
for i in df.index: # this is the slow loop
bf = time.time()
sym = df["symbol"][i]
T(0)
tsMean = df["tsMean"][i]
T(1)
tsMean = tsFormatter(tsMean)
T(2)
o = df[observable][i]
T(3)
timeseries.loc[sym, tsMean] = o
T(4)
from pprint import pprint
print "times needed (total = %.1f seconds) for each command:" % sum(t.values())
pprint (t)
return timeseries
有(不重要,不慢)
def tsFormatter(ts):
"as human readable string, only up to whole seconds"
return time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime(ts))
。 .
--> 待优化代码在for循环中。
(T, 和 t 只是辅助函数和字典,用于计时。)
我已经为每一步计时。绝大多数时间:
len(df.index)= 47160
times needed (total = 20.2 seconds) for each command:
0: 1.102,
1: 0.741,
2: 0.243,
3: 0.792,
4: 17.371
花费在最后一步
timeseries.loc[sym, tsMean] = o
我已经下载并安装了 pypy - 但遗憾的是,它还不支持 pandas。
任何想法如何加快填充二维数组?
谢谢!
编辑:抱歉,没有提到 - 'timeseries' 也是一个数据框:
timeseries = pd.DataFrame("name": titles, index=index)
【问题讨论】:
不知道timeseries
是什么对象。但是,如果它有一个“.loc”方法,它可能有一个.at
方法。如果您在特定位置分配,.at
应该更快。
编辑:抱歉,没有提到:timeseries 是一个数据框:timeseries = pd.DataFrame("name":titles, index=index) 我现在已将其添加到 OP。
我将研究那个 .at 函数。非常感谢,@piRSquared
【参考方案1】:
更新:从 Pandas 0.20.1 the .ix indexer is deprecated, in favor of the more strict .iloc and .loc indexers开始。
================================================ =======================
@jezrael 提供了一个有趣的比较,我决定使用更多的索引方法和 10M 行 DF 重复它(实际上在这种特殊情况下大小无关紧要):
设置:
In [15]: df = pd.DataFrame(np.random.rand(10**7, 5), columns=list('abcde'))
In [16]: df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 10000000 entries, 0 to 9999999
Data columns (total 5 columns):
a float64
b float64
c float64
d float64
e float64
dtypes: float64(5)
memory usage: 381.5 MB
In [17]: df.shape
Out[17]: (10000000, 5)
时间:
In [37]: %timeit df.loc[random.randint(0, 10**7), 'b']
1000 loops, best of 3: 502 µs per loop
In [38]: %timeit df.iloc[random.randint(0, 10**7), 1]
1000 loops, best of 3: 394 µs per loop
In [39]: %timeit df.at[random.randint(0, 10**7), 'b']
10000 loops, best of 3: 66.8 µs per loop
In [41]: %timeit df.iat[random.randint(0, 10**7), 1]
10000 loops, best of 3: 32.9 µs per loop
In [42]: %timeit df.ix[random.randint(0, 10**7), 'b']
10000 loops, best of 3: 64.8 µs per loop
In [43]: %timeit df.ix[random.randint(0, 10**7), 1]
1000 loops, best of 3: 503 µs per loop
结果为条形图:
时序数据作为 DF:
In [88]: r
Out[88]:
method timing
0 loc 502.0
1 iloc 394.0
2 at 66.8
3 iat 32.9
4 ix_label 64.8
5 ix_integer 503.0
In [89]: r.to_dict()
Out[89]:
'method': 0: 'loc',
1: 'iloc',
2: 'at',
3: 'iat',
4: 'ix_label',
5: 'ix_integer',
'timing': 0: 502.0,
1: 394.0,
2: 66.799999999999997,
3: 32.899999999999999,
4: 64.799999999999997,
5: 503.0
绘图
ax = sns.barplot(data=r, x='method', y='timing')
ax.tick_params(labelsize=16)
[ax.annotate(str(round(p.get_height(),2)), (p.get_x() + 0.2, p.get_height() + 5)) for p in ax.patches]
ax.set_xlabel('indexing method', size=20)
ax.set_ylabel('timing (microseconds)', size=20)
【讨论】:
哇哇哇。太感谢了。这真的很有启发性。所以我对这种缓慢的怀疑确实是有道理的。我选择了可能是最舒适的解决方案,使用 .loc - 但与 .at 和 .iat 相比,我以 7.5 到 15.3 倍的时间复杂度购买它 - 非常感谢,这是非常有价值的信息! 完成。谢谢!非常感谢。 不幸的是,我不能同时接受这两个答案。嗯......你的直方图输出非常容易阅读。但是@jezrael 是第一个回答的,他的回答也很好。现在该怎么办?【参考方案2】:我一直认为at
是最快的,但不是。 ix
更快:
import pandas as pd
df = pd.DataFrame('A':[1,2,3],
'B':[4,5,6],
'C':[7,8,9],
'D':[1,3,5],
'E':[5,3,6],
'F':[7,4,3])
print (df)
A B C D E F
0 1 4 7 1 5 7
1 2 5 8 3 3 4
2 3 6 9 5 6 3
print (df.at[2, 'B'])
6
print (df.ix[2, 'B'])
6
print (df.loc[2, 'B'])
6
In [77]: %timeit df.at[2, 'B']
10000 loops, best of 3: 44.6 µs per loop
In [78]: %timeit df.ix[2, 'B']
10000 loops, best of 3: 40.7 µs per loop
In [79]: %timeit df.loc[2, 'B']
1000 loops, best of 3: 681 µs per loop
编辑:
我尝试MaxU
df
和random.randint
函数引起的差异:
df = pd.DataFrame(np.random.rand(10**7, 5), columns=list('ABCDE'))
In [4]: %timeit (df.ix[2, 'B'])
The slowest run took 25.80 times longer than the fastest. This could mean that an intermediate result is being cached.
10000 loops, best of 3: 20.7 µs per loop
In [5]: %timeit (df.ix[random.randint(0, 10**7), 'B'])
The slowest run took 9.42 times longer than the fastest. This could mean that an intermediate result is being cached.
10000 loops, best of 3: 28 µs per loop
【讨论】:
非常有趣。非常感谢!现在我想知道如何解释你的结果 jezrael 和@MaxU 在那里报告的结果之间的差异:***.com/a/37761363/6185262 是不是他的df是10M行,而你的行数很小?所以也许 .ix 对于小型数据帧是最快的,而 .at / .iat 对于大型数据帧是最快的? 我认为时间最适合比较方法,并且在每台 PC 中都略有不同。而且这些时间不取决于数据帧的长度。iat
是最快的,但您需要列的位置 - 如果不知道位置并且只知道列名,则不能使用 iat - 使用 ix 是最快的方法。
最好在docs解释。
因为你的两个答案都一样好......我已经扔了一千个硬币: sum([random.random() for _ in range(1000)])/1000 导致 0.4972785257694664 -所以 MaxU 赢了,随机选择 :-) 祝你有美好的一天!【参考方案3】:
如果您在循环中添加行,请考虑这些性能问题;对于大约前 1000 到 2000 条记录,“my_df.loc”性能更好,并且随着循环中记录数量的增加逐渐变慢。
如果你打算在一个大循环中进行精简(比如 10M 记录左右),你最好混合使用“iloc”和“append”;用 iloc 填充一个临时数据帧,直到大小达到 1000 左右,然后将其附加到原始数据帧,并清空临时数据帧。这将使您的表现提高大约 10 倍
【讨论】:
以上是关于pandas df.loc[z,x]=y 如何提高速度?的主要内容,如果未能解决你的问题,请参考以下文章
python:pandas之DataFrame取行列(df.loc(),df.iloc())以及索引