如何解决 pandas 的内存分配问题?

Posted

技术标签:

【中文标题】如何解决 pandas 的内存分配问题?【英文标题】:How do I fix memory allocation problem with pandas? 【发布时间】:2020-02-17 09:21:43 【问题描述】:

小背景,我在公司继承了一段用 Python 编写的代码,我真的不知道,该代码使用 pandas 将几个预先下载的 Excel 报告组合成一个。我一直遇到内存分配错误:

MemoryError: Unable to allocate 368. MiB for an array with shape (17, 5668350) and data type object

这是给我错误的代码:

dfCC = dfVendNew.merge(dfVendOld[['SAP ID', 'Cost ctr']], on='SAP ID', how='left')

我被困在这一点上,无法进一步前进。我尝试在 Windows 上更改分页大小,但没有帮助。我怀疑它与我的计算机设置有关,因为此脚本在其他机器上运行顺利。

如果有任何帮助,我将不胜感激。

【问题讨论】:

您可能需要更多 RAM...更合格的答案取决于数据、实际数据帧以及项目中 RAM 中加载的其他内容 我已经检查了我的 RAM 使用量在 31.8 GB 中没有超过 7 GB 32 位 Python(或需要 32 位 Python 的 32 位操作系统)意味着您被限制为可用于 Python 的 4G 最大内存 - 您是否在 64 位上使用 64 位 Python操作系统? 哦不,我想你可能已经找到了解决我的问题的方法,让我看看它在 64 位版本上的表现,因为我目前使用的是 32 位 【参考方案1】:

您可以尝试分块处理和合并数据帧,这是我的尝试:

all_data = pd.DataFrame()
new = dfVendNew
old = dfVendOld[['SAP ID', 'Cost ctr']]
    for sap_id in np.array_split(new['SAP ID'].unique(), 10):
         new_chunk = new[new['SAP ID'].isin(sap_id)]
         old_chunk = old[old['SAP ID'].isin(sap_id)]
         merged = new_chunk.merge(old_chunk, on='SAP ID', how='left')
         all_data = pd.concat([merged, all_data], ignore_index=True, sort=False)

        del new_chunk
        del old_chunk
        del merged

    return all_data

首先您获得新数据框的唯一 SAP ID,然后创建 10 个不同的 SAP ID 列表,然后根据这些列表拆分新旧数据框。合并每个块和del 以减轻您的记忆。

【讨论】:

您好 Onur,感谢您的回复,我已经尝试实现此功能,错误消息更改为:MemoryError: Unable to allocate 241. MiB for an array with shape (18, 3511836) and data type object 它在第 6 次循环中中断,那些 del 指令应该在 for 循环之外是吗? 嗨 Tomasz,del 应该在 for 循环中,因为循环会创建块、合并它们、附加到 all_data,然后它不再使用这些块,因此它会删除这些对象以释放内存每次迭代。对于您的新错误消息,我认为一个关键因素可能是您的 SAP ID 如何在数据中分布,例如,如果 %80 的行包含 %10 的 SAP ID,那么基于唯一 SAP ID 拆分数据帧没有多大意义,因为它会导致不平衡的块。希望了解更多有关您的 SAP ID 如何在您的数据中分布的信息,谢谢。【参考方案2】:

对象将成为存储事物列表的最胖方式。但是你需要知道一些东西是如何存储的,以使它更小更快。 使用数据框的df.info()查看列类型

这是一个玩具示例:

<class 'pandas.core.frame.DataFrame'>
RangeIndex: 3 entries, 0 to 2
Data columns (total 3 columns):
people     3 non-null object
cost_ctr    3 non-null object
number     3 non-null int64
dtypes: int64(1), object(2)
memory usage: 152.0+ bytes

在这种情况下,人是一个对象类,消息也是。要查看的另一件事是在最后一行:内存使用情况。因此,现在您将能够更改数据类型并观察内存使用量的下降。那么让我们来看看如何更改其中的一些类型。

默认情况下,您的 SAP_ID 可能是一个 int。如果不是,您可以使用所有数字数据:

df['SAP ID']=df['SAP ID'].astype(int)

df['SAP ID']=pd.to_numeric(df['SAP ID'])

所以现在你已经改变了一个列的类型,再次用df.info()检查内存。

“Cost ctr”听起来像是一个简短的重复很多东西的列表,但通常存储为字符串列表。您可以将此列更改为 pd.categorical 并查看使用此命令节省了多少内存。

df['Cost_Ctr'] = df['Cost_Ctr'].astype(pd.Categorical)

查看使用astype here的文档 下一个级别的举措是首先正确地导入它。当您阅读 excel 文件时,请使用 converters argument in read_excel。

如果降低内存使用率仍然是个问题(这不应该只存在于 Excel 记录中),还有其他分布式技术可以用于此目的,即Dask。

希望这会有所帮助。

【讨论】:

感谢您的回答,这些文件中的数据结构确实非常奇怪,所以我一定要看看。但是我的问题是在 64 位操作系统上运行 32 位 Python 的结果。

以上是关于如何解决 pandas 的内存分配问题?的主要内容,如果未能解决你的问题,请参考以下文章

如何释放为数组分配的内存?

如何解决 MemoryError:Micropython 的内存分配失败?

如何解决内存碎片

如何解决 keras 上的错误“分配超过 10 的系统内存”?

如何解决python包的静态TLS块错误中无法分配内存

无法足够快地分配内存?