将十进制范围转换为Numpy数组,每个位都是一个数组元素

时间:2014-02-20 20:02:04

标签: python numpy

我创建了一个小函数,它将整数length作为输入,并返回所有numpy整数的二进制表示的array 2**length范围[0:2**length-1]

import numpy as np

def get_bitstrings(length):
  # We need to binary-fy 2^length numbers.
  iterations = 2**length
  # Pre-allocate memory.
  the_array = np.zeros((iterations, length))
  # Go through all decimals in the range [0:iterations-1]
  for num in range(iterations):
    # Get binary representation in string format with 'length' zeroes padded
    t_string = '{f_num:0{f_width}b}'.format(f_num=num, f_width=length)
    # Convert to a Python list
    t_list   = list(t_string)
    # Convert to Numpy array and store.
    the_array[num,:] = np.array(t_list)

  return the_array

if __name__ == '__main__':
  var1 = get_bitstrings(2)
  var2 = get_bitstrings(3)
  print('var1:\n{}\n'.format(var1))
  print('var2:\n{}\n'.format(var2))

产生:

var1:
[[ 0.  0.]
 [ 0.  1.]
 [ 1.  0.]
 [ 1.  1.]]

var2:
[[ 0.  0.  0.]
 [ 0.  0.  1.]
 [ 0.  1.  0.]
 [ 0.  1.  1.]
 [ 1.  0.  0.]
 [ 1.  0.  1.]
 [ 1.  1.  0.]
 [ 1.  1.  1.]]

该过程包括将每个整数的二进制表示形式作为字符串(在它之前填充0,使得长度在length处保持不变),将字符串转换为Python列表,然后转换列表进入numpy array

我发现这是满足每个位是数组中一个条目的要求的唯一方法 - 即,bitstring 10101x4 numpy array而不仅仅是1x1数组中的整数。但我确信有更好的选择,因此问题。

你可以想象,问题在于效率低下。我想知道我是否可以通过使用Python / Numpy技巧来改进它。

修改的: 我以前在MATLAB中用这个片段做了这个:

  

t_length = 5;   dc = [0:2 ^ t_length-1]&#39 ;;   bc = rem(floor(dc * pow2( - (t_length-1):0)),2);

但是对于Python / Numpy来说,我是一个完整的菜鸟!也许它会激励某人。 :-)

2 个答案:

答案 0 :(得分:4)

你可以使用NumPy的广播和矢量化操作来相当有效地完成这项工作:

>>> from numpy import arange, newaxis
>>> powers_of_two = 2**arange(4)[::-1]
>>> (arange(2**4)[:, newaxis] & powers_of_two) / powers_of_two
array([[0, 0, 0, 0],
       [0, 0, 0, 1],
       [0, 0, 1, 0],
       [0, 0, 1, 1],
       [0, 1, 0, 0],
       [0, 1, 0, 1],
       [0, 1, 1, 0],
       [0, 1, 1, 1],
       [1, 0, 0, 0],
       [1, 0, 0, 1],
       [1, 0, 1, 0],
       [1, 0, 1, 1],
       [1, 1, 0, 0],
       [1, 1, 0, 1],
       [1, 1, 1, 0],
       [1, 1, 1, 1]])

简要说明:我们将所有整数从0到15(arange(2**4)),然后重新整形以给出一个形状(16, 1)的数组(这是[:, newaxis]切片部分) 。然后我们采用按位 - 并且使用2的幂,从最高到最低(2**arange(4)[::-1])。重新整形确保按位和操作作为一种“外部”操作执行:我们采用原位arange的按位和每个元素与powers_of_two数组的每个元素。这是NumPy的broadcastingslicing正在进行中。缺少明确的Python级for循环应该比基于for循环或列表推导的解决方案明显更快。

这里有点光滑,事实证明,更快,替代沿着相同的路线:

>>> from numpy import arange, newaxis
>>> arange(2**4)[:,newaxis] >> arange(4)[::-1] & 1
array([[0, 0, 0, 0],
       [0, 0, 0, 1],
       [0, 0, 1, 0],
       [0, 0, 1, 1],
       [0, 1, 0, 0],
       [0, 1, 0, 1],
       [0, 1, 1, 0],
       [0, 1, 1, 1],
       [1, 0, 0, 0],
       [1, 0, 0, 1],
       [1, 0, 1, 0],
       [1, 0, 1, 1],
       [1, 1, 0, 0],
       [1, 1, 0, 1],
       [1, 1, 1, 0],
       [1, 1, 1, 1]])

与往常一样,如果效率是一个问题,那么您应该充分利用Python以timeitprofile模块的形式提供的工具。使用length=16的机器上的计时似乎表明第二个变体明显快于第一个:

taniyama:~ mdickinson$ python -m timeit -s "from numpy import arange, newaxis" "arange(1<<16)[:, newaxis] >> arange(16)[::-1] & 1"
100 loops, best of 3: 4.08 msec per loop
taniyama:~ mdickinson$ python -m timeit -s "from numpy import arange, newaxis" "(arange(1<<16)[:, newaxis] & 2**arange(16)[::-1]) / 2**arange(16)[::-1]"
10 loops, best of 3: 21.6 msec per loop

答案 1 :(得分:1)

一种方法是使用numpy.binary_repr。它将产生一个字符串,但您可以轻松地将其转换为整数或浮点数组(只需更改dtype参数)。例如:

import numpy as np

k = 4
print np.array([list(np.binary_repr(x, k)) for x in range(2**k)], dtype=int)

这会产生:

[[0 0 0 0]
 [0 0 0 1]
 [0 0 1 0]
 [0 0 1 1]
 [0 1 0 0]
 [0 1 0 1]
 [0 1 1 0]
 [0 1 1 1]
 [1 0 0 0]
 [1 0 0 1]
 [1 0 1 0]
 [1 0 1 1]
 [1 1 0 0]
 [1 1 0 1]
 [1 1 1 0]
 [1 1 1 1]]

或者,如果你想要一个更易阅读的版本:

def bitstrings(k):
    binary = [np.binary_repr(item, width=k) for item in range(2**k)]
    return np.array([list(item) for item in binary], dtype=int)