Python / Numpy:将bool列表转换为unsigned int

时间:2010-10-31 23:28:07

标签: python list numpy scipy

  1. 转化的最快(或最“Pythonic”)方式是什么

    x = [False, False, True, True]
    

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

  2. 如果x而不是numpy.array个布尔怎么办?是否有特殊命令?

  3. 我有一个大的m-by-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投射bool,其他答案可能会更快。但我懒得再测试一切。


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

10 个答案:

答案 0 :(得分:10)

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

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

在我的测试中速度非常快 - 即使它像疯了一样溢出,也可以使用numpy方法获取大量的数据。我使用liori的测试模块进行测试。史蒂夫的方法,我建议的改变,只是快几点。但是,如果需要一次完成很多这类转换(并且没有太多位),我认为numpy会更快。

答案 1 :(得分:6)

大多数Pythonic可能就是这样:

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

很难判断它是否也是最快的。

在numpy我会用

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

但对于小型数组x来说这不会更快,并且它不适用于大型数组x,因为使用机器大小整数而不是Pythons任意精度整数。

答案 2 :(得分:3)

reduce(lambda a,b:2*a+b, reversed(x))

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

答案 3 :(得分:2)

这是一种优雅,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(通过枚举和平方理解,就像其他答案一样) - 但也许这样更清楚。

答案 4 :(得分:2)

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

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

答案 5 :(得分:1)

这样的东西?

>>> 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]

答案 6 :(得分:1)

如果您有矩阵,您可能希望这样做:

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

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

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

答案 7 :(得分:1)

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

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

此外,如果您的布尔向量是numpy.ndarray,将其转换为python数组x.tolist()并运行相同的工具似乎在这种情况下更快。这一切都是微不足道的,但是在这些速度下,边际加起来也很好。

答案 8 :(得分:1)

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的倍数,则必须用零填充

答案 9 :(得分:0)

如果您愿意为混音添加另一个扩展名,我将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