numpy的Loadtxt函数似乎占用了过多内存
当我使用numpy.loadtxt加载一个数组时,感觉占用的内存有点多。例如:
a = numpy.zeros(int(1e6))
这会导致内存增加大约8MB(用htop查看,或者简单计算8字节*100万大约等于8MB)。另一方面,如果我先保存这个数组,然后再加载它,
numpy.savetxt('a.csv', a)
b = numpy.loadtxt('a.csv')
我的内存使用量会增加大约100MB!同样,我是通过htop观察到的。这是在iPython环境下,以及使用Pdb++逐步调试代码时发现的。
有没有人知道这是怎么回事?
在阅读了jozzas的回答后,我意识到如果我事先知道数组的大小,有一种更节省内存的方法来处理,比如说'a'是一个mxn的数组:
b = numpy.zeros((m,n))
with open('a.csv', 'r') as f:
reader = csv.reader(f)
for i, row in enumerate(reader):
b[i,:] = numpy.array(row)
2 个回答
0
这是我最后解决这个问题的方法。即使你事先不知道数据的形状,它也能正常工作。这个方法先把数据转换成浮点数,然后再把数组合并(和@JohnLyon的回答不同,他是先合并字符串数组再转换成浮点数)。对我来说,这种方法使用的内存少了很多,虽然可能稍微慢一点。不过,我的内存实在不够用来使用np.loadtxt
,所以如果你的内存也不够,这个方法会更好:
def numpy_loadtxt_memory_friendly(the_file, max_bytes = 1000000, **loadtxt_kwargs):
numpy_arrs = []
with open(the_file, 'rb') as f:
i = 0
while True:
print(i)
some_lines = f.readlines(max_bytes)
if len(some_lines) == 0:
break
vec = np.loadtxt(some_lines, **loadtxt_kwargs)
if len(vec.shape) < 2:
vec = vec.reshape(1,-1)
numpy_arrs.append(vec)
i+=len(some_lines)
return np.concatenate(numpy_arrs, axis=0)
5
把这个浮点数数组保存到一个文本文件里,会生成一个24M的文本文件。当你重新加载这个文件时,numpy会逐行读取文件,解析文本并重新创建对象。
我预计在这个过程中内存使用会激增,因为numpy在读取到文件末尾之前并不知道最终数组需要多大,所以我认为至少会有24M + 8M + 其他临时内存的使用。
下面是numpy代码中相关的部分,来自/lib/npyio.py
:
# Parse each line, including the first
for i, line in enumerate(itertools.chain([first_line], fh)):
vals = split_line(line)
if len(vals) == 0:
continue
if usecols:
vals = [vals[i] for i in usecols]
# Convert each value according to its column and store
items = [conv(val) for (conv, val) in zip(converters, vals)]
# Then pack it according to the dtype's nesting
items = pack_items(items, packing)
X.append(items)
#...A bit further on
X = np.array(X, dtype)
这种额外的内存使用其实不需要担心,因为这就是python的工作方式——虽然你的python程序看起来使用了100M的内存,但内部会知道哪些项目不再使用,并会重复利用那部分内存。例如,如果你在同一个程序中重复执行这个保存-加载的过程(保存、加载、保存、加载),你的内存使用不会增加到200M。