如何从 1000 个 CSV 文件创建一个比我的 RAM 大得多的 Numpy 数组?

How can I create a Numpy Array that is much bigger than my RAM from 1000s of CSV files?

我有 1000 个 CSV 文件,我想附加这些文件并创建一个大的 numpy 数组。问题是 numpy 数组会比我的 RAM 大得多。有没有一种方法可以在不将整个阵列都放在 RAM 中的情况下一次写入磁盘?

还有没有办法一次只从磁盘读取数组的特定部分?

在处理 numpy 和大型数组时,有几种方法取决于您需要对数据执行的操作。

最简单的答案是使用更少的数据。如果您的数据有很多重复元素,通常可以使用 scipy 中的 sparse array,因为这两个库高度集成。

另一个答案(IMO:您问题的正确解决方案)是使用 memory mapped array. This will let numpy only load the necessary parts to ram when needed, and leave the rest on disk. The files containing the data can be simple binary files created using any number of methods, but the built-in python module that would handle this is struct。追加更多数据就像以追加模式打开文件并写入更多数据字节一样简单。每当有更多数据写入文件时,请确保对内存映射数组的任何引用 re-created,以便信息是最新的。

最后是压缩之类的东西。 Numpy 可以使用 savez_compressed 压缩数组,然后可以使用 numpy.load 打开数组。重要的是,压缩的 numpy 文件不能 memory-mapped,并且必须完全加载到内存中。一次加载一列可能会让您低于阈值,但这同样可以应用于其他方法以减少内存使用。 Numpy 的内置压缩技术只会节省磁盘 space 而不是内存。可能存在其他执行某种流式压缩的库,但这超出了我的回答范围。

下面是一个将二进制数据放入文件然后将其作为 memory-mapped 数组打开的示例:

import numpy as np

#open a file for data of a single column
with open('column_data.dat', 'wb') as f:
    #for 1024 "csv files"
    for _ in range(1024):
        csv_data = np.random.rand(1024).astype(np.float) #represents one column of data
        f.write(csv_data.tobytes())

#open the array as a memory-mapped file
column_mmap = np.memmap('column_data.dat', dtype=np.float)

#read some data
print(np.mean(column_mmap[0:1024]))

#write some data
column_mmap[0:512] = .5

#deletion closes the memory-mapped file and flush changes to disk.
#  del isn't specifically needed as python will garbage collect objects no
#  longer accessable. If for example you intend to read the entire array,
#  you will need to periodically make sure the array gets deleted and re-created
#  or the entire thing will end up in memory again. This could be done with a
#  function that loads and operates on part of the array, then when the function
#  returns and the memory-mapped array local to the function goes out of scope,
#  it will be garbage collected. Calling such a function would not cause a
#  build-up of memory usage.
del column_mmap

#write some more data to the array (not while the mmap is open)
with open('column_data.dat', 'ab') as f:
    #for 1024 "csv files"
    for _ in range(1024):
        csv_data = np.random.rand(1024).astype(np.float) #represents one column of data
        f.write(csv_data.tobytes())