提问人:lara_toff 提问时间:8/13/2020 更新时间:9/9/2020 访问量:1329
如何从 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 中?
还有没有办法一次只从磁盘读取阵列的特定部分?
答:
2赞
Aaron
8/13/2020
#1
在使用 numpy 和大型数组时,有几种方法,具体取决于您需要对该数据执行的操作。
最简单的答案是使用更少的数据。如果您的数据有很多重复元素,通常可以使用 scipy 中的稀疏数组,因为这两个库高度集成。
另一个答案(IMO:问题的正确解决方案)是使用内存映射数组。这将使 numpy 只在需要时加载必要的部分到 ram,其余的留在磁盘上。包含数据的文件可以是使用任意数量的方法创建的简单二进制文件,但处理此问题的内置 python 模块是 struct
。追加更多数据就像在追加模式下打开文件并写入更多字节的数据一样简单。确保在将更多数据写入文件时重新创建对内存映射数组的任何引用,以便信息是最新的。
最后是压缩之类的东西。Numpy 可以用 savez_compressed 压缩数组,然后可以用 打开
。重要的是,压缩的numpy文件不能进行内存映射,必须完全加载到内存中。一次加载一列可能会使您低于阈值,但这同样可以应用于其他方法以减少内存使用量。Numpy的内置压缩技术只会节省磁盘空间,而不会节省内存。可能存在其他库执行某种流式压缩,但这超出了我的回答范围。numpy.load
下面是将二进制数据放入文件,然后将其作为内存映射数组打开的示例:
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())
评论
0赞
lara_toff
8/20/2020
但是,我怎样才能首先制作出庞大的阵列呢?例如,我有 25GB 的 RAM。我有 1500 个 CSV 文件,加载后总计约为 50GB。我想加载所有 1500 个 CSV 文件以制作一个巨大的数组,但我不能,因为它们不会同时全部适合我的 RAM。我的想法是制作一个 for 循环和 np.vstack 将每个 CSV 文件的内容添加到一个数组中......
1赞
Aaron
8/20/2020
@lara_toff有几种方法可以将数据导入文件。我已经在我的回答中提到过,numpy 还提供了数组的方法,它返回可以写入二进制文件的原始二进制文件。我写了一个快速的例子来做到这一点。struct
tobytes
0赞
lara_toff
8/20/2020
好的,只是为了确认在“写入一些数据”部分,我可以做一个 np.vstack 添加到 memmap 中?
1赞
Aaron
8/20/2020
您对 memmap 所做的任何修改都将保留在 RAM 中,直到您关闭文件,如示例末尾的超长注释中所述。Numpy 数组的设计永远不会改变大小,因此像 vstack 这样的东西通常会复制相关数据并创建一个全新的对象。我演示的方法一次消耗的内存不应超过每个单独的 csv 中的数据(加上 python 本身的任何开销,以及操作系统完成的缓冲文件写入)。
1赞
Aaron
8/20/2020
@lara_toff,在我链接的文档中,有很多详细的解释和内存映射阵列的示例。
评论