pandas.concat 和 numpy.append 的大型数据集的内存错误

Posted

技术标签:

【中文标题】pandas.concat 和 numpy.append 的大型数据集的内存错误【英文标题】:Memory error with large data sets for pandas.concat and numpy.append 【发布时间】:2013-11-04 15:18:54 【问题描述】:

我面临一个问题,我必须在循环中生成大型 DataFrame(每次两个 2000 x 800 pandas DataFrame 计算 50 次迭代)。我想将结果保存在内存中更大的 DataFrame 或类似结构的字典中。 使用 pandas.concat 时,我在循环中的某个位置出现内存错误。当使用 numpy.append 将结果存储在 numpy 数组字典而不是 DataFrame 中时,也会发生同样的情况。在这两种情况下,我仍然有很多可用内存(几 GB)。对于 pandas 或 numpy 来说,处理的数据太多了吗?有没有更节省内存的方法来存储我的数据而不将其保存在磁盘上?

例如,只要nbIds 大于 376,以下脚本就会失败:

import pandas as pd
import numpy as np
nbIds = 376
dataids = range(nbIds)
dataCollection1 = []
dataCollection2 = []
for bs in range(50):
    newData1 = pd.DataFrame( np.reshape(np.random.uniform(size = 
                                                          2000 * len(dataids)), 
                                        (2000,len(dataids ))))
    dataCollection1.append( newData1 )
    newData2 = pd.DataFrame( np.reshape(np.random.uniform(size = 
                                                          2000 * len(dataids)), 
                                        (2000,len(dataids ))))
    dataCollection2.append( newData2 )
dataCollection1 = pd.concat(dataCollection1).reset_index(drop = True)
dataCollection2 = pd.concat(dataCollection2).reset_index(drop = True)

nbIds 为 665 或更高时,以下代码会失败

import pandas as pd
import numpy as np
nbIds = 665
dataids = range(nbIds)
dataCollection1 = dict( (i , np.array([])) for i in dataids )
dataCollection2 = dict( (i , np.array([])) for i in dataids )
for bs in range(50):
    newData1 = np.reshape(np.random.uniform(size = 2000 * len(dataids)), 
                         (2000,len(dataids )))
    newData1 = pd.DataFrame(newData1)
    newData2 = np.reshape(np.random.uniform(size = 2000 * len(dataids)), 
                         (2000,len(dataids)))
    newData2 = pd.DataFrame(newData2)
    for i in dataids :
        dataCollection1[i] = np.append(dataCollection1[i] , 
                                       np.array(newData1[i]))
        dataCollection2[i] = np.append(dataCollection2[i] , 
                                       np.array(newData2[i]))

我确实需要每次都计算两个 DataFrame,对于 dataids 的每个元素 i,我需要获取一个 pandas 系列或一个包含为 i 生成的 50 * 2000 个数字的 numpy 数组。理想情况下,我需要能够在 nbIds 等于 800 或更多的情况下运行它。 有没有一种简单的方法可以做到这一点?

我在 Python 2.7.5、pandas 0.12.0 和 numpy 1.7.1 中使用 32 位 Python。

非常感谢您的帮助!

【问题讨论】:

你能准确地说它失败时使用了多少内存吗?可能是32位版本的原因? 使用第一种方法(使用pandas.concat),进程在失败时使用 638 MB。使用第二种方法(将数据存储在 numpy 数组的字典中),进程在失败前达到 1.113 GB 的内存使用量。 如果您的环境允许,请在 64 位 Python 上使用 pandas 上面的代码确实在 Python 64bit 上运行良好。但是我需要让它适用于 Python 32,因为我需要它可以移植到 32 位系统。 【参考方案1】:

一种简单的(但使用硬盘驱动器)方法是简单地使用搁置(硬盘驱动器字典):http://docs.python.org/2/library/shelve.html

【讨论】:

如前所述,我希望尽可能避免将数据保存在磁盘上。 为什么投反对票?这个答案对其中一个问题很有用。 您是否介意评论一下这对所询问的内容有何帮助?因为根据他们自己的描述,Shelve 只是持久化存储。这意味着,您必须首先将 DataFrame 加载到内存中,然后使用 Shelve 来存储它,据我所知,您不能使用 Shelve 而不是使用内存。【参考方案2】:

这基本上就是你正在做的事情。请注意,如果您在之前或之后转换为 DataFrame,从内存的角度来看并没有太大的区别。

但您可以指定 dtype='float32' 以有效地占用 1/2 内存。

In [45]: np.concatenate([ np.random.uniform(size=2000 * 1000).astype('float32').reshape(2000,1000) for i in xrange(50) ]).nbytes
Out[45]: 400000000

In [46]: np.concatenate([ np.random.uniform(size=2000 * 1000).reshape(2000,1000) for i in xrange(50) ]).nbytes
Out[46]: 800000000

In [47]: DataFrame(np.concatenate([ np.random.uniform(size=2000 * 1000).reshape(2000,1000) for i in xrange(50) ]))
Out[47]: 
<class 'pandas.core.frame.DataFrame'>
Int64Index: 100000 entries, 0 to 99999
Columns: 1000 entries, 0 to 999
dtypes: float64(1000)

【讨论】:

pd.DataFrame(np.concatenate([ np.random.uniform(size=2000 * 1000).reshape(2000,1000) for i in xrange(50) ])) 行在我的系统上触发了内存错误。 pd.DataFrame(np.concatenate([ np.random.uniform(size=2000 * 1000).astype('float32').reshape(2000,1000) for i in xrange(50) ])) 行有效,但在第三次调用后触发内存错误,并且在我的代码中使用实际的 DataFrames,使用 float32 允许我在发生内存错误之前创建第一个但不是第二个 DataFrame。 正如上述评论者所建议的,您遇到了 32 位分配问题。它需要找到连续的块才能工作。您根据定义创建事物的方式会碎片化内存,因此它可能会或可能不会起作用。最好的办法是真正安装 64 位 python(addtl memory 不会对 32 位有帮助)。所有连接例程都复制。你也可以做小号的。【参考方案3】:

正如 cmets 中 usethedeathstar、Boud 和 Jeff 所建议的那样,切换到 64 位 python 就可以了。 如果丢失精度不是问题,使用 Jeff 建议的 float32 数据类型也会增加可在 32 位环境中处理的数据量。

【讨论】:

以上是关于pandas.concat 和 numpy.append 的大型数据集的内存错误的主要内容,如果未能解决你的问题,请参考以下文章

pandas.concat 和 numpy.append 的大型数据集的内存错误

为啥 Pandas 串联 (pandas.concat) 的内存效率如此之低?

2018.03.27 pandas concat 和 combin_first使用

pandas.concat() 不填充列

第十五节:pandas之concat()级联

pandas.concat:无法处理非唯一的多索引!熊猫蟒