我使用 np.save() 保存了几个 numpy 数组,它们的总大小非常大。
是否可以将它们全部作为内存映射文件加载,然后通过连接和切片来处理所有数组,而无需将任何内容加载到内存中?
我使用 np.save() 保存了几个 numpy 数组,它们的总大小非常大。
是否可以将它们全部作为内存映射文件加载,然后通过连接和切片来处理所有数组,而无需将任何内容加载到内存中?
使用 numpy.concatenate
方法会将数组加载到内存中。为了避免这种情况,可以轻松地在新文件中创建第三个 memmap
数组,并从要连接的数组中读取值。更有效的方式是,您还可以将新数组附加到磁盘上已存在的文件中。
无论哪种情况,您都必须选择正确的数组顺序(行主序或列主序)。
以下示例说明了如何沿着轴 0 和轴 1 进行连接。
1) 沿着 axis=0
进行连接
a = np.memmap('a.array', dtype='float64', mode='w+', shape=( 5000,1000)) # 38.1MB
a[:,:] = 111
b = np.memmap('b.array', dtype='float64', mode='w+', shape=(15000,1000)) # 114 MB
b[:,:] = 222
你可以定义一个第三个数组,以与要连接的第一个数组(这里是a
)相同的文件为基础,在模式r+
(读取并追加)下进行操作,但是它的形状应该是你想要在拼接后获得的最终数组的形状,例如:
c = np.memmap('a.array', dtype='float64', mode='r+', shape=(20000,1000), order='C')
c[5000:,:] = b
沿着axis=0
进行连接并不需要传递order='C'
,因为这已经是默认顺序。
2)沿着axis=1
进行连接
a = np.memmap('a.array', dtype='float64', mode='w+', shape=(5000,3000)) # 114 MB
a[:,:] = 111
b = np.memmap('b.array', dtype='float64', mode='w+', shape=(5000,1000)) # 38.1MB
b[:,:] = 222
在磁盘上保存的数组实际上是扁平化的,所以如果您使用mode=r+
和shape=(5000,4000)
创建c
时不更改数组顺序,则来自a
第二行的前1000个元素将进入c
中的第一行。但是,您可以通过向memmap
传递order='F'
(列优先)来轻松避免这种情况:c = np.memmap('a.array', dtype='float64', mode='r+',shape=(5000,4000), order='F')
c[:, 3000:] = b
这里有一个更新后的文件'a.array',其中包含连接结果。您可以重复此过程以成对地连接。
相关问题:
也许有另一种解决方案,但我有一个跨多个文件的单个多维数组,我只想要读取它。我用dask concatenation解决了这个问题。
import numpy as np
import dask.array as da
a = np.memmap('a.array', dtype='float64', mode='r', shape=( 5000,1000))
b = np.memmap('b.array', dtype='float64', mode='r', shape=(15000,1000))
c = da.concatenate([a, b], axis=0)
这种方法避免了繁琐的附加文件处理。然后可以对 dask 数组进行切片和操作,几乎像任何 numpy 数组一样,并且在计算结果时调用 compute
。
请注意有两个注意事项:
c[::2] = 0
不可行,因此在这些情况下需要采用创造性的解决方案。store
方法。此方法还可以接受一个 memmapped
数组。order='F'
,会导致另一个问题,即下次加载文件时,即使通过order='F'
,也会变得一团糟。因此,我的解决方案如下,经过多次测试,效果非常好。fp = your old memmap...
shape = fp.shape
data = your ndarray...
data_shape = data.shape
concat_shape = data_shape[:-1] + (data_shape[-1] + shape[-1],)
print('cancat shape:{}'.format(concat_shape))
new_fp = np.memmap(new_file_name, dtype='float32', mode='r+', shape=concat_shape)
if len(concat_shape) == 1:
new_fp[:shape[0]] = fp[:]
new_fp[shape[0]:] = data[:]
if len(concat_shape) == 2:
new_fp[:, :shape[-1]] = fp[:]
new_fp[:, shape[-1]:] = data[:]
elif len(concat_shape) == 3:
new_fp[:, :, :shape[-1]] = fp[:]
new_fp[:, :, shape[-1]:] = data[:]
fp = new_fp
fp.flush()