【问题标题】:Pandas uses substantially more memory for storage than asked forPandas 使用的存储空间比要求的要多得多
【发布时间】:2017-05-24 00:03:33
【问题描述】:

我在带有 python 2.7 或 3.5 的 Ubuntu 16.10 上使用 numpy (1.13.1) 和 pandas (0.20.3)(两者都有相同的问题)。

我正在调查 pandas 内存处理(特别是当它复制或不复制数据时),并遇到了一个我不明白的重大内存问题。虽然我已经看到(许多)人们对其内存性能提出的其他问题,但我还没有找到任何可以直接解决这个问题的问题。

具体来说,pandas 分配的内存比我要求的多很多。在尝试分配具有特定大小列的 DataFrame 时,我注意到一些非常奇怪的行为:

import pandas as pd, numpy as np
GB = 1024**3
df = pd.DataFrame()
df['MyCol'] = np.ones(int(1*GB/8), dtype='float64')

执行此操作时,我看到我的 python 进程实际上分配了 6GB 内存(如果我要求 2GB,则为 12G,如果我要求 3GB,则为 21GB,如果我要求 4GB,我的计算机会中断:-/)而不是1GB,这是预期的。起初我以为 Python 可能在进行一些激进的预分配,但是如果我只构建 numpy 数组本身,我每次都会准确地获得我要求的内存量,无论是 1GB、10GB、25GB 等等。

此外,更有趣的是,如果我将代码稍微改成这样:

df['MyCol'] = np.ones(int(1*GB), dtype='uint8')

它分配了太多内存,导致我的系统崩溃(单独运行 numpy 调用会正确分配 1GB 内存)。 (编辑 2017/8/17:出于好奇,我今天尝试了更新版本的 pandas (0.20.3) 和 numpy (1.13.1),以及升级到 64GB 的 RAM。运行这个命令仍然被破坏,分配所有 64(ish)GB 的可用 RAM。)

我可以理解如果 pandas 进行复制并且可能分配另一列来存储索引,则要求的内存增加一倍甚至三倍,但我无法解释它实际上在做什么。粗略看一下代码也不是很清楚。

我尝试了几种不同的方式来构建数据框,结果都一样。鉴于其他人成功地使用这个包进行大数据分析,我不得不假设我做错了一些可怕的事情,尽管根据文档我可以判断这应该是正确的。

想法?

一些补充说明:

  1. 即使内存使用量很大,当调用 memory_usage() 时,pandas 仍然(错误地)报告预期的数据大小(即,如果我分配一个 1GB 的数组,它会报告 1GB,即使实际分配了 6-10GB)。
  2. 在所有情况下,索引都很小(由 memory_usage() 报告,可能不准确)。
  3. 释放 pandas DataFrame (df = None, gc.collect()) 实际上并没有释放所有内存。使用这种方法一定有泄漏。

【问题讨论】:

  • 试试这个:df = pd.DataFrame({'MyCol':np.ones(int(1*GB), dtype=np.uint8)})
  • @MaxU 虽然这做得更好,但它仍然不完美。分配的开销很大(大约是对象大小的 100%,多一点)(我认为这是在制作数据的副本)。这似乎比我的示例中发生的任何事情都有更合理的解释,但如果不幸的是我需要在内存中投入超过 15GB 的内存,这也无济于事。 (注意:即使数据被拆分到多个通道上也会发生这种情况,例如 3 个 5GB 通道使用这种方法需要将近 30GB 来分配,所以如果正在制作数据副本,它们直到最后都不会被释放。)跨度>
  • df.index 占用多少空间?如果添加另一列(可能是相同的 np.array),内存使用会如何变化?
  • @hpaulj 通过 MaxU 的方法构建后,索引占用 72 个字节。最终的 DataFrame 是正确的大小(15GB),它只是分配了很多开销才能到达那里。如果我使用上述方法分配 1GB 的双精度,则通过 df.memory_usage() 报告的大小再次为索引的 72 字节和数据的 1GB,尽管在这种情况下,进程实际上分配了大约 10GB。

标签: python pandas numpy memory


【解决方案1】:

所以我做了一个 8000 字节的数组:

In [248]: x=np.ones(1000)

In [249]: df=pd.DataFrame({'MyCol': x}, dtype=float)
In [250]: df.info()
<class 'pandas.core.frame.DataFrame'>
Int64Index: 1000 entries, 0 to 999
Data columns (total 1 columns):
MyCol    1000 non-null float64
dtypes: float64(1)
memory usage: 15.6 KB

所以 8k 用于数据,8k 用于索引。

我添加一列 - 使用量增加x

In [251]: df['col2']=x
In [252]: df.info()
<class 'pandas.core.frame.DataFrame'>
Int64Index: 1000 entries, 0 to 999
Data columns (total 2 columns):
MyCol    1000 non-null float64
col2     1000 non-null float64
dtypes: float64(2)
memory usage: 23.4 KB

In [253]: x.nbytes
Out[253]: 8000

【讨论】:

  • 我用一些笔记更新了我的问题。 Pandas 会报告预期的内存利用率(例如,如果我创建一个 1GB 数组,它会报告 1GB),即使实际分配了更多(例如 6GB)。当对象被销毁并且垃圾收集器运行时,它也不会释放所有分配的内存,这一事实让我相信 pandas 中可能存在内存泄漏。
  • 所以你更关心的是一路上的内存使用情况,而不是最终的使用情况。我想知道如果你制作 numpy 数组,然后制作一个副本加上一个相同大小的np.arange()(索引数组)会发生什么。换句话说,尝试创建相同的最终 numpy 数组,但没有 df 结构。
  • 两者,但这是“最终”用法。 DataFrame 可能只有 1GB,但是如果分配的总内存(仍然分配的,而不是在构造对象过程中使用的)是 6 倍大,那是一个真正的问题。另外,请注意,在使用 uint8 初始化分配 1GB 的情况下,实际上会炸毁我的内存(32GB)并崩溃。这些都是问题。正如帖子中提到的,构建 numpy 数组本身没有问题,只需将其放入数据框中即可。
  • 我怀疑 pandas 至少制作了一个,而不是两个,numpy 数组的副本。加上索引数组。如果它扫描输入的一致性或者它需要为索引和 dtype 决策做的其他事情,可能会有一个或多个临时数组。
猜你喜欢
  • 2023-03-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-05-08
  • 2016-11-30
  • 2012-02-15
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多