【问题标题】:Python/Numpy: Convert list of bools to unsigned intPython/Numpy:将布尔列表转换为无符号整数
【发布时间】:2011-05-03 05:14:27
【问题描述】:
  1. 最快(或最“Pythonic”)的转换方式是什么

    x = [False, False, True, True]
    

    进入12? (如果有这样的方法。)

  2. 如果 xnumpy.array 的布尔值会怎样?有什么特殊的命令吗?

我有一个大型的 m×n 布尔数组,其中每个 n 元素行代表一个高维特征向量的单个低维散列。 (在上面的示例中,n = 4。)我想知道答案,以便尽可能地压缩我的数据。谢谢。


编辑:感谢您的回复!使用下面的测试代码,

t = 0
for iter in range(500):
    B = scipy.signbit(scipy.randn(1000,20))
    for b in B:
        t0 = time.clock()
        # test code here
        t1 = time.clock()
        t += (t1-t0)
print t

...这是我的 Thinkpad 笔记本电脑上的运行时:

当然,我欢迎任何可以证实或反驳我的数据的独立测试!


编辑:在我下面的回答中,将int(j) 更改为简单的j 仍然有效,但运行速度慢了六倍!那么如果使用int 转换布尔值,其他答案可能会变得更快。但是我懒得再测试一切了。


编辑:liori 发布了独立测试的结果here

【问题讨论】:

  • [False, False, True, True] 转换成12的规则是什么?
  • x[0] 是 LSB,x[-1] 是 MSB。
  • 请使用timeit进行测试,这样更不容易出错。我的时代:pastebin.com/x1FEP9gY
  • 感谢您的测试!我一点也不怀疑他们。我已将它们添加到帖子中。
  • 需要注意的一点——在 liori 的测试中,sven2() 惨遭失败,因为我们使用的是 1000 位数字。检查结果(如每个函数返回的数字),你会发现它的结果对于这么大的数字是错误的。

标签: python list numpy scipy


【解决方案1】:

从其他各种答案中汲取各种想法,这是另一种方法:

sum(1<<i for i, b in enumerate(x) if b)

在我的测试中,它的速度非常快——即使它疯狂地溢出,也可以使用 numpy 方法处理大量位。我使用 liori 的测试模块进行测试。史蒂夫的方法,加上我建议的改变,只是快了一点点。但是,如果需要一次完成大量此类转换(并且位不太多),我敢打赌 numpy 会更快。

【讨论】:

  • sum(b&lt;&lt;i for i, b in enumerate(x))
  • @KennyTM。聪明,但我分析它原来的速度大约快 20%。这是迄今为止最快的。
【解决方案2】:

大多数 Pythonic 可能是这样的:

sum(2**i*b for i, b in enumerate(x))

很难说它是否也是最快的。

在 numpy 中我会使用

numpy.sum(2**numpy.arange(len(x))*x)

但这对于小数组 x 不会更快,并且对于大数组 x 不起作用,因为使用机器大小整数而不是 Python 的任意精度整数。

【讨论】:

  • 谢谢!对于某些数组大小,第二种解决方案效果很好,但对于另一些数组则效果不佳。
  • @Steve - numpy 解决方案的另一个优点是您可以避免遍历每一行。使用上面测试代码中的“B”数组:numpy.sum(2**numpy.arange(B.shape[1])*B, axis=1)。与遍历数组中的每一行相比,这应该会带来很大的加速......完整的 500x 循环在我的机器上执行不到一秒......
  • 由于 numpy 不能像 Python 那样处理大整数,因此您必须小心处理非常大的数字。如果有更大的数字,您可以通过在 arange() 中执行 dtype=numpy.longlong 来从该方法中获得更多收益。此外,使用生成的 numpy 数组的 sum 方法而不是使用 numpy.sum 有一个非常非常小的加速。
【解决方案3】:
reduce(lambda a,b:2*a+b, reversed(x))

如果数组末尾有最低有效位,则可以摆脱 reversed()。这也适用于 numpy.array,并且不需要 enumerate()。从我的测试来看,似乎也更快:不需要使用幂运算。

【讨论】:

  • 感谢您提供优雅的解决方案!当我第一次看到它时,我被震撼了。不幸的是,不管有没有reversed,它似乎运行得最慢。有人知道为什么吗?
  • @Steve:在我的电脑上它比 sum+exponentiation 快。有趣的事情......你使用多长时间的向量?您是否使用timeit 测试性能?
【解决方案4】:

我的初步尝试,仅供参考:

def bool2int(x):
    y = 0
    for i,j in enumerate(x):
        if j: y += int(j)<<i
    return y

【讨论】:

  • 等等,这很有趣:将int(j) 更改为简单的j 仍然有效,但运行速度慢了六倍!
  • 如果你把int(j)改为1,你的最快。
【解决方案5】:

这是一种优雅、pythonic、始终有效的方式:

def powers(x):
    """yield powers of x, starting from x**0 forever"""
    power = 1
    while True:
        yield power
        power *= x

def bools_to_int(bools):
    # in Python 2, use itertools.izip!
    return sum(int(place) * place_weight for place_weight, place in 
               zip(powers(2), bools))

请注意,您可以摆脱powers(通过在理解中枚举和平方,就像其他答案一样) - 但也许这样更清楚。

【讨论】:

  • 您的答案与其他人的答案不同。将bools 替换为reversed(bools) 可以修复它。
  • @Justin Peel:再来一次?我已经注意到在回答后不久并添加了reversed...
  • 用 OP 给出的例子试试你在这里的代码。当它应该是 12 时,我得到 3 作为答案。您不需要将 reversed 放入。
  • 头撞墙 @Justin:是的,你是对的,现在我明白为什么了。
【解决方案6】:

这样的?

>>> x = [False, False, True, True]
>>> sum([int(y[1])*2**y[0] for y in enumerate(x)])
12

您可以使用 list() 强制转换将 numpy 数组转换为常规列表。

>>> a = numpy.array([1,2,3,4])
>>> a
array([1, 2, 3, 4])
>>> list(a)
[1, 2, 3, 4]

【讨论】:

  • 0**0 是 1,所以如果第一个元素是 False,你会得到一个错误的错误。
  • @liori,我认为这不适用于我的代码,因为我实际上并没有在任何地方这样做?不过还是很有趣。不知道。
  • int(False)*2==0enumerate 给出的第一个索引是 0
  • @liori,是的,但我不会把它的价值提升到任何程度。我的代码是 i * 2^j。对于第一位 i * 2^0 = i*1 = i
  • 好吧,真丢脸。我搞砸了优先规则:-)。
【解决方案7】:

如果你有一个矩阵,你可能想这样做:

#precompute powers of two
vals = 2.**np.arange(20)

B = ....
compressed = np.dot(B, vals) # matrix multiplication.

np.dot 应该比 Python 中的任何循环都快。更快。

【讨论】:

    【解决方案8】:

    我正在尝试ipython %timeit,似乎执行以下操作更快:

    y = 0
    for i,j in enumerate(x):
        if j: y += 1<<i
    

    此外,如果您的布尔向量是一个 numpy.ndarray,那么在这种情况下,将其转换为 python 数组 x.tolist() 并运行它似乎会更快。这一切都是边际的,但始终如一,在这些速度下,边际加起来很好。

    【讨论】:

      【解决方案9】:

      numpy 对此有 packbits 函数。 它还支持沿轴的操作:

      In [3]: B = scipy.signbit(scipy.randn(1000,8)).astype("i1")
      
      In [3]: B[0]
      Out[3]: array([0, 1, 0, 0, 0, 1, 0, 0], dtype=int8)
      
      In [4]: np.packbits(B[0])
      Out[4]: array([68], dtype=uint8)
      
      In [5]: %timeit np.packbits(B, axis=1)
      10000 loops, best of 3: 37 µs per loop
      

      它适用于 int8 尺寸,对于较大的尺寸,您必须移动和或

      In [8]: x # multiple of 8
      Out[8]: array([1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1], dtype=int8)
      
      In [9]: r = np.packbits(x).astype(np.int32); r
      Out[9]: array([171, 129], dtype=uint8)
      
      In [10]: r[0] << 8 | r[1] 
      Out[10]: 33237
      
      In [11]: sum(1<<i for i, b in enumerate(x[::-1]) if b)
      Out[11]: 33237
      

      如果x 不是 8 的倍数,则必须填充零

      【讨论】:

        【解决方案10】:

        如果您愿意添加另一个扩展,我将 pack() 和 unpack() 添加到 gmpy 的开发分支。我的测试表明它可能快 2 倍或 3 倍。

        >>> import gmpy2
        >>> gmpy2.pack([0,0,1,1],1)
        mpz(12)
        >>> gmpy2.unpack(12,1)
        [mpz(0), mpz(0), mpz(1), mpz(1)]
        

        免责声明:开发版本称为gmpy2,可以与稳定版本共存。它仍处于 alpha 阶段,但有望在几周内成为 beta。您需要同时安装 GMP 和 MPFR 库。源代码在http://code.google.com/p/gmpy/source/checkout

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2016-04-14
          • 1970-01-01
          • 2020-06-26
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-05-24
          相关资源
          最近更新 更多