首页 > 解决方案 > 将 numpy ndarray 的子类存储在磁盘上后保存它

问题描述

假设我有一个array从类子类化的实例np.ndarray

class RealisticInfoArray(np.ndarray):
    def __new__(cls, input_array, info=None):
        obj = np.asarray(input_array).view(cls)
        obj.info = info
        return obj

    def __array_finalize__(self, obj):
        if obj is None: return
        self.info = getattr(obj, 'info', None)

    def __reduce__(self):
        print('in reduce')
        # Get the parent's __reduce__ tuple
        pickled_state = super(RealisticInfoArray, self).__reduce__()
        # Create our own tuple to pass to __setstate__
        new_state = pickled_state[2] + (self.info,)
        # Return a tuple that replaces the parent's __setstate__ tuple with our own
        return (pickled_state[0], pickled_state[1], new_state)

    def __setstate__(self, state):
        print('in set_state')
        self.info = state[-1]  # Set the info attribute
        # Call the parent's __setstate__ with the other tuple elements.
        super(RealisticInfoArray, self).__setstate__(state[0:-1])
        
    def tofile(self, fid, sep="", format="%s"):
        super().tofile(fid, sep, format)
        print('in tofile')
        
    def tobytes(self, order='C'):
        super().tobytes(order)
        print('in tobytes')

array = RealisticInfoArray(np.zeros((7, 9, 13)), info='tester')

方法__reduce__, __setstate__,tofiletobytes被包括在内,因为我认为它们参与了我想要执行的保存:我想将数组存储在磁盘上(通过任何np.save, np.savez, np.savez_compressed)并在保留该对象的类的同时将其加载回来以及所有的自定义属性。

我已经尝试过另一个 SO question的方法,但这不起作用,因为我想使用np函数,而不是pickleor dill。另外,我从那里借用了 MWE 的子类。

另一点信息是实际保存由 执行np.lib.npyio.format.write_array,这似乎不允许存储数据的任何自定义行为。

所以,我的问题是是否可以保留存储数组的类,如果可以,怎么做?

标签: pythonarraysnumpysave

解决方案


现有的子类做什么? np.matrix,np.recarraynp.ma.MaskedArray. 我没有测试他们save或查看他们是否有特殊代码。 matrix具有相同的属性和数据缓冲区,只是限制维度的方法。掩码数组同时具有datamask数组属性。 recarray就像一个具有不同索引方法的结构化数组。

scipy.sparse有自己的save,savez用来保存自己的属性。这也需要它自己的负载,以重新创建矩阵。 sparse虽然不是子类。

以后我可能会做一些探索自己。

但不要对pickle. np.save和之间有着密切的联系picklenp.savendarray,while的 pickle 方法,save用于pickle序列化对象元素。

不保留矩阵子类:

In [52]: np.save('matrix.npy',M)
In [53]: np.load('matrix.npy')
Out[53]: 
array([[1, 2],
       [3, 4]])

pickle保留子类(省略演示)。

np.save没有为掩码数组实现:

In [66]: ma = np.ma.masked_array(np.ones(4))
In [67]: ma
Out[67]: 
masked_array(data=[1., 1., 1., 1.],
             mask=False,
       fill_value=1e+20)
In [68]: np.save('masked.npy',ma)
Traceback (most recent call last):
  File "<ipython-input-68-3c39e0b0fc22>", line 1, in <module>
    np.save('masked.npy',ma)
  File "<__array_function__ internals>", line 6, in save
  File "/usr/local/lib/python3.6/dist-packages/numpy/lib/npyio.py", line 529, in save
    pickle_kwargs=dict(fix_imports=fix_imports))
  File "/usr/local/lib/python3.6/dist-packages/numpy/lib/format.py", line 675, in write_array
    array.tofile(fp)
  File "/usr/local/lib/python3.6/dist-packages/numpy/ma/core.py", line 6116, in tofile
    raise NotImplementedError("MaskedArray.tofile() not implemented yet.")
NotImplementedError: MaskedArray.tofile() not implemented yet.

再次泡菜工作:

In [69]: with open('masked.pkl','wb') as f:
    ...:     pickle.dump(ma,f)
    ...: 
In [70]: with open('masked.pkl','rb') as f:
    ...:     pp=pickle.load(f)
    ...: 
In [71]: pp
Out[71]: 
masked_array(data=[1.0, 1.0, 1.0, 1.0],
             mask=[False, False, False, False],
       fill_value=1e+20)

推荐阅读