【问题标题】:How to merge very large numpy arrays?如何合并非常大的numpy数组?
【发布时间】:2018-06-08 23:34:36
【问题描述】:

我将有许多Numpy arrays 存储在npz 文件中,这些文件正在使用savez_compressed 函数保存。

我将信息拆分为多个数组,因为如果不是,我正在使用的函数由于内存问题而崩溃。数据不是稀疏的。

我需要将所有这些信息合并到一个唯一的数组中(以便能够使用一些例程处理它),并将其存储到磁盘中(使用不同的参数多次处理它)。

阵列不适合 RAM+交换内存。

如何将它们合并成一个唯一的数组并保存到磁盘?

我怀疑我应该使用mmap_mode,但我不知道具体如何。另外,我想如果我一开始不保留连续的磁盘空间,可能会出现一些性能问题。

我已经阅读了this 的帖子,但我仍然不知道该怎么做。


编辑

澄清:我已经制作了许多函数来处理类似的数据,其中一些需要一个数组作为参数。在某些情况下,我可以通过使用切片将它们仅传递给这个大数组的一部分。但掌握所有信息仍然很重要。在这样的数组中。

这是因为:数组包含时间排序的信息(来自物理模拟)。在函数的参数中,用户可以设置初始处理时间和最后处理时间。此外,他/她可以设置处理块的大小(这很重要,因为这会影响性能,但允许的块大小取决于计算资源)。因此,我无法将数据存储为单独的块。

这个特定数组(我正在尝试创建的数组)的构建方式在它工作时并不重要。

【问题讨论】:

  • 你不能映射压缩数组。如果您尝试,我认为当前的np.load 实现只是忽略了mmap_mode
  • 感谢您提供的信息。
  • 必须将它们合并到一个数组中,还是只能逐块加载、逐块处理、逐块写出?
  • @user1420303:我明白了。您仍然可以通过查看时间范围、找到相应的块并加载这些块并在必要时对第一个和最后一个块进行切片来使用分块数据。这有点逻辑,但它可以防止您耗尽内存。您甚至可以将其抽象为某种流集合类,允许透明的数组索引和隐藏该逻辑。
  • @user1420303:我不知道这个功能。这似乎很合理,是的。但是,我不完全确定它是如何完成的。

标签: numpy memory-management


【解决方案1】:

您应该能够在 np.memap 数组上逐块加载:

import numpy as np

data_files = ['file1.npz', 'file2.npz2', ...]

# If you do not know the final size beforehand you need to
# go through the chunks once first to check their sizes
rows = 0
cols = None
dtype = None
for data_file in data_files:
    with np.load(data_file) as data:
        chunk = data['array']
        rows += chunk.shape[0]
        cols = chunk.shape[1]
        dtype = chunk.dtype

# Once the size is know create memmap and write chunks
merged = np.memmap('merged.buffer', dtype=dtype, mode='w+', shape=(rows, cols))
idx = 0
for data_file in data_files:
    with np.load(data_file) as data:
        chunk = data['array']
        merged[idx:idx + len(chunk)] = chunk
        idx += len(chunk)

但是,正如 cmets 所指出的,在不是最快的维度上工作会非常慢。

【讨论】:

  • 感谢您的回答。我给了我一些想法。我不明白代码如何加载多个预先存在的 npz 文件。
  • @user1420303 使用data.iteritems 浏览文件中的数组,使用sorted(data.keys()) 浏览数组中的名称(我假设它们应该按字母顺序排序,但可能是别的东西)。
  • 对。据我了解代码,它读取包含多个数组的“一个”npz 文件,并将它们合并。我需要读取“许多”npz 文件,每个文件包含一个数组,然后合并它们。
  • @user1420303 啊,我明白了,好吧,我没有正确理解。我现在改了。
【解决方案2】:

这是一个如何将 90GB 易于压缩的数据写入磁盘的示例。这里提到了最重要的点https://stackoverflow.com/a/48405220/4045774

普通 HDD 的写入/读取速度应在 (300 MB/s,500MB/s) 范围内。

示例

import numpy as np
import tables #register blosc
import h5py as h5
import h5py_cache as h5c
import time

def read_the_arrays():
  #Easily compressable data
  #A lot smaller than your actual array, I do not have that much RAM
  return np.arange(10*int(15E3)).reshape(10,int(15E3))

def writing(hdf5_path):
  # As we are writing whole chunks here this isn't realy needed,
  # if you forget to set a large enough chunk-cache-size when not writing or reading 
  # whole chunks, the performance will be extremely bad. (chunks can only be read or written as a whole)
  f = h5c.File(hdf5_path, 'w',chunk_cache_mem_size=1024**2*1000) #1000 MB cache size
  dset = f.create_dataset("your_data", shape=(int(15E5),int(15E3)),dtype=np.float32,chunks=(10000,100),compression=32001,compression_opts=(0, 0, 0, 0, 9, 1, 1), shuffle=False)

  #Lets write to the dataset
  for i in range(0,int(15E5),10):
    dset[i:i+10,:]=read_the_arrays()

  f.close()

def reading(hdf5_path):
  f = h5c.File(hdf5_path, 'r',chunk_cache_mem_size=1024**2*1000) #1000 MB cache size
  dset = f["your_data"]

  #Read chunks
  for i in range(0,int(15E3),10):
    data=np.copy(dset[:,i:i+10])
  f.close()

hdf5_path='Test.h5'
t1=time.time()
writing(hdf5_path)
print(time.time()-t1)
t1=time.time()
reading(hdf5_path)
print(time.time()-t1)

【讨论】:

  • 谢谢。写入速度刚刚好。我需要考虑代码。我对hdf5不熟悉。问:你这样做:'dset = f.create_dataset' 然后 'dset[i:i+10,:]=read_the_arrays()' 很多次。整个数组永远不会在 RAM 中,对吗?
  • 是的,read_the_arrays() 函数应该简单地模仿 npz 文件的读取过程。所以最大。 RAM 使用量应该是一个 InputArray 的大小+ chunk-chache-size,我设置为 1000MB。这也可以更低,但如果缓存太少,性能会急剧下降。
  • 很好,这让我很容易解决另一个问题(有些时间值在数组中重复,即有一点叠加)。你认为最终的.h5文件可以简单地转换为.npz吗?
  • 如果一个chunk中出现重复值,压缩算法会很好的处理。可以将 HDF5 文件(它太大而无法放入内存)转换为压缩的 numpy 文件,但不是那么简单。 (将某些内容分块写入 zip 文件)。
猜你喜欢
  • 1970-01-01
  • 2013-10-05
  • 1970-01-01
  • 2021-04-06
  • 2019-10-28
  • 1970-01-01
  • 1970-01-01
  • 2019-03-30
  • 2021-09-05
相关资源
最近更新 更多