我对这种顺序保存和加载的工作原理感到有点惊讶。我认为没有记录(请纠正我)。但显然每个save
是一个独立的单元,并且load
读取到该单元的末尾,而不是文件的末尾。
想想每一个load
as a readline
。您不能只读取文件的最后一行;你必须阅读它之前的所有内容。
嗯 - 有一种方法可以读取最后一个 - 使用seek
将读取的文件移动到特定点。但要做到这一点,你必须确切地知道所需的块从哪里开始。
np.savez
是将多个数组保存到文件(或者更确切地说,保存到 zip 存档)的预期方法。
save
保存两部分,一个包含如下信息的标头dtype
, shape
and strides
,以及数组数据缓冲区的副本。这nbytes
属性给出了数据缓冲区的大小。至少对于数字和字符串数据类型来说是这样。
save
doc 有一个使用打开的文件的示例 - 带有seek(0)
倒带文件以供使用load
.
np.lib.npyio.format
有关于保存格式的更多信息。看起来可以通过读取头几个字节来确定头的长度。您可能可以使用模块中的函数来执行所有这些读取和计算。
如果我从示例中读取整个文件,我会得到:
In [696]: f.read()
Out[696]:
b"\x93NUMPY\x01\x00F\x00
{'descr': '<i4', 'fortran_order': False, 'shape': (5,), }\n
\x01\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\x04\x00\x00\x00\x05\x00\x00\x00
\x93NUMPY\x01\x00F\x00
{'descr': '<i4', 'fortran_order': False, 'shape': (5,), }\n
\x06\x00\x00\x00\x07\x00\x00\x00\x08\x00\x00\x00\t\x00\x00\x00\n\x00\x00\x00"
我添加了换行符来突出显示该文件的不同部分。请注意,每个save
以。。开始\x93NUMPY
.
使用打开的文件f
,我可以使用以下方式读取标题(或第一个数组):
In [707]: np.lib.npyio.format.read_magic(f)
Out[707]: (1, 0)
In [708]: np.lib.npyio.format.read_array_header_1_0(f)
Out[708]: ((5,), False, dtype('int32'))
我可以使用以下方式加载数据:
In [722]: np.fromfile(f, dtype=np.int32, count=5)
Out[722]: array([1, 2, 3, 4, 5])
我从中推断出这一点np.lib.npyio.format.read_array
功能代码。
现在该文件位于:
In [724]: f.tell()
Out[724]: 100
这是下一个数组的头:
In [725]: np.lib.npyio.format.read_magic(f)
Out[725]: (1, 0)
In [726]: np.lib.npyio.format.read_array_header_1_0(f)
Out[726]: ((5,), False, dtype('int32'))
In [727]: np.fromfile(f, dtype=np.int32, count=5)
Out[727]: array([ 6, 7, 8, 9, 10])
我们现在处于 EOF 状态。
并且知道int32
有4个字节,我们可以算出数据占用20个字节。因此,我们可以通过读取标头、计算数据块的大小以及seek
越过它即可到达下一个数组。对于小型阵列来说,工作并不值得;但对于非常大的,它可能很有用。