首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >提高大熊猫群的性能

提高大熊猫群的性能
EN

Stack Overflow用户
提问于 2017-11-20 13:05:26
回答 1查看 18.7K关注 0票数 37

我有一个用Python编写的机器学习应用程序,其中包括一个数据处理步骤。当我编写它时,我最初在Pandas DataFrames上进行了数据处理,但是当这导致了糟糕的性能时,我最终用普通的Python重写了它,使用的是for循环,而不是矢量化的操作,列表和切分,而不是DataFrames和Series。令我惊讶的是,用vanilla编写的代码的性能最终远远高于使用Pandas编写的代码。

由于我的手工编码的数据处理代码比原来的Pandas代码要大得多,而且更加混乱,所以我还没有完全放弃使用Pandas,而且我目前正在尝试优化Pandas代码,但没有成功。

数据处理步骤的核心包括以下内容:我首先将行划分为几个组,因为数据由数千个时间序列(每个“个体”一个)组成,然后对每个组进行相同的数据处理:大量汇总,将不同的列组合成新的列,等等。

我使用朱庇特笔记本的lprun分析了我的代码,大部分时间都花在了下面和其他类似的行上:

代码语言:javascript
复制
grouped_data = data.groupby('pk')
data[[v + 'Diff' for v in val_cols]] = grouped_data[val_cols].transform(lambda x: x - x.shift(1)).fillna(0)
data[[v + 'Mean' for v in val_cols]] = grouped_data[val_cols].rolling(4).mean().shift(1).reset_index()[val_cols]
(...)

...a混合矢量化和非矢量化处理.我知道,非矢量化操作不会比我手写的循环更快,因为这基本上就是它们在引擎盖下的情况,但是它们怎么会慢得多呢?我们讨论的是我的手写代码和Pandas代码之间的性能下降了10-20倍。

我是不是做错了什么事?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2017-11-25 02:50:51

不,我认为你不应该放弃熊猫。肯定有更好的方法来做你想做的事。诀窍是尽可能避免任何形式的apply/transform。像躲避瘟疫一样避开它们。它们基本上是作为循环实现的,所以您最好直接使用python for循环,这些循环以C的速度工作,并给您更好的性能。

真正的速度增长是在哪里你摆脱了循环,并使用熊猫的函数,隐含地向量化他们的操作。例如,您的第一行代码可以大大简化,我很快就会向您展示。

在这篇文章中,我概述了设置过程,然后,针对您问题中的每一行,提供一个改进,以及时间和正确性的并行比较。

设置

代码语言:javascript
复制
data = {'pk' : np.random.choice(10, 1000)} 
data.update({'Val{}'.format(i) : np.random.randn(1000) for i in range(100)})

df = pd.DataFrame(data)
代码语言:javascript
复制
g = df.groupby('pk')
c = ['Val{}'.format(i) for i in range(100)]

transform + sub + shiftdiff

您的第一行代码可以替换为一个简单的diff语句:

代码语言:javascript
复制
v1 = df.groupby('pk')[c].diff().fillna(0)

健全检查

代码语言:javascript
复制
v2 = df.groupby('pk')[c].transform(lambda x: x - x.shift(1)).fillna(0)

np.allclose(v1, v2)
True

性能

代码语言:javascript
复制
%timeit df.groupby('pk')[c].transform(lambda x: x - x.shift(1)).fillna(0)
10 loops, best of 3: 44.3 ms per loop

%timeit df.groupby('pk')[c].diff(-1).fillna(0)
100 loops, best of 3: 9.63 ms per loop

消除冗余索引操作

就您的第二行代码而言,我不认为有太多的改进余地,不过如果您的groupby语句没有将reset_index() + [val_cols]作为索引,则可以消除pk +[val_cols]调用:

代码语言:javascript
复制
g = df.groupby('pk', as_index=False)

然后,将第二行代码简化为:

代码语言:javascript
复制
v3 = g[c].rolling(4).mean().shift(1)

健全检查

代码语言:javascript
复制
g2 = df.groupby('pk')
v4 = g2[c].rolling(4).mean().shift(1).reset_index()[c]

np.allclose(v3.fillna(0), v4.fillna(0))
True

性能

代码语言:javascript
复制
%timeit df.groupby('pk')[c].rolling(4).mean().shift(1).reset_index()[c]
10 loops, best of 3: 46.5 ms per loop

%timeit df.groupby('pk', as_index=False)[c].rolling(4).mean().shift(1)
10 loops, best of 3: 41.7 ms per loop

注意,不同的机器上的时间不同,所以请确保对代码进行了彻底的测试,以确保您的数据确实有改进。

虽然这一次的差别并不大,但你可以欣赏到这样一个事实:你可以做出一些改进!这可能会对更大的数据产生更大的影响。

后来语

总之,大多数操作都是缓慢的,因为它们可以加速。关键是摆脱任何不使用矢量化的方法。

为了达到这一目的,走出熊猫的空间,踏进矮胖的空间,有时是有益的。对于numpy数组或使用numpy的操作往往比熊猫等价物快得多(例如,np.sumpd.DataFrame.sum快,np.wherepd.DataFrame.where快等等)。

有时,循环是无法避免的。在这种情况下,您可以创建一个基本的循环函数,然后可以使用numba或cython对其进行矢量化。这方面的例子在这里的提高绩效,直接从马的嘴。

在其他情况下,您的数据太大,无法合理地适合于numpy数组。在这种情况下,是时候放弃使用daskspark了,它们都为处理大数据提供了高性能的分布式计算框架。

票数 46
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/47392758

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档