【发布时间】:2015-08-03 06:56:11
【问题描述】:
是否可以保存一个 numpy 数组,并将其附加到一个已经存在的 npy 文件 --- 类似于 np.save(filename,arr,mode='a')?
我有几个函数必须遍历一个大数组的行。由于内存限制,我无法立即创建数组。为了避免一遍又一遍地创建行,我想创建每一行一次并将其保存到文件中,并将其附加到文件中的前一行。稍后我可以在 mmap_mode 中加载 npy 文件,在需要时访问切片。
【问题讨论】:
是否可以保存一个 numpy 数组,并将其附加到一个已经存在的 npy 文件 --- 类似于 np.save(filename,arr,mode='a')?
我有几个函数必须遍历一个大数组的行。由于内存限制,我无法立即创建数组。为了避免一遍又一遍地创建行,我想创建每一行一次并将其保存到文件中,并将其附加到文件中的前一行。稍后我可以在 mmap_mode 中加载 npy 文件,在需要时访问切片。
【问题讨论】:
内置的.npy 文件格式非常适合处理小型数据集,无需依赖numpy 以外的外部模块。
但是,当您开始拥有大量数据时,最好使用专门用于处理此类数据集的文件格式(例如 HDF5)[1]。
例如,下面是使用PyTables 将numpy 数组保存在HDF5 中的解决方案,
第 1 步:创建可扩展的 EArray 存储
import tables
import numpy as np
filename = 'outarray.h5'
ROW_SIZE = 100
NUM_COLUMNS = 200
f = tables.open_file(filename, mode='w')
atom = tables.Float64Atom()
array_c = f.create_earray(f.root, 'data', atom, (0, ROW_SIZE))
for idx in range(NUM_COLUMNS):
x = np.random.rand(1, ROW_SIZE)
array_c.append(x)
f.close()
第 2 步:将行追加到现有数据集(如果需要)
f = tables.open_file(filename, mode='a')
f.root.data.append(x)
第 3 步:读回数据子集
f = tables.open_file(filename, mode='r')
print(f.root.data[1:10,2:20]) # e.g. read from disk only this part of the dataset
【讨论】:
np.save 没有附加模式。如果它是明智的,我想它已经实现了。
要使用 numpy.save 将数据附加到现有文件,我们应该使用:
f_handle = file(filename, 'a')
numpy.save(f_handle, arr)
f_handle.close()
我已检查它在 python 2.7 和 numpy 1.10.4 中是否有效
我已经改编了来自here 的代码,其中谈到了 savetxt 方法。
【讨论】:
python 2.7.12 和numpy 1.12.1 中不起作用。数组保持不变,没有附加任何内容。另请注意,您提供的链接谈论的是savetxt 方法,而不是np.save。
with Path('/tmp/npy').open('wb') as f: np.save(f, np.zeros(2))with Path('/tmp/npy').open('ab') as f: np.save(f, np.ones(2))np.load('/tmp/npy')Out: array([0., 0.])希望array([[0., 0.], [1., 1.]])
open 而不是file?
.npy 文件包含标头,其中包含数组的形状和 dtype。如果您知道生成的数组是什么样子,您可以自己编写标头,然后以块的形式编写数据。例如,这里是连接二维矩阵的代码:
import numpy as np
import numpy.lib.format as fmt
def get_header(fnames):
dtype = None
shape_0 = 0
shape_1 = None
for i, fname in enumerate(fnames):
m = np.load(fname, mmap_mode='r') # mmap so we read only header really fast
if i == 0:
dtype = m.dtype
shape_1 = m.shape[1]
else:
assert m.dtype == dtype
assert m.shape[1] == shape_1
shape_0 += m.shape[0]
return {'descr': fmt.dtype_to_descr(dtype), 'fortran_order': False, 'shape': (shape_0, shape_1)}
def concatenate(res_fname, input_fnames):
header = get_header(input_fnames)
with open(res_fname, 'wb') as f:
fmt.write_array_header_2_0(f, header)
for fname in input_fnames:
m = np.load(fname)
f.write(m.tostring('C'))
如果您需要更通用的解决方案(在追加时编辑标题),您将不得不使用 [1] 中的 fseek 技巧。
灵感来自
[1]:https://mail.scipy.org/pipermail/numpy-discussion/2009-August/044570.html(不能开箱即用)
[2]:https://docs.scipy.org/doc/numpy/neps/npy-format.html
[3]:https://github.com/numpy/numpy/blob/master/numpy/lib/format.py
【讨论】:
你可以尝试读取文件然后添加新数据
import numpy as np
import os.path
x = np.arange(10) #[0 1 2 3 4 5 6 7 8 9]
y = np.load("save.npy") if os.path.isfile("save.npy") else [] #get data if exist
np.save("save.npy",np.append(y,x)) #save the new
2次操作后:
print(np.load("save.npy")) #[0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9]
【讨论】:
这是对 Mohit Pandey 答案的扩展,显示了完整的保存/加载示例。它使用 Python 3.6 和 Numpy 1.11.3 进行了测试。
from pathlib import Path
import numpy as np
import os
p = Path('temp.npy')
with p.open('ab') as f:
np.save(f, np.zeros(2))
np.save(f, np.ones(2))
with p.open('rb') as f:
fsz = os.fstat(f.fileno()).st_size
out = np.load(f)
while f.tell() < fsz:
out = np.vstack((out, np.load(f)))
out = array([[ 0., 0.], [ 1., 1.]])
【讨论】:
size = (<num_rows>, <num_cols) # the shape of your matrix for i in range(size[0]): data[i,:] = np.load(f)
以下内容基于 PaxRomana99 的回答。 它创建了一个类,您可以使用它来保存和加载数组。 理想情况下,每次添加新数组时都会更改 npy 文件的标题以修改形状的描述(有关标题的描述,请参阅here)
import numpy as np
import pickle
from pathlib import Path
import os
class npyAppendableFile():
def __init__(self, fname, newfile=True):
'''
Creates a new instance of the appendable filetype
If newfile is True, recreate the file even if already exists
'''
self.fname=Path(fname)
if newfile:
with open(self.fname, "wb") as fh:
fh.close()
def write(self, data):
'''
append a new array to the file
note that this will not change the header
'''
with open(self.fname, "ab") as fh:
np.save(fh, data)
def load(self, axis=2):
'''
Load the whole file, returning all the arrays that were consecutively
saved on top of each other
axis defines how the arrays should be concatenated
'''
with open(self.fname, "rb") as fh:
fsz = os.fstat(fh.fileno()).st_size
out = np.load(fh)
while fh.tell() < fsz:
out = np.concatenate((out, np.load(fh)), axis=axis)
return out
def update_content(self):
'''
'''
content = self.load()
with open(self.fname, "wb") as fh:
np.save(fh, content)
@property
def _dtype(self):
return self.load().dtype
@property
def _actual_shape(self):
return self.load().shape
@property
def header(self):
'''
Reads the header of the npy file
'''
with open(self.fname, "rb") as fh:
version = np.lib.format.read_magic(fh)
shape, fortran, dtype = np.lib.format._read_array_header(fh, version)
return version, {'descr': dtype,
'fortran_order' : fortran,
'shape' : shape}
arr_a = np.random.rand(5,40,10)
arr_b = np.random.rand(5,40,7)
arr_c = np.random.rand(5,40,3)
f = npyAppendableFile("testfile.npy", True)
f.write(arr_a)
f.write(arr_b)
f.write(arr_c)
out = f.load()
print (f.header)
print (f._actual_shape)
# after update we can load with regular np.load()
f.update_content()
new_content = np.load('testfile.npy')
print (new_content.shape)
【讨论】:
我创建了一个库来创建大于机器主内存的 Numpy .npy 文件,方法是附加在零轴上。然后可以使用mmap_mode="r" 读取该文件。
https://pypi.org/project/npy-append-array
conda install -c conda-forge npy-append-array
或
pip install npy-append-array
from npy_append_array import NpyAppendArray
import numpy as np
arr1 = np.array([[1,2],[3,4]])
arr2 = np.array([[1,2],[3,4],[5,6]])
filename = 'out.npy'
with NpyAppendArray(filename) as npaa:
npaa.append(arr1)
npaa.append(arr2)
npaa.append(arr2)
data = np.load(filename, mmap_mode="r")
print(data)
附加到由 np.save 创建的数组在某些情况下是可能的,因为 .npy 总标题字节数需要被 64 整除。因此,可能有一些空闲空间来增加形状条目数组描述符。但是,这不能保证,并且可能会随机失败。直接使用 NpyAppendArray(filename) 初始化数组(见上文),因此将使用 64 字节的备用头空间创建头以供增长。
它最多允许 10^64 >= 2^212 个数组条目或数据位。事实上,这比宇宙中的原子数还少。然而,由于量子力学的限制,完全填充这样的阵列需要比煮沸海洋所需的更多能量,比较
https://hbfs.wordpress.com/2009/02/10/to-boil-the-oceans
因此,这种方法应该可以覆盖广泛的用例。
【讨论】: