大型 CSV 文件 (numpy) 上的 Python 内存不足

Posted

技术标签:

【中文标题】大型 CSV 文件 (numpy) 上的 Python 内存不足【英文标题】:Python out of memory on large CSV file (numpy) 【发布时间】:2012-02-15 22:17:31 【问题描述】:

我有一个 3GB 的 CSV 文件,我尝试用 python 读取,我需要中间列。

from numpy import * 
def data():
    return genfromtxt('All.csv',delimiter=',')

data = data() # This is where it fails already.

med = zeros(len(data[0]))
data = data.T
for i in xrange(len(data)):
    m = median(data[i])
    med[i] = 1.0/float(m)
print med

我得到的错误是这样的:

Python(1545) malloc: *** mmap(size=16777216) failed (error code=12)

*** error: can't allocate region

*** set a breakpoint in malloc_error_break to debug

Traceback (most recent call last):

  File "Normalize.py", line 40, in <module>

  data = data()

  File "Normalize.py", line 39, in data

  return genfromtxt('All.csv',delimiter=',')

File "/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/site-
packages/numpy/lib/npyio.py", line 1495, in genfromtxt

for (i, line) in enumerate(itertools.chain([first_line, ], fhd)):

MemoryError

我认为这只是内存不足的错误。我正在运行具有 4GB 内存的 64 位 MacOSX,并且 numpy 和 Python 都以 64 位模式编译。

我该如何解决这个问题?我应该尝试分布式方法,仅用于内存管理吗?

谢谢

编辑:也试过这个,但没有运气......

genfromtxt('All.csv',delimiter=',', dtype=float16)

【问题讨论】:

使用pandas.read_csv会明显更快。 【参考方案1】:

使用 genfromtxt() 的问题在于它试图将整个文件加载到内存中,即加载到 numpy 数组中。这对于小文件非常有用,但对于像您这样的 3GB 输入则不好。由于您只是在计算列中位数,因此无需读取整个文件。一种简单但不是最有效的方法是多次逐行读取整个文件并遍历列。

【讨论】:

好吧,好吧。但是有没有更可持续的解决方案呢?就像在 Java 程序中一样,您可以选择使用 5GB 内存来启动它。是否有 Python 的等价物?我的意思是,下次我可能只有一个单行 4Gb 的 CSV 文件.. Python 不限制您可以分配多少内存。如果你在 64 位 Python 中得到MemoryError,那你真的是内存不足了。 遗憾的是,并非所有 Python 模块都支持 64 位架构。【参考方案2】:

你为什么不使用 python csv 模块?

>> import csv
>> reader = csv.reader(open('All.csv'))
>>> for row in reader:
...     print row

【讨论】:

因为我的整个程序都使用 numpy 和基本的线性代数.. 与读者我不能做所有这些事情。 结合 kz26 的答案,这实际上提供了一个可行的解决方法。也很有趣:经过一次迭代,文件被缓存,进程从 60% 跃升至 99% cpu。【参考方案3】:

正如其他人所提到的,对于一个非常大的文件,最好进行迭代。

但是,出于各种原因,您通常希望将整个内容保存在内存中。

genfromtxt 的效率远低于loadtxt(尽管它处理丢失的数据,而loadtxt 更“精益求精”,这就是这两个功能共存的原因)。

如果您的数据非常规则(例如,只是所有相同类型的简单分隔行),您也可以使用 numpy.fromiter 来改进。

如果您有足够的内存,请考虑使用np.loadtxt('yourfile.txt', delimiter=',')(如果您的文件有标题,您可能还需要指定skiprows。)

作为一个快速比较,使用loadtxt 加载约 500MB 的文本文件在使用高峰时使用约 900MB 的内存,而使用genfromtxt 加载相同的文件使用约 2.5GB。

加载txt


Genfromtxt


或者,考虑以下类似的事情。它只适用于非常简单的常规数据,但速度非常快。 (loadtxtgenfromtxt 做了很多猜测和错误检查。如果你的数据非常简单和规律,你可以大大改进它们。)

import numpy as np

def generate_text_file(length=1e6, ncols=20):
    data = np.random.random((length, ncols))
    np.savetxt('large_text_file.csv', data, delimiter=',')

def iter_loadtxt(filename, delimiter=',', skiprows=0, dtype=float):
    def iter_func():
        with open(filename, 'r') as infile:
            for _ in range(skiprows):
                next(infile)
            for line in infile:
                line = line.rstrip().split(delimiter)
                for item in line:
                    yield dtype(item)
        iter_loadtxt.rowlength = len(line)

    data = np.fromiter(iter_func(), dtype=dtype)
    data = data.reshape((-1, iter_loadtxt.rowlength))
    return data

#generate_text_file()
data = iter_loadtxt('large_text_file.csv')

Fromiter

【讨论】:

基本上,蛮力。 :) 这是我的 shell 脚本,如果你有兴趣的话:gist.github.com/2447356 它远非优雅,但已经足够接近了。 啊,太好了! (虽然我承认我希望得到import memoryprofile 或其他东西,废话!) 好吧,有heapy,(guppy 的一部分:guppy-pe.sourceforge.net)但不幸的是,它不适用于 numpy 数组。不过,很遗憾,import memoryprofile 太棒了! 亲爱的@JoeKington,能否请您为图表的 Y 轴使用单一比例,以便比较在视觉上相似? IMO,您最好将内存使用情况与输出数组大小而不是文件大小进行比较。例如,如果你想加载一个 8192x8192 双精度矩阵,那么无论文本文件有多大,一个最优函数只需要 512MB(8 * 8192 * 8192 字节)即可加载。

以上是关于大型 CSV 文件 (numpy) 上的 Python 内存不足的主要内容,如果未能解决你的问题,请参考以下文章

Numpy+Pandas+Matplotlib学习

Numpy+Pandas+Matplotlib学习

Numpy+Pandas+Matplotlib学习

更新不在磁盘上的大型 DataFrame 对象。

如何将csv文件转换为numpy数组格式?

高级库介绍