避免在大型Pandas DataFrame上进行GroupBy时出现内存问题

15

更新:

pandas数据框是这样创建的:

df = pd.read_sql(query, engine)
encoded = pd.get_dummies(df, columns=['account'])

从这个 df 创建一个 dask df 看起来像这样:
df = dd.from_pandas(encoded, 50)

使用dask执行操作时,没有任何可见的进展(使用dask诊断进行检查):

result = df.groupby('journal_entry').max().reset_index().compute()

翻译结果:

我有一个包含2.7M行和4,000列的大型pandas df。除了四个列以外,所有的列都是uint8类型。uint8列只包含值1或0。我试图对df执行以下操作:

result = df.groupby('id').max().reset_index()

可预见的是,此操作会立即返回一个内存错误。我的初始想法是将df水平和垂直地分块。然而,这会创建一个混乱的情况,因为.max()需要在所有uint8列上执行,而不仅仅是一对列。此外,像这样分块df仍然非常缓慢。我的机器上有32 GB的RAM。

有什么策略可以缓解内存问题吗?

3个回答

44
如果您的数据中有任何分类列(而不是存储为对象列或字符串的类别),请确保在groupby命令中使用observed=True选项。这样可以确保只创建存在条目的行,例如每个customer_id、order_id组合只创建一行,而不是创建n_custs * n_orders行!
我刚刚对一个包含2600万行数据集进行了groupby-sum操作,并且内存使用量从未超过7GB。在添加observed=True选项之前,内存使用量一直上升到62GB,然后耗尽。

2
你救了我的一命!它从几个小时变成了毫秒级别!你知道他们为什么不将其设置为默认值吗? - Daniel O
1
我在将pandas软件包版本从0.23.3更新到0.25.1时,发现内存消耗大幅增加。我猜测这个标志在早期版本中默认启用了。 - username
你可以进去并点赞这个问题,支持更改默认 https://github.com/pandas-dev/pandas/issues/43999 - Peter Mølgaard Pallesen
你真是一个超级女孩!非常感谢! - undefined

8
你可以使用 dask.dataframe 来完成这个任务。
import dask.dataframe as dd
df = dd.from_pandas(df)
result = df.groupby('id').max().reset_index().compute()

你只需要将你的转换为。Dask是一个Python分布式并行计算框架,提供了各种并行化容器类型,其中之一就是dataframe。它可以让你在并行和/或分布式环境下执行大多数常见的pandas.DataFrame操作,处理那些无法放入内存的数据。Dask的核心是一组调度程序和构建计算图的API,因此我们必须在最后调用.compute()才能实际进行任何计算。该库易于安装,因为它主要是用纯Python编写的。

1
dask.diagnostics具有进度条,这应该是找出计算需要多长时间的完美工具。在dask中,groupby操作通常很慢,因此在进行groupby之前将“id”设置为索引可能能够加快整个操作的速度。 - tobsecret
1
不确定您是否可以在 pandas 中执行 groupby 操作,然后仅让 dask 处理其余的计算。由于 dask 必须为每个分区设置索引,然后与其他分区通信(groupby 操作也是如此),因此 groupby 和索引操作在 dask 中很慢。因此,一般来说,如果分区较少,则这些操作应该更快,但请不要引用我说的话。 - tobsecret
我也将用于创建初始df的代码添加到了我的问题中。 - OverflowingTheGlass
我感谢你的帮助 - 我会联系他们。现在,我已经将我的数据分成了较小的块,并单独对每个块运行了Dask计算。 - OverflowingTheGlass
我已经使用dask多年了,但这对我的内存问题没有解决。 - Matt Elgazar
显示剩余8条评论

1
作为一个想法,我会建议按列将数据分割成四个部分,然后使用每个子集的id执行操作,最后重新合并。

是的,这是我的当前思路 - 即使只有两列,它也非常缓慢。 - OverflowingTheGlass

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接