5000 万行的 Pandas groupby+transform 需要 3 小时
Posted
技术标签:
【中文标题】5000 万行的 Pandas groupby+transform 需要 3 小时【英文标题】:Pandas groupby+transform on 50 million rows is taking 3 hours 【发布时间】:2015-10-31 15:48:54 【问题描述】:我正在使用熊猫模块。在我的 DataFrame 3 中,字段是帐户、月份和薪水。
account month Salary
1 201501 10000
2 201506 20000
2 201506 20000
3 201508 30000
3 201508 30000
3 201506 10000
3 201506 10000
3 201506 10000
3 201506 10000
我正在对 Account 和 Month 进行 groupby,并将薪水转换为所属组薪水的百分比。
MyDataFrame['salary'] = MyDataFrame.groupby(['account'], ['month'])['salary'].transform(lambda x: x/x.sum())
现在 MyDataFrame 变成如下表
account month Salary
1 201501 1
2 201506 .5
2 201506 .5
3 201508 .5
3 201508 .5
3 201506 .25
3 201506 .25
3 201506 .25
3 201506 .25
问题是:5000 万行这样的操作需要 3 个小时。 我单独执行了 groupyby,它很快只需要 5 秒。我认为这里的转换需要很长时间。 有什么方法可以提高性能?
更新: 为了提供更清晰的添加示例 一些账户持有人在 6 月收到了 2000 的工资,在 7 月收到了 8000 的工资,因此他的比例变为 6 月的 0.2 和 7 月的 0.8。我的目的是计算这个比例。
【问题讨论】:
回答 = 请求。所以意思是,创建数据集回答每个条目以获得最佳性能。否则浪费资源。不要迭代你的集合。为什么 google bot 会访问所有网站? @SDilmac 忘记在 groupby 之后添加列名,现在更新。根据您的评论,不确定这是否浪费资源。当你说不要迭代你的集合时,另一种方式是什么?这就是我需要的。当一切都在内存中时,为什么我需要一个一个地迭代。但我不知道其他方式是什么 当你说不要迭代你的收藏=收藏的条目百分比(1/50m)所以当检查总状态时你会扫描所有收藏。朋友,您将准备好接受所有请求。创建集合状态的集合。同时写入源和状态(重组、分析)集合。 @SDilmac 什么都不懂,你能举个例子/链接你想建议什么 @Vipin:你为什么要做lambda x: x.sum()
而不是简单的sum
或'sum'
? Python 函数和字符串都被截获并传递给函数的快速版本。
【参考方案1】:
我会使用不同的方法 第一排序,
MyDataFrame.sort(['account','month'],inplace=True)
然后迭代求和
(account,month)=('','') #some invalid values
salary=0.0
res=[]
for index, row in MyDataFrame.iterrows():
if (row['account'],row['month'])==(account,month):
salary+=row['salary']
else:
res.append([account,month,salary])
salary=0.0
(account,month)=(row['account'],row['month'])
df=pd.DataFrame(res,columns=['account','month','salary'])
这样,pandas 就不需要在内存中保存分组数据了。
【讨论】:
为什么你认为它会更快?transform
函数将整个组传递给处理函数的参数,这意味着 pandas 需要将这些数据存储在某个地方,此方法(受 map-reduce 模式的启发)不会存储整个分组数据,仅聚合(res
)
顺便说一下,我认为对于小的DataFrame
s,df.groupby().sum()
会更快【参考方案2】:
嗯,你需要更加明确,准确地展示你在做什么。这是 pandas 擅长的。
@Uri Goren 的注意事项。这是一个恒定的内存过程,一次只有 1 个组在内存中。这将与组数成线性关系。也不需要排序。
In [20]: np.random.seed(1234)
In [21]: ngroups = 1000
In [22]: nrows = 50000000
In [23]: dates = pd.date_range('20000101',freq='MS',periods=ngroups)
In [24]: df = DataFrame('account' : np.random.randint(0,ngroups,size=nrows),
'date' : dates.take(np.random.randint(0,ngroups,size=nrows)),
'values' : np.random.randn(nrows) )
In [25]:
In [25]: df.info()
<class 'pandas.core.frame.DataFrame'>
Int64Index: 50000000 entries, 0 to 49999999
Data columns (total 3 columns):
account int64
date datetime64[ns]
values float64
dtypes: datetime64[ns](1), float64(1), int64(1)
memory usage: 1.5 GB
In [26]: df.head()
Out[26]:
account date values
0 815 2048-02-01 -0.412587
1 723 2023-01-01 -0.098131
2 294 2020-11-01 -2.899752
3 53 2058-02-01 -0.469925
4 204 2080-11-01 1.389950
In [27]: %timeit df.groupby(['account','date']).sum()
1 loops, best of 3: 8.08 s per loop
如果你想转换输出,那么就这样做
In [37]: g = df.groupby(['account','date'])['values']
In [38]: result = 100*df['values']/g.transform('sum')
In [41]: result.head()
Out[41]:
0 4.688957
1 -2.340621
2 -80.042089
3 -13.813078
4 -70.857014
dtype: float64
In [43]: len(result)
Out[43]: 50000000
In [42]: %timeit 100*df['values']/g.transform('sum')
1 loops, best of 3: 30.9 s per loop
花一点时间。但同样,这应该是一个相对较快的操作。
【讨论】:
我想计算所有账户持有人每月收到的工资比例。例如,一位账户持有人在 6 月收到 2000 美元的工资,在 7 月收到 8000 美元,因此他的比例变为 6 月的 0.2 和 7 月的 0.8。我的目的是计算这个比例。 刚接触 pandas 和 python,可能我没有在这里使用最佳实践。据我了解,在这种情况下使用转换我正在为 50m 个元素运行循环。看时间我当然知道这是不正确的方式。 我认为这是导致问题的转换。在求和过程中,内存使用量翻了一番,之后又恢复到只有适度的增长。在转换过程中,对我来说,它翻了两番(所以增加了 3 倍)并花了 2 分钟。如果 OP 深入交换,我可以相信事情会一蹶不振。 (此外,我进行转换所花费的时间比求和要长 8 倍,这似乎有点奇怪,这实际上应该只是求和 + 重复。) 另外,他使用lambda x: x.sum()
的事实会使其速度降低另一个数量级,因此如果 OP 的计算机比我的计算机慢几倍,那么只有几个差异可以解释。
lambda x: x.sum()
将使用慢速算法,将是多个数量级以上是关于5000 万行的 Pandas groupby+transform 需要 3 小时的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 pandas 或 python 将具有数百万行的表从 PostgreSQL 复制到 Amazon Redshift