有人可以解释这段代码中发生的事情,将numpy数组保存为二进制文件吗?

时间:2017-11-01 02:10:48

标签: arrays numpy format pickle binaryfiles

我一直在使用pickle库来读取和编写numpy数组,但它们往往非常大。在我寻找是否有更好的方法的过程中,我在this页面(图表中的那个)找到了Mark的答案。基本上,将其存储为二进制文件似乎不仅是最快的读写,而且还占用最少的内存。所以我点击his github link并在第96行找到了我认为他用来保存ndarrays的代码。他的代码是:

class Binary(TimeArrStorage):
    def save(self, arr, pth):
        with open(pth, 'wb+') as fh:
            fh.write(b'{0:s} {1:d} {2:d}\n'.format(arr.dtype, *arr.shape))
            fh.write(arr.data)
            sync(fh)

    def load(self, pth):
        with open(pth, 'rb') as fh:
            dtype, w, h = str(fh.readline()).split()
            return frombuffer(fh.read(), dtype=dtype).reshape((int(w), int(h)))

我的具体问题是,传递给第一次调用fh.write的字符串的含义是什么?我假设前面的" b"意思是二进制,但是{0:s} {1:d} {2:d}怎么样,特别是因为格式后括号内只有两个参数。第二个问题是这个方法可以用于任何数据类型的ndarray吗?第三个问题是,我是否需要调用sync方法(方法是在github页面的顶部定义的)?最后一个问题是,我查看arr.data返回的内容,如果arr是一个ndarray,它基本上是数据开始的内存位置,那么这段代码如何知道它到达了反对它试图写?

1 个答案:

答案 0 :(得分:0)

我(来自另一个问题)

In [509]: arr
Out[509]: 
array([[-1.0856306 ,  0.99734545],
       [ 0.2829785 , -1.50629471],
       [-0.57860025,  1.65143654]])

我可以使用其属性格式化字符串:

In [510]: '%s %d %d'%(arr.dtype, *arr.shape)
Out[510]: 'float64 3 2'

示例中的格式在py3中给出了错误(在py2中它没问题):

In [500]: '{0:s} {1:d} {2:d}'.format(arr.dtype, *arr.shape)
...
TypeError: non-empty format string passed to object.__format__

没关系:

In [515]: '{0} {1} {2}'.format(arr.dtype, *arr.shape)
Out[515]: 'float64 3 2'

In [533]: '{0!s} {1:d} {2:d}'.format(arr.dtype, *arr.shape)
Out[533]: 'float64 3 2'

对于二维数组,arr.shape是一个2元素元组,*arr.shape扩展它。 SO有2d数组,有3个参数。

现在你提到它arr.data确实看起来很有趣。我怀疑它们是指整个数据缓冲区内容,但这个特定属性是地址,而不是内容。

正如我在评论中提到的,np.save基本上做同样的事情,初始块略大一些。如果这个代码有问题,坚持使用久经考验的np.save会更明智。

查看np.lib.npyio.format以查看完整的np.save代码。它写一个标题,然后用以下内容写入数据缓冲区:

array.tofile(fp)

np.load如果可以,则使用np.fromfile,但会回退到使用frombuffer

在Py2中,这有效:

>>> arr=np.ones((2,3))
>>> b'{0:s} {1:d} {2:d}'.format(arr.dtype, *arr.shape)
'float64 2 3'