如何在python中加速数组赋值?

时间:2015-12-30 03:21:43

标签: python arrays numpy

我正在尝试在python中进行数组赋值,但它很慢,有没有办法加速?

simi_matrix_img = np.zeros((len(annot), len(annot)), dtype='float16')
for i in range(len(annot)):
    for j in range(i + 1):
        score = 0
        times = 0
        if i != j:
            x_idx = [p1 for (p1, q1) in enumerate(annot[i]) if np.abs(q1 - 1) < 1e-5]
            y_idx = [p2 for (p2, q2) in enumerate(annot[j]) if np.abs(q2 - 1) < 1e-5]

            for idx in itertools.product(x_idx, y_idx):
                score += simi_matrix_word[idx]
                times += 1
            simi_matrix_img[i, j] = score/times
        else:
            simi_matrix_img[i, j] = 1.0

&#34; ANNOT&#34;是一个numpy数组。有没有办法加速呢?

2 个答案:

答案 0 :(得分:1)

(1)在可能的情况下,您可以使用生成器而不是列表推导。例如:

        x_idx = (p1 for (p1, q1) in enumerate(annot[i]) if np.abs(q1 - 1) < 1e-5)
        y_idx = (p2 for (p2, q2) in enumerate(annot[j]) if np.abs(q2 - 1) < 1e-5)

有了这个,你只对这些项进行一次迭代(在for idx in itertools.product(x_idx, y_idx)中),而不是两次(一次用于构造列表,然后再在所述for循环中)。

(2)你用的是什么Python?如果&lt; 3,我预感到问题的一个重要部分是你正在使用range(),这对于非常大的范围来说可能是昂贵的(因为我假设你在这里使用)。在Python 2.7中,range()实际上构造了列表(在Python 3中不是这样),这可能是一个昂贵的操作。尝试使用简单的while循环实现相同的结果。例如,代替for i in range(len(annot)),执行:

i=0
while i < len(annot):
    ... do stuff with i ...
    i += 1

(3)为什么要多次拨打len(annot)?看起来好像你在改变annot。虽然len(annot)是快速O,但您可以将长度存储在var中,例如annot_len = len(annot),然后只引用它。不过不会刮掉很多东西,我很害怕。

答案 1 :(得分:1)

我认为这一行的缩进是错误的:

            simi_matrix_img[i, j] = score/times

您希望在所有product次迭代后执行该分配。但由于它是最后一次分配,结果将是相同的。

这是您的代码的部分修改

def foo1(annot, simi_matrix_word):
    N = annot.shape[0]
    simi_matrix_img = np.zeros((N,N))
    for i in range(N):
        for j in range(i + 1):
            if i != j:
                x_idx = np.nonzero(annot[i])[0]
                y_idx = np.nonzero(annot[j])[0]
                idx = np.ix_(x_idx, y_idx)
                # print(idx, simi_matrix_word[idx])
                score = simi_matrix_word[idx].mean()
                simi_matrix_img[i, j] = score
            else:
                simi_matrix_img[i, j] = 1.0
    return simi_matrix_img

对于小型测试用例,它返回相同的内容:

annot=np.array([[1,0,1],[0,1,1]])
simi_matrix_word = np.arange(12, dtype=float).reshape(3,4)
[[ 1.  0.]
 [ 7.  1.]]

摆脱了所有内部迭代。下一步将减少外部迭代。例如,从np.eye(N)开始,只迭代较低的三个索引:

In [169]: np.eye(2)
Out[169]: 
array([[ 1.,  0.],
       [ 0.,  1.]])

In [170]: np.tril_indices(2,-1)
Out[170]: (array([1]), array([0]))

请注意,对于2行annot,我们只会在score计算一个[1,0]

用布尔索引替换nonzero

def foo3(annot, simi_matrix_word):
    N = annot.shape[0]
    A = annot.astype(bool)
    simi_matrix_img = np.eye(N,dtype=float)
    for i,j in zip(*np.tril_indices(N,-1)):
        score = simi_matrix_word[A[i],:][:,A[j]]
        simi_matrix_img[i, j] = score.mean()
    return simi_matrix_img

或者这可能会加快索引的速度:

def foo4(annot, simi_matrix_word):
    N = annot.shape[0]
    A = annot.astype(bool)
    simi_matrix_img = np.eye(N,dtype=float)
    for i in range(1,N):
        x = simi_matrix_word[A[i],:]
        for j in range(i):
            score = x[:,A[j]]
            simi_matrix_img[i, j] = score.mean()
    return simi_matrix_img

由于annot每行的非零值的数量可能不同,因此每个score求和的术语数也不同。这有力地表明进一步的矢量化是不可能的。