Python在处理大CSV文件时内存不足(numpy)

35 投票
3 回答
29303 浏览
提问于 2025-04-17 10:48

我有一个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

我觉得这只是内存不足的错误。我在一台64位的MacOSX上运行,内存是4GB,并且numpy和Python都是64位编译的。

我该怎么解决这个问题?我是不是应该尝试一种分布式的方法,仅仅是为了更好地管理内存?

谢谢

补充:我也试过这个方法,但还是没成功...

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

3 个回答

2

你为什么不使用Python的csv模块呢?

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

使用genfromtxt()的一个问题是,它会试图把整个文件都加载到内存里,也就是放到一个numpy数组中。对于小文件来说,这样做很好,但对于像你这种3GB的大文件就不好了。因为你只是想计算每一列的中位数,所以没必要把整个文件都读进来。一个简单的方法(虽然不是最有效的)就是多次逐行读取整个文件,然后对每一列进行计算。

73

正如其他人提到的,对于非常大的文件,逐行读取会更好。

不过,有时候你确实需要把整个文件都加载到内存中,原因有很多。

genfromtxt的效率比loadtxt低很多,虽然它能处理缺失的数据,而loadtxt则更简洁高效,这也是这两个函数同时存在的原因。

如果你的数据结构非常规则(比如都是相同类型的简单分隔行),你可以通过使用numpy.fromiter来进一步提高效率。

如果你的电脑内存足够,可以考虑使用np.loadtxt('yourfile.txt', delimiter=',')(如果文件有表头,你可能还需要指定skiprows)。

简单比较一下,加载一个大约500MB的文本文件时,使用loadtxt的内存峰值大约是900MB,而使用genfromtxt则需要大约2.5GB。

Loadtxt

使用numpy.loadtxt加载约500MB的ascii文件时的内存和CPU使用情况

Genfromtxt

使用numpy.genfromtxt加载约500MB的ascii文件时的内存和CPU使用情况

另外,你也可以考虑使用下面的方法。这种方法只适用于非常简单、规则的数据,但速度很快。(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

使用fromiter加载相同的约500MB数据文件

撰写回答