【问题标题】:Using numpy. binary_repr on array of numbers or alternatives - Python使用 numpy. binary_repr 上的数字或替代数组 - Python
【发布时间】:2019-09-08 15:36:10
【问题描述】:

使用以下代码我试图将数字列表转换为二进制数,但出现错误

import numpy as np

lis=np.array([1,2,3,4,5,6,7,8,9])
a=np.binary_repr(lis,width=32)

运行程序后的错误是

Traceback(最近一次调用最后一次):

文件“”,第 4 行,在 a=np.binary_repr(lis,width=32)

文件 "C:\Users.......", 在 binary_repr 如果 num == 0:

ValueError:具有多个元素的数组的真值是 模糊的。使用 a.any() 或 a.all()

有什么办法解决这个问题?

【问题讨论】:

  • np.binary_repr 适用于单个值,而不适用于类似数组的对象。
  • 我知道。但是有什么办法让它在数组中工作..?
  • 或者python中是否有任何代码可以将我的整个数组或列表转换为二进制32位?
  • 您要获取字符串输出还是其他内容?

标签: python numpy binary


【解决方案1】:

您可以使用np.vectorize 来解决此问题。

>>> lis=np.array([1,2,3,4,5,6,7,8,9])
>>> a=np.binary_repr(lis,width=32)
>>> binary_repr_vec = np.vectorize(np.binary_repr)
>>> binary_repr_vec(lis, width=32)
array(['00000000000000000000000000000001',
       '00000000000000000000000000000010',
       '00000000000000000000000000000011',
       '00000000000000000000000000000100',
       '00000000000000000000000000000101',
       '00000000000000000000000000000110',
       '00000000000000000000000000000111',
       '00000000000000000000000000001000',
       '00000000000000000000000000001001'], dtype='<U32')

【讨论】:

    【解决方案2】:

    方法#1

    这是一个数字数组的矢量化数组,利用 broadcasting -

    def binary_repr_ar(A, W):
        p = (((A[:,None] & (1 << np.arange(W-1,-1,-1)))!=0)).view('u1')
        return p.astype('S1').view('S'+str(W)).ravel()
    

    示例运行 -

    In [67]: A
    Out[67]: array([1, 2, 3, 4, 5, 6, 7, 8, 9])
    
    In [68]: binary_repr_ar(A,32)
    Out[68]: 
    array(['00000000000000000000000000000001',
           '00000000000000000000000000000010',
           '00000000000000000000000000000011',
           '00000000000000000000000000000100',
           '00000000000000000000000000000101',
           '00000000000000000000000000000110',
           '00000000000000000000000000000111',
           '00000000000000000000000000001000',
           '00000000000000000000000000001001'], dtype='|S32')
    

    方法#2

    另一个带有数组赋值的向量化 -

    def binary_repr_ar_v2(A, W):
        mask = (((A[:,None] & (1 << np.arange(W-1,-1,-1)))!=0))
        out = np.full((len(A),W),48, dtype=np.uint8)
        out[mask] = 49
        return out.view('S'+str(W)).ravel()
    

    或者,直接使用掩码获取字符串数组-

    def binary_repr_ar_v3(A, W):
        mask = (((A[:,None] & (1 << np.arange(W-1,-1,-1)))!=0))
        return (mask+np.array([48],dtype=np.uint8)).view('S'+str(W)).ravel()
    

    请注意,最终输出将是中间输出之一的视图。因此,如果您需要它拥有自己的内存空间,只需附加 .copy()


    大型输入数组的计时 -

    In [49]: np.random.seed(0)
        ...: A = np.random.randint(1,1000,(100000))
        ...: W = 32
    
    In [50]: %timeit binary_repr_ar(A, W)
        ...: %timeit binary_repr_ar_v2(A, W)
        ...: %timeit binary_repr_ar_v3(A, W)
    1 loop, best of 3: 854 ms per loop
    100 loops, best of 3: 14.5 ms per loop
    100 loops, best of 3: 7.33 ms per loop
    

    来自其他已发布的解决方案 -

    In [22]: %timeit [np.binary_repr(i, width=32) for i in A]
    10 loops, best of 3: 97.2 ms per loop
    
    In [23]: %timeit np.frompyfunc(np.binary_repr,2,1)(A,32).astype('U32')
    10 loops, best of 3: 80 ms per loop
    
    In [24]: %timeit np.vectorize(np.binary_repr)(A, 32)
    10 loops, best of 3: 69.8 ms per loop
    

    开启@Paul Panzer's solutions -

    In [5]: %timeit bin_rep(A,32)
    548 µs ± 1.02 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
    
    In [6]: %timeit bin_rep(A,31)
    2.2 ms ± 5.55 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
    

    【讨论】:

    • 感谢时间。
    【解决方案3】:

    正如documentation on binary_repr 所说:

    num : int

        只能是十进制整数 用过。

    但是您可以矢量化这个操作,例如:

    np.vectorize(np.binary_repr)(lis, 32)
    

    这给了我们:

    >>> np.vectorize(np.binary_repr)(lis, 32)
    array(['00000000000000000000000000000001',
           '00000000000000000000000000000010',
           '00000000000000000000000000000011',
           '00000000000000000000000000000100',
           '00000000000000000000000000000101',
           '00000000000000000000000000000110',
           '00000000000000000000000000000111',
           '00000000000000000000000000001000',
           '00000000000000000000000000001001'], dtype='<U32')
    

    或者如果您经常需要,您可以将矢量化变体存储在变量中:

    binary_repr_vector = np.vectorize(np.binary_repr)
    binary_repr_vector(lis, 32)
    

    当然会给出相同的结果:

    >>> binary_repr_vector = np.vectorize(np.binary_repr)
    >>> binary_repr_vector(lis, 32)
    array(['00000000000000000000000000000001',
           '00000000000000000000000000000010',
           '00000000000000000000000000000011',
           '00000000000000000000000000000100',
           '00000000000000000000000000000101',
           '00000000000000000000000000000110',
           '00000000000000000000000000000111',
           '00000000000000000000000000001000',
           '00000000000000000000000000001001'], dtype='<U32')
    

    【讨论】:

      【解决方案4】:

      这是使用np.unpackbits的快速方法

      (np.unpackbits(lis.astype('>u4').view(np.uint8))+ord('0')).view('S32')
      # array([b'00000000000000000000000000000001',
      #        b'00000000000000000000000000000010',
      #        b'00000000000000000000000000000011',
      #        b'00000000000000000000000000000100',
      #        b'00000000000000000000000000000101',
      #        b'00000000000000000000000000000110',
      #        b'00000000000000000000000000000111',
      #        b'00000000000000000000000000001000',
      #        b'00000000000000000000000000001001'], dtype='|S32')
      

      更笼统的:

      def bin_rep(A,n):
          if n in (8,16,32,64):
              return (np.unpackbits(A.astype(f'>u{n>>3}').view(np.uint8))+ord('0')).view(f'S{n}')
          nb = max((n-1).bit_length()-3,0)
          return (np.unpackbits(A.astype(f'>u{1<<nb}')[...,None].view(np.uint8),axis=1)[...,-n:]+ord('0')).ravel().view(f'S{n}')
      

      注意:特殊的大小写 n = 8,16,32,64 绝对值得,因为它为这些数字提供了几倍的加速。

      另请注意,此方法的最大值为 2^64,较大的整数需要不同的方法。

      【讨论】:

      • 它可以推广到通用窗口长度吗?数值范围有限制吗?
      • @Divakar 嗯,它有一些工作;-) 显然超出 uint64 它变得毛茸茸的。
      • 看起来很有前途,显然是一个聪明的人把这些放在一边。
      • @Divakar 我添加了一个通用版本。不过,没有尝试处理 >= 2^64 的整数。对于未对齐的尺寸,它会慢一些,但仍然非常快。
      【解决方案5】:
      In [193]: alist = [1,2,3,4,5,6,7,8,9]                                                                        
      

      np.vectorize 方便,但不快:

      In [194]: np.vectorize(np.binary_repr)(alist, 32)                                                            
      Out[194]: 
      array(['00000000000000000000000000000001',
             '00000000000000000000000000000010',
             '00000000000000000000000000000011',
              ....
             '00000000000000000000000000001001'], dtype='<U32')
      In [195]: timeit np.vectorize(np.binary_repr)(alist, 32)                                                     
      71.8 µs ± 1.88 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
      

      简单的旧列表理解更好:

      In [196]: [np.binary_repr(i, width=32) for i in alist]                                                       
      Out[196]: 
      ['00000000000000000000000000000001',
       '00000000000000000000000000000010',
       '00000000000000000000000000000011',
      ...
       '00000000000000000000000000001001']
      In [197]: timeit [np.binary_repr(i, width=32) for i in alist]                                                
      11.5 µs ± 181 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
      

      另一个迭代器:

      In [200]: timeit np.frompyfunc(np.binary_repr,2,1)(alist,32).astype('U32')                                   
      30.1 µs ± 1.79 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
      

      【讨论】:

      • 对于确实如此的小数组,对于较大的数组,如lis = np.arange(1000),人们会发现列表解析通常需要两倍的时间。 frompyfunc 需要多花大约 10% 的时间。这并不奇怪,因为 numpy 对于少量数据绝对不是一个好主意,只有在“批量”处理数据时才会有回报。
      猜你喜欢
      • 2017-02-02
      • 2013-01-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-02-02
      • 1970-01-01
      • 2017-11-05
      • 1970-01-01
      相关资源
      最近更新 更多