【问题标题】:Creating a "bitmask" from several boolean numpy arrays从几个布尔 numpy 数组创建“位掩码”
【发布时间】:2017-02-06 07:46:42
【问题描述】:

我正在尝试使用 numpy 将几个掩码(布尔数组)转换为位掩码,虽然理论上可行,但我觉得我做了太多操作。

例如创建我使用的位掩码:

import numpy as np

flags = [
    np.array([True, False, False]),
    np.array([False, True, False]),
    np.array([False, True, False])
]

flag_bits = np.zeros(3, dtype=np.int8)
for idx, flag in enumerate(flags):
    flag_bits += flag.astype(np.int8) << idx  # equivalent to flag * 2 ** idx

这给了我预期的“位掩码”:

>>> flag_bits 
array([1, 6, 0], dtype=int8)

>>> [np.binary_repr(bit, width=7) for bit in flag_bits]
['0000001', '0000110', '0000000']

但是我觉得特别是对int8 的强制转换和flag_bits 数组的添加太复杂了。因此,我想问是否有任何我错过的 NumPy 功能可用于创建这样的“位掩码”数组?

注意:我正在调用一个需要这样一个位掩码的外部函数,否则我会坚持使用布尔数组。

【问题讨论】:

    标签: python numpy


    【解决方案1】:
    >>> x = np.array(2**i for i in range(1, np.shape(flags)[1]+1))
    >>> np.dot(flags, x)
    array([1, 2, 2])
    

    它是如何工作的:在位掩码中,每个位实际上是一个原始数组元素,根据其位置乘以 2 的度数,例如4 = False * 1 + True * 2 + False * 4。实际上,这可以表示为矩阵乘法,这在 numpy 中非常有效。

    所以,第一行是创建这些权重的列表推导:x = [1, 2, 4, 8, ... 2^(n+1)]。

    然后,flags 中的每一行乘以 x 中的相应元素,然后将所有内容相加(这就是矩阵乘法的工作原理)。最后,我们得到了位掩码

    【讨论】:

    • 好的,但是为什么结果是错误的?我想要[1, 6, 0],这给了我[1, 2, 4]
    • 两个原因:我使用不同的值进行测试,我按行总结,而问题按列进行。要按列,只需交换矩阵或转置标志
    • flags 必须在计算点积之前进行转置 (flags.T)。
    • 对于大的flags,我认为np.dot(x, flags)会比flags.T更有效率
    • @Marat 就用:np.dot(2**np.arange(3),flags),简单又甜美。
    【解决方案2】:

    这个怎么样(如果需要,添加到int8 的转换):

    flag_bits = (np.transpose(flags) << np.arange(len(flags))).sum(axis=1)\
                 .astype(np.int8)
    #array([1, 6, 0], dtype=int8)
    

    【讨论】:

    • 一个建议:代替np.arrayflags.T,您可以合二为一:(np.transpose(flags) &lt;&lt; np.arange(len(flags))).sum(axis=1)。但是我似乎无法强制 sum 返回一个 int8 数组。它总是使用int32
    • 如果将int8 转换为字符串,为什么还要关心它呢? (但尽管如此,我还是在答案中添加了转换)。
    • 字符串版本更多地是为了说明位掩码是什么——而不是预期的结果。我有点担心内存,我使用 6 个形状 (12000, 12000) 布尔数组,中间的 np.transpose(flags)&lt;&lt;np.arange(len(flags)) 数组对于 dtype int64 有点“大”(即使是 int32)。
    • 不过没关系,你的答案按预期工作!
    【解决方案3】:

    这是一种使用boolean-indexing 直接获取字符串位掩码的方法-

    out = np.repeat('0000000',3).astype('S7')
    out.view('S1').reshape(-1,7)[:,-3:] = np.asarray(flags).astype(int)[::-1].T
    

    示例运行 -

    In [41]: flags
    Out[41]: 
    [array([ True, False, False], dtype=bool),
     array([False,  True, False], dtype=bool),
     array([False,  True, False], dtype=bool)]
    
    In [42]: out = np.repeat('0000000',3).astype('S7')
    
    In [43]: out.view('S1').reshape(-1,7)[:,-3:] = np.asarray(flags).astype(int)[::-1].T
    
    In [44]: out
    Out[44]: 
    array([b'0000001', b'0000110', b'0000000'], 
          dtype='|S7')
    

    使用与@Marat's solution 中详细讨论的相同矩阵乘法策略,但使用为我们提供flag_bits 的矢量化缩放数组-

    np.dot(2**np.arange(3),flags)
    

    【讨论】:

    • 呵呵,我其实想要一个整数数组作为结果。字符串化更多的是显示位掩码代表什么(我不知道该术语是否是常识)。 :)
    • @MSeifert 啊,我明白了。好吧,我假设该字符串作为位掩码:)
    • 我怀疑不使用** 会更快:np.dot(1&lt;&lt;np.arange(3, dtype=np.int8),flags)
    猜你喜欢
    • 1970-01-01
    • 2018-10-02
    • 2021-03-25
    • 1970-01-01
    • 2018-05-27
    • 1970-01-01
    • 2021-07-01
    • 1970-01-01
    • 2019-11-04
    相关资源
    最近更新 更多