循环通过2D numpy数组(例如创建一条线)

时间:2015-04-09 11:41:46

标签: python arrays performance numpy multidimensional-array

我经常发现自己必须在2D数组中创建一条线(或某种其他形状)。换句话说,除了y = mx + c之外,数组的值都是零。 (旁白 - 这种方法的动机,而不是在一维数组中存储一条线,是我的工作经常需要2D傅里叶变换,所以除了线/形状/等等之外我还需要零点)。

我这样做的常用方法如下:

array = numpy.zeros((height, width))
for i, line in enumerate(array):
    for j, pixel in enumerate(line):
         if j == m*i + c:
             array[i,j] = 1

这样可以正常工作,但它并没有让我觉得特别是pythonic,而且当阵列变大时它往往变得相当慢。所以,我的问题是一个相当普遍的问题 - 是否有人知道更好的方法呢?

提前致谢!

3 个答案:

答案 0 :(得分:3)

您可以在这里使用broadcasting来摆脱那些嵌套循环 -

import numpy as np
out = (np.arange(height) == m*np.arange(width)[:,None]+c)+0.0

作为验证正确性的示例,使用这些参数 -

height = 10
width = 10
m = 0.5;
c = 6;

你会 -

In [306]: array
Out[306]: 
array([[ 0.,  0.,  0.,  0.,  0.,  0.,  1.,  0.,  0.,  0.],
       [ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.],
       [ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  1.,  0.,  0.],
       [ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.],
       [ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  1.,  0.],
       [ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.],
       [ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  1.],
       [ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.],
       [ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.],
       [ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.]])

In [307]: out
Out[307]: 
array([[ 0.,  0.,  0.,  0.,  0.,  0.,  1.,  0.,  0.,  0.],
       [ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.],
       [ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  1.,  0.,  0.],
       [ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.],
       [ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  1.,  0.],
       [ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.],
       [ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  1.],
       [ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.],
       [ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.],
       [ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.]])

答案 1 :(得分:2)

函数np.fromfunction是为可以从索引构建数组的情况设计的,例如这种情况。

在你的情况下,

np.fromfunction(lambda i, j: j == m*i+c, (height, width), dtype=np.float)

将等同于您的方法,但使用numpy的例程而不是Python for循环。

简短演示:

import numpy as np
height, width = 10,10
m, c = 2, 4

a = np.zeros((height, width))
for i, line in enumerate(a):
    for j, pixel in enumerate(a):
         if j == m*i + c:
             a[i,j] = 1

b = np.fromfunction(lambda i, j: j == m*i+c, (height, width), dtype=np.float)

np.all(a==b)
# True
b.astype(np.int) # as type added to reduce output (no need for all the periods)
#array([[0, 0, 0, 0, 1, 0, 0, 0, 0, 0],
#       [0, 0, 0, 0, 0, 0, 1, 0, 0, 0],
#       [0, 0, 0, 0, 0, 0, 0, 0, 1, 0],
#       [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
#       [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
#       [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
#       [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
#       [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
#       [0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
#       [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]])

编辑:即使这个答案被接受,我想指出@Divakar's answer在我的机器上快了大约10倍。如果您正在寻找速度:使用该答案如果您的问题很容易让自己适应矢量化,就像Divakar所示(并非每个fromfunction调用都可以轻松地进行矢量化)。我赞成它,因为它是解决这个问题的好方法。

答案 2 :(得分:1)

使用np.put,但您需要创建特定索引的列表,您可以使用列表解析来执行此操作:

>>> np.put(arr,[j for j in range(arr.shape[1]) for i in range(arr.shape[0]) if j == m*i + c],1)

演示:

>>> np.put(arr,[j for j in range(arr.shape[1]) for i in range(arr.shape[0]) if j == 3*i + 1],1)
>>> arr
array([[ 0.,  1.,  0.],
       [ 0.,  0.,  0.],
       [ 0.,  0.,  0.],
       [ 0.,  0.,  0.],
       [ 0.,  0.,  0.]])
>>> np.put(arr,[j for j in range(arr.shape[1]) for i in range(arr.shape[0]) if j == 0.5*i + 2],1)
>>> arr
array([[ 0.,  1.,  1.],
       [ 0.,  0.,  0.],
       [ 0.,  0.,  0.],
       [ 0.,  0.,  0.],
       [ 0.,  0.,  0.]])