【问题标题】:Convert binary (0|1) numpy to integer or binary-string?将二进制(0 | 1)numpy转换为整数或二进制字符串?
【发布时间】:2017-04-25 11:41:55
【问题描述】:

是否有将二进制 (0|1) numpy 数组转换为整数或二进制字符串的快捷方式? F.e.

b = np.array([0,0,0,0,0,1,0,1])   
  => b is 5

np.packbits(b)

有效,但仅适用于 8 位值..如果 numpy 是 9 个或更多元素,它会生成 2 个或更多 8 位值。 另一种选择是返回一个字符串 0|1 ...

我现在做的是:

    ba = bitarray()
    ba.pack(b.astype(np.bool).tostring())
    #convert from bitarray 0|1 to integer
    result = int( ba.to01(), 2 )

太丑了!!!

【问题讨论】:

    标签: python numpy binary bitstring


    【解决方案1】:

    一种方法是将dot-product 与2-powered 范围数组一起使用-

    b.dot(2**np.arange(b.size)[::-1])
    

    示例运行 -

    In [95]: b = np.array([1,0,1,0,0,0,0,0,1,0,1])
    
    In [96]: b.dot(2**np.arange(b.size)[::-1])
    Out[96]: 1285
    

    或者,我们可以使用按位左移运算符来创建范围数组,从而获得所需的输出,就像这样 -

    b.dot(1 << np.arange(b.size)[::-1])
    

    如果对时间感兴趣 -

    In [148]: b = np.random.randint(0,2,(50))
    
    In [149]: %timeit b.dot(2**np.arange(b.size)[::-1])
    100000 loops, best of 3: 13.1 µs per loop
    
    In [150]: %timeit b.dot(1 << np.arange(b.size)[::-1])
    100000 loops, best of 3: 7.92 µs per loop
    

    逆向处理

    要取回二进制数组,请使用 np.binary_repr 和 np.fromstring -

    In [96]: b = np.array([1,0,1,0,0,0,0,0,1,0,1])
    
    In [97]: num = b.dot(2**np.arange(b.size)[::-1]) # integer
    
    In [98]: np.fromstring(np.binary_repr(num), dtype='S1').astype(int)
    Out[98]: array([1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1])
    

    【讨论】:

    • 聪明 :) .... 如果有人能想出更好的方法,我会等一两天.. 在我批准之前。
    • 对转换回原始二进制数组格式有什么建议吗?
    • @Jonathan 好问题!早该加的。已发布。
    • @Divakar 太棒了,谢谢!我还在其中添加了一个“zfill”以确保它保持相同的长度,因此: np.fromstring(np.binary_repr(num).zfill(b.size), dtype='S1').astype(int)
    • @Jonathan 这是保持长度的好建议。谢谢。
    【解决方案2】:

    我通过使用矢量化矩阵乘法代码扩展了 @Divikar 的良好点积解决方案,使其在我的主机上运行速度提高了约 180 倍。一次运行一行的原始代码在我的 pandas 数据框中运行 18 列的 10 万行需要大约 3 分钟。好吧,下周我需要从 100K 行升级到 20M 行,所以大约 10 小时的运行时间对我来说不够快。首先,新代码是矢量化的。这是python代码的真正变化。其次,matmult 通常在您看不到它的情况下在多核处理器上并行运行,具体取决于您的主机配置,尤其是当 OpenBLAS 或其他 BLAS 存在以供 numpy 用于像 matmult 这样的矩阵代数时。所以它可以使用很多处理器和内核,如果你有的话。

    新的——非常简单——代码在我的主机上运行 100K 行 x 18 个二进制列,大约 1 秒 ET,这对我来说是“任务完成”:

    '''
    Fast way is vectorized matmult. Pass in all rows and cols in one shot.
    '''
    def BitsToIntAFast(bits):
      m,n = bits.shape # number of columns is needed, not bits.size
      a = 2**np.arange(n)[::-1]  # -1 reverses array of powers of 2 of same length as bits
      return bits @ a  # this matmult is the key line of code
    
    '''I use it like this:'''
    bits = d.iloc[:,4:(4+18)] # read bits from my pandas dataframe
    gs = BitsToIntAFast(bits)
    print(gs[:5])
    gs.shape
    ...
    d['genre'] = np.array(gs)  # add the newly computed column to pandas
    

    希望这会有所帮助。

    【讨论】:

      【解决方案3】:

      我的时间结果:

      b.dot(2**np.arange(b.size)[::-1])
      100000 loops, best of 3: 2.48 usec per loop
      
      b.dot(1 << np.arange(b.size)[::-1])
      100000 loops, best of 3: 2.24 usec per loop
      
      # Precompute powers-of-2 array with a = 1 << np.arange(b.size)[::-1]
      b.dot(a)
      100000 loops, best of 3: 0.553 usec per loop
      
      # using gmpy2 is slower
      gmpy2.pack(list(map(int,b[::-1])), 1)
      100000 loops, best of 3: 10.6 usec per loop
      

      因此,如果您提前知道大小,则预先计算 2 的幂数组会明显更快。但如果可能的话,您应该像 Geoffrey Anderson 的回答那样使用矩阵乘法同时进行所有计算。

      【讨论】:

        【解决方案4】:

        使用 numpy 进行转换会将您限制为 64 位有符号二进制结果。如果您真的想使用 numpy 并且 64 位限制对您有用,那么使用 numpy 的更快实现是:

        import numpy as np
        def bin2int(bits):
            return np.right_shift(np.packbits(bits, -1), bits.size).squeeze()
        

        因为通常如果您使用 numpy 您关心速度,那么 > 64 位结果的最快实现是:

        import gmpy2
        def bin2int(bits):
            return gmpy2.pack(list(bits[::-1]), 1)
        

        如果你不想获取对 gmpy2 的依赖,这会稍微慢一些,但没有依赖并支持 > 64 位结果:

        def bin2int(bits):
            total = 0
            for shift, j in enumerate(bits[::-1]):
                if j:
                    total += 1 << shift
            return total
        

        观察者会注意到上一个版本与此问题的其他答案有一些相似之处,主要区别在于使用

        【讨论】:

        • numpy 支持无符号的 64 位 dtype,这样可以吗?
        • 这真的更快吗?我的时间显示使用 gmpy 慢了大约 4 倍。 gmpy 也有一个奇怪的错误包github.com/aleaxit/gmpy/issues/129
        【解决方案5】:
        def binary_converter(arr):
            total = 0
            for index, val in enumerate(reversed(arr)):
                total += (val * 2**index)
            print total
        
        
        In [14]: b = np.array([1,0,1,0,0,0,0,0,1,0,1])
        In [15]: binary_converter(b)
        1285
        In [9]: b = np.array([0,0,0,0,0,1,0,1])
        In [10]: binary_converter(b)
        5
        

        或

        b = np.array([1,0,1,0,0,0,0,0,1,0,1])
        sum(val * 2**index for index, val in enumerate(reversed(b)))
        

        【讨论】:

          猜你喜欢
          • 2012-10-05
          • 2021-02-24
          • 1970-01-01
          • 2012-02-27
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多