如何估计熊猫的数据帧需要多少内存?

我一直在想... ... 如果我正在将一个400MB 的 csv 文件读入熊猫数据框(使用 read _ csv 或 read _ table) ,有没有办法估计这将需要多少内存?只是想更好地感受一下数据帧和内存。

140241 次浏览

有的。熊猫将存储您的数据在二维麻木的 ndarray结构分组他们的 dtype。ndarray基本上是一个带有小头的原始 C 数组。因此,您可以通过将它包含的 dtype的大小与数组的维数相乘来估计它的大小。

例如: 如果有1000行,其中有2个 np.int32和5个 np.float64列,那么 DataFrame 将有一个2x1000 np.int32数组和一个5x1000 np.float64数组,即:

4字节 * 2 * 1000 + 8字节 * 5 * 1000 = 48000字节

如果您知道数组的 dtype,那么您可以直接计算存储数据所需的字节数 + Python 对象本身所需的一些字节数。numpy数组的一个有用属性是 nbytes。通过执行以下操作,您可以从熊猫 DataFrame中的数组中获得字节数

nbytes = sum(block.values.nbytes for block in df.blocks.values())

object dtype 数组每个对象存储8个字节(object dtype 数组存储一个指向不透明的 PyObject的指针) ,所以如果你的 csv 中有字符串,你需要考虑到 read_csv会把它们转换成 object dtype 数组,并相应地调整你的计算。

编辑:

有关 object dtype的详细信息,请参阅 标量类型页。由于只存储引用,因此还需要考虑数组中对象的大小。正如该页所说,对象数组在某种程度上类似于 Pythonlist对象。

你得反着来。

In [4]: DataFrame(randn(1000000,20)).to_csv('test.csv')


In [5]: !ls -ltr test.csv
-rw-rw-r-- 1 users 399508276 Aug  6 16:55 test.csv

从技术上讲,内存与此有关(包括索引)

In [16]: df.values.nbytes + df.index.nbytes + df.columns.nbytes
Out[16]: 168000160

因此,168 MB 的内存和400 MB 的文件,1M 行的20个浮点列

DataFrame(randn(1000000,20)).to_hdf('test.h5','df')


!ls -ltr test.h5
-rw-rw-r-- 1 users 168073944 Aug  6 16:57 test.h5

以二进制 HDF5文件编写时要紧凑得多

In [12]: DataFrame(randn(1000000,20)).to_hdf('test.h5','df',complevel=9,complib='blosc')


In [13]: !ls -ltr test.h5
-rw-rw-r-- 1 users 154727012 Aug  6 16:58 test.h5

数据是随机的,所以压缩没有太大帮助

我想我应该给讨论带来更多的数据。

我对这个问题进行了一系列测试。

通过使用 pythonresource包,我得到了进程的内存使用情况。

通过将 csv 写入 StringIO缓冲区,我可以很容易地以字节为单位测量它的大小。

我做了两个实验,每个实验都创建了20个数据框架,它们的大小在10,000行到1000,000行之间不断增加。都有10列。

在第一个实验中,我只在数据集中使用浮点数。

与 csv 文件相比,内存是如何随行数的增加而增加的。(以兆字节为单位)

Memory and CSV size in Megabytes as a function of the number of rows with float entries

第二个实验我也采用了同样的方法,但是数据集中的数据仅由短串组成。

Memory and CSV size in Megabytes as a function of the number of rows with string entries

看起来 csv 的大小和数据帧的大小之间的关系可能有很大的不同,但是内存中的大小总是要大2-3倍(对于这个实验中的帧大小)

我想用更多的实验来完成这个答案,如果你想让我尝试一些特别的东西,请评论。

df.memory_usage() 将返回每列占用的字节数:

>>> df.memory_usage()


Row_ID            20906600
Household_ID      20906600
Vehicle           20906600
Calendar_Year     20906600
Model_Year        20906600
...

若要包含索引,请传递 index=True

为了得到总体内存消耗:

>>> df.memory_usage(index=True).sum()
731731000

此外,传递 deep=True将启用更准确的内存使用情况报告,该报告说明了所包含对象的全部使用情况。

这是因为如果 deep=False(默认情况) ,内存使用量不包括不是数组组件的元素所消耗的内存。

我相信这给了 Python 中任何对象的内存大小。熊猫和麻木的内脏需要检查

>>> import sys
#assuming the dataframe to be df
>>> sys.getsizeof(df)
59542497

下面是不同方法的比较—— sys.getsizeof(df)是最简单的。

对于这个例子,df是一个数据框架,包含814行,11列(2整数,9个对象)——从427kb 的 shapefile 中读取

Getsizeof (df)

>>> import sys
>>> sys.getsizeof(df)
(gives results in bytes)
462456

Memory _ using ()

>>> df.memory_usage()
...
(lists each column at 8 bytes/row)


>>> df.memory_usage().sum()
71712
(roughly rows * cols * 8 bytes)


>>> df.memory_usage(deep=True)
(lists each column's full memory usage)


>>> df.memory_usage(deep=True).sum()
(gives results in bytes)
462432


Info ()

将数据帧信息打印到标准输出。从技术上讲,它们是 kibibyte (KiB) ,而不是 Kobytes ——正如 docstring 所说,“内存使用量以人类可读的单位(基数为2的表示)显示。”因此,要获取字节,需要乘以1024,例如,451.6 KiB = 462,438字节。

>>> df.info()
...
memory usage: 70.0+ KB


>>> df.info(memory_usage='deep')
...
memory usage: 451.6 KB

要打印人类可读的结果,您可以尝试这样做:

suffixes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB']
def humansize(nbytes):
i = 0
while nbytes >= 1024 and i < len(suffixes)-1:
nbytes /= 1024.
i += 1
f = ('%.2f' % nbytes).rstrip('0').rstrip('.')
return '%s %s' % (f, suffixes[i])


df.memory_usage(index=True, deep=True).apply(humansize)
# Index  128 B
# a      571.72 MB
# b      687.78 MB
# c      521.6 MB
# dtype: object


humansize(df.memory_usage(index=True, deep=True).sum())
# 1.74 GB

代码改编自 这个这个答案。