是否可以将numpy数组附加到已经存在的npy文件中进行保存,类似于np.save(filename,arr,mode='a')
?
我有几个函数需要遍历一个大型数组的行。由于内存限制,我不能一次性创建整个数组。为了避免重复创建每一行,我想将每一行创建一次并将其附加到先前在文件中的上一行。稍后,我可以在mmap_mode下加载npy文件,在需要时访问其中的切片。
编辑:这个答案有点过时了,请参考第二个关于NpyAppendArray
的答案。我不建议在2023年使用HDF5,而是使用numpy或zarr。
内置的.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)
步骤三:读取数据的子集
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
https://pypi.org/project/npy-append-array
通过在增长轴(C顺序为0,Fortran顺序为-1)上追加创建Numpy的.npy
文件。它的行为类似于numpy.concatenate
,但不同之处在于结果存储在一个.npy
文件中,可以重复使用进行进一步的追加。创建后,文件可以通过内存映射读取(例如添加mmap_mode="r"
),这样就可以创建和读取文件(可选地)大于计算机的主内存。
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, delete_if_exists=True) as npaa:
npaa.append(arr1)
npaa.append(arr2)
npaa.append(arr2)
data = np.load(filename, mmap_mode="r")
print(data)
NpyAppendArray包含了Numpy软件包中修改过的、部分版本的format.py
。它确保创建的数组头具有21个(=len(str(8*2**64-1))
)字节的空余空间。这样可以在不增加数组头大小的情况下容纳一个最大化的维度数组(对于64位机器)。这使得我们可以简单地在向.npy
文件末尾添加数据时重写头文件。
这是对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 = 数组([[ 0., 0.], [ 1., 1.]])
for i in range(size[0]):
data[i,:] = np.load(f)```
- Khodeir.npy
文件包含头文件,其中包含数组的形状和数据类型。如果您知道最终数组的外观,您可以自己编写头文件,然后将数据分块。例如,这是连接二维矩阵的代码:
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'))
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
f_handle = file(filename, 'a')
numpy.save(f_handle, arr)
f_handle.close()
python 2.7.12
和 numpy 1.12.1
中不起作用。数组仍然保持不变,没有添加任何内容。另外请注意,您提供的链接是关于 savetxt
方法,而不是 np.save
。 - Dennis Golomazovwith 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')
输出:array([0., 0.])
希望得到 array([[0., 0.], [1., 1.]])
。 - ethanabrooksopen
而不是 file
吗? - Shamoonimport 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
经过两次操作后:
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]
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)
np.save
没有追加模式。如果有必要,我想它会被实现的。 - user3820991