在磁盘上保存的numpy数组中进行随机访问



我有一个大数组A,形状为dtypefloat64(2_000_000, 2000),占用32GB。

(或者,将相同的数据拆分为10个形状的阵列(200_0002000(,序列化可能更容易?(。

我们如何将其序列化到磁盘,以便对数据的任何部分进行快速随机读取访问

更准确地说,我需要能够从A以随机起始索引i:读取一万个形状的窗口(162000(

L = []
for i in range(10_000):
i = random.randint(0, 2_000_000 - 16):
window = A[i:i+16, :]         # window of A of shape (16, 2000) starting at a random index i
L.append(window)
WINS = np.concatenate(L)   # shape (10_000, 16, 2000) of float64, ie: ~ 2.4 GB

假设我只有8GB的RAM可用于此任务;完全不可能在RAM中加载整个32GB的CCD_ 7。

我们如何在磁盘上序列化的numpy数组中读取这样的窗口(.h5格式或任何其他(

注:阅读是在随机起始索引处完成的,这一点很重要。

此示例显示如何将HDF5文件用于所描述的过程。

首先,创建一个HDF5文件,其中包含shape(2_000_000, 2000)dtype=float64值的数据集。我在尺寸上使用了变量,所以你可以修改它。

import numpy as np
import h5py
import random
h5_a0, h5_a1 = 2_000_000, 2_000
with h5py.File('SO_68206763.h5','w') as h5f:
dset = h5f.create_dataset('test',shape=(h5_a0, h5_a1))

incr = 1_000
a0 = h5_a0//incr
for i in range(incr):
arr = np.random.random(a0*h5_a1).reshape(a0,h5_a1)
dset[i*a0:i*a0+a0, :] = arr       
print(dset[-1,0:10])  # quick dataset check of values in last row

接下来,以读取模式打开文件,读取形状为(16,2_000)的10_000个随机阵列切片,并将其附加到列表L中。最后,将列表转换为数组WINS。注意,默认情况下,数组将有2个轴——如果每个注释需要3个轴,则需要使用.reshape()(还显示了整形(。

with h5py.File('SO_68206763.h5','r') as h5f:
dset = h5f['test']
L = []
ds0, ds1 = dset.shape[0], dset.shape[1]
for i in range(10_000):
ir = random.randint(0, ds0 - 16)
window = dset[ir:ir+16, :]  # window from dset of shape (16, 2000) starting at a random index i
L.append(window)
WINS = np.concatenate(L)   # shape (160_000, 2_000) of float64,
print(WINS.shape, WINS.dtype)
WINS = np.concatenate(L).reshape(10_0000,16,ds1)   # reshaped to (10_000, 16, 2_000) of float64
print(WINS.shape, WINS.dtype)

上面的过程没有内存效率。您最终得到两个随机切片数据的副本:在列表L和数组WINS中。如果内存有限,这可能是个问题。要避免中间拷贝,请直接将随机数据幻灯片读取到数组中。这样做简化了代码,并减少了内存占用。该方法如下所示(WINS2是2轴阵列,WINS3是3轴阵列(。

with h5py.File('SO_68206763.h5','r') as h5f:
dset = h5f['test']
ds0, ds1 = dset.shape[0], dset.shape[1]
WINS2 = np.empty((10_000*16,ds1))
WINS3 = np.empty((10_000,16,ds1))
for i in range(10_000):
ir = random.randint(0, ds0 - 16)
WINS2[i*16:(i+1)*16,:] = dset[ir:ir+16, :]
WINS3[i,:,:] = dset[ir:ir+16, :]

我尝试过的h5py数据集的另一种解决方案是使用memmap,正如@RyanPepper的评论中所建议的那样。

将数据写入二进制

import numpy as np
with open('a.bin', 'wb') as A:
for f in range(1000):
x =  np.random.randn(10*2000).astype('float32').reshape(10, 2000)
A.write(x.tobytes())
A.flush()

稍后以memmap打开

A = np.memmap('a.bin', dtype='float32', mode='r').reshape((-1, 2000))
print(A.shape)  # (10000, 2000)
print(A[1234:1234+16, :])  # window

最新更新