如何使int8 python numpy数组对用户显示为bool

ecr0jaav  于 2023-10-19  发布在  Python
关注(0)|答案(1)|浏览(131)

需要存储一个bool numpy数组,但它必须与旧规范(astropy.io.fits.ImageHDU)兼容,该规范只能存储int8(其他类型也是可能的,但int8是最小的内存占用)。关键问题是,用户想要做的是,

mask = np.array((True, False, False, True))
print(np.arange(4, 8)[mask])
[4, 7]

如果掩码为int,则给予完全不同的结果

[5, 4, 4, 5]

这里有两个(错误的)实现,可以给予我所需要的概念

import numpy as np
class MyClass(dict):
     @property
     def mask(self):
         return self['mask'].astype(bool)
     @mask.setter
     def mask(self, inmask):
         self['mask'] = inmask.astype(np.int8)
input_mask = np.array((0, 1), dtype=np.int8)
obj = MyClass((('mask', input_mask),))

所需的行为应该使obj.maskobj['mask']始终同步,即

print(obj.mask)
[False, True]
print(obj['mask'])
[0, 1]
obj['mask'][0] = 1
print(obj.mask)
[True, True]
obj.mask[0] = False
print(obj.mask)
[False, True]
print(obj['mask'])
[0, 1]

但是实现失败了,因为mask总是返回与self['mask']不同的示例。所以,作为替代,我试着

import numpy as np
class MyClass(dict):
     @property
     def mask(self):
         try:
             return self._mask
         except AttributeError:
             self._mask = self['mask'].astype(bool)
             return self._mask
     @mask.setter
     def mask(self, inmask):
         self._mask = inmask
         self['mask'] = inmask.astype(np.int8)
input_mask = np.array((0, 1), dtype=np.int8)
obj = MyClass((('mask', input_mask),))

此操作失败,因为self._maskself['mask']不同步

obj.mask[0] = True
print(obj.mask, ' - ', obj['maks'])
[True True] - [0 1]
rn0zuynd

rn0zuynd1#

在这个特定的设置中,我会直接利用Numpy中布尔数组的值是用字节处理的这一事实(即,8位值)。我不完全确定这是否被认为是一个实现细节,但到目前为止,它对我很有效。
这意味着我们可以使用Numpy的ndarray.view()来创建掩码内存的两个表示:布尔表示(用于实际屏蔽)和int8表示(用于规范一致性)。由于这两种表示都引用同一个内存,这意味着它们已经保持同步,不需要我们做进一步的工作。
调整你第一次尝试的实现,我们可以这样写:

import numpy as np

class MyClass(dict):
    
    @property
    def mask(self):
        return self['mask'].view(bool)

    @mask.setter
    def mask(self, inmask):
        assert inmask.dtype in (np.uint8, np.int8, bool)
        self['mask'] = inmask.view(np.int8)

这将给我们给予预期的行为:

input_mask = np.array((0, 1), dtype=np.int8)
obj = MyClass((('mask', input_mask),))

# Check behavior
print(obj.mask)
# >>> [False  True]
print(obj['mask'])
# >>> [0 1]
obj['mask'][0] = 1
print(obj.mask)
# >>> [ True  True]
obj.mask[0] = False
print(obj.mask)
# >>> [False  True]
print(obj['mask'])
# >>> [0 1]

注意,在这个实现中,我们实际上有 * 三个 * 相同内存的表示:进入setter的inmaskobj['mask']obj.mask。如果我们想确保在改变obj.maskobj['mask']inmask不会改变(或者,同样地,对inmask的改变不会改变obj.maskobj['mask']),我们可以像以前一样在setter中使用astype()

相关问题