使用所有可能列创建二维数组的优雅方法

时间:2015-10-09 11:08:57

标签: python numpy

在numpy中我想制作一个2d arrray(r,by 2 ** r),其中列都是可能的二进制列。

例如,如果列的高度为5,则列将为

[0,0,0,0,0],  [0,0,0,0,1], [0,0,0,1,0], [0,0,0,1,1], [0,0,1,0,0], ...

我的解决方案是

 np.array(list(itertools.product([0,1],repeat = c))).T

这看起来非常难看。有更优雅的方式吗?

5 个答案:

答案 0 :(得分:4)

你可以在这里使用一些broadcasting,如此 -

(((np.arange(2**r)[:,None] & 2**np.arange(r)[::-1]))>0).astype(int)

对于r0之间的8,您还可以使用np.unpackbits -

np.unpackbits(np.arange(2**r,dtype='uint8')[:,None], axis=1)[:,8-r:]

运行时测试 -

案例#1(原件r = 5):

In [217]: r = 5

In [218]: from itertools import product

In [219]: %timeit np.array(list(product([0,1], repeat=5)))
10000 loops, best of 3: 33.9 µs per loop

In [220]: %timeit np.unpackbits(np.arange(2**r,dtype='uint8')[:,None], axis=1)[:,8-r:]
100000 loops, best of 3: 10.6 µs per loop

In [221]: %timeit (((np.arange(2**r)[:,None] & 2**np.arange(r)[::-1]))>0).astype(int) 
10000 loops, best of 3: 31.1 µs per loop

案例#2(较大r):

In [242]: r = 15

In [243]: %timeit (((np.arange(2**r)[:,None] & 2**np.arange(r)[::-1]))>0).astype(int)
100 loops, best of 3: 6.6 ms per loop

In [244]: %timeit np.array(list(product([0,1], repeat=r)))
10 loops, best of 3: 77.5 ms per loop

答案 1 :(得分:3)

它不是那么笨拙而是相当优雅:

r = 5
boolcols = [[y&1<<x and 1 for x in range(r)[::-1]] for y in range(2**r)]

对中心部分的一些澄清可能有用:

y&1<<x and 1

这相当于

(y&(1<<x)) and 1

让我们假装x = 3而y = 5然后得到:

  1. 1<<x1<<3,二进制表示法为1000。二进制1向左移3步。在python中,您可以编写0b10008Read more
  2. y&(1<<x)现在是5&8,它是01011000的按位'与',0
  3. 现在我们离开(0) and 1,产生0
  4. 注意: 使用timeit来衡量其与其他解决方案的性能之后,这不是您应该选择使用的那个!我在每次测试中都比较慢。例如:

    In [21]: r = 15
    
    In [23]: %timeit [[y&1<<x and 1 for x in range(r)[::-1]] for y in range(2**r)]
    10 loops, best of 3: 39.8 ms per loop
    
    In [30]: %timeit (((np.arange(2**r)[:,None] & 2**np.arange(r)[::-1]))>0).astype(int)
    1000 loops, best of 3: 1.31 ms per loop
    

答案 2 :(得分:3)

另一个非笨拙的解决方案:

r = 3
print [map(int, list('{:0{w}b}'.format(x, w=r))) for x in xrange(2**r)]

,并提供:

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

答案 3 :(得分:1)

这个怎么样?

   np.array([list("0"*(r-1-int(np.log2(i)))+"{0:b}".format(i))  if i>0 else [i]*r for i in range(0,2**r)]).astype(int)

修改

我注意到在格式化解决方案中不必使用log2计算:

frmt = "{0:0"+str(r)+"b}"
print [map(int,list(frmt.format(i))) for i in range(0,2**r)]

和时差......

In [17]: timeit [map(int,list(frmt.format(i))) for i in range(0,2**r)]
10000 loops, best of 3: 171 µs per loop

In [18]: timeit np.array([list("0"*(r-1-int(np.log2(i)))+"{0:b}".format(i)) if i>0 else [i]*r for i in range(0,2**r)]).astype(int)
1000 loops, best of 3: 514 µs per loop

答案 4 :(得分:1)

itertools.product是一个快速的C代码; np.array是较慢的通用代码。在适当的情况下,np.fromiter可以更快。但fromiter需要平面输入,而不是嵌套列表。但另一个itertools可以很好地平整列表。

以下是一些迭代时间比较:

In [72]: timeit list(product([0,1],repeat=5))
100000 loops, best of 3: 7.03 us per loop

In [73]: timeit list(chain(*product([0,1],repeat=5)))
100000 loops, best of 3: 18.3 us per loop

In [74]: timeit np.fromiter(chain(*product([0,1],repeat=5)),int,count=160).reshape(-1,5)
10000 loops, best of 3: 33.8 us per loop

In [75]: timeit np.array(list(product([0,1],repeat=5)))
10000 loops, best of 3: 65.1 us per loop

在这种情况下,包括count胜利fromiter没有太大区别。

链接发电机有一定的Pythonic优雅。

通过比较,我对纯粹numpy方法的时间有点慢:

In [85]: timeit (((np.arange(2**5)[:,None] & 2**np.arange(5)[::-1]))>0).astype(int)
10000 loops, best of 3: 38.1 us per loop

但是@Divakar表明这个解决方案可以更好地扩展。