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

How to make an int8 python numpy array appear as bool to the user

提问人:astabada 提问时间:8/23/2023 更新时间:8/24/2023 访问量:73

问:

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

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

如果 mask 是 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]

但是实现失败,因为总是返回与 不同的实例。所以,作为替代方案,我尝试了maskself['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]
python 数组 numpy 转换

评论

1赞 simon 8/23/2023
为什么首先需要两个数组(一个布尔数组,一个整数数组)?请注意,Numpy 中的所有布尔数组在内部都是 int8(或者更确切地说是 uint8)数组,所以我要做的是:(1) 将我的掩码存储为您需要的 int8 数组 (like ) 和 (2) 使用它的布尔视图进行掩码 (like )。这样,您就不需要使两个阵列保持同步。此外,您不会浪费额外的内存,因为视图不会复制原始的 int8 数据。或者也许我误解了你的问题?mask = np.array((1, 0, 0, 1), dtype=np.int8)np.arange(4, 8)[mask.view(dtype=bool)]
0赞 astabada 8/23/2023
@simon,感谢您的回复。我同意你所说的一切,但我有“浪费内存”的外部约束:-/,以避免用户犯错误,用int掩码索引数组
0赞 simon 8/23/2023
我明白了,这是有道理的。但是您仍然可以使用数组视图而不是使用 ?通过这种方式,您可以获得单独的数组实例(在 bool 和 int8 的情况下,甚至是不同类型的实例),它们仍然可以访问相同的内存——这意味着,如果一个数组中的值发生变化,其他数组中的值也会发生变化。我真的不了解你的工作流程,所以我不能告诉你我的意见,最好在哪里实施更改,但也许可以考虑一下。astype
0赞 astabada 8/24/2023
嘿@simon,我认为你是对的,我只需要更改第一个示例即可返回,它就可以了......如果你好心地把这个写成答案,我会在两天后再次检查并接受它!self['mask'].view(dtype=bool)
0赞 simon 8/24/2023
很高兴听到这个消息。让我看看我是否能找到时间:D无论如何:很高兴我能帮上忙

答:

1赞 simon 8/24/2023 #1

在该特定设置中,我将直接利用 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 的 、 和 。如果我们想确保在更改或(或者,同样,更改为不更改和 ),我们可以像以前一样在 setter 中使用。inmaskobj['mask']obj.maskinmaskobj.maskobj['mask']inmaskobj.maskobj['mask']astype()