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 顺便说一下,我认为对于小的DataFrames,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

groupby中特定行的Python pandas差异

pandas 操作过程中的进度指示器

Pandas groupby计算每n行的平均值

在不包括当前行的两列之间使用pandas groupby除法创建一个新列

如何有效地迭代 Pandas 数据帧的连续块