重新嵌入了python循环

时间:2014-08-05 13:40:45

标签: python performance for-loop nested vectorization

我知道有几个关于如何加速嵌套python循环的帖子,但我无法将这些应用到我的问题中。 我有一个嵌套的python for循环,第一个遍历列表的所有元素,第二个迭代所有剩余的列表项,产生所有列表项的组合。对于一个我认为这是相当丑陋的,但它也很慢。任何想法如何使这更快,更pythonic?

n = len(delta_vec)
for j in range(0, n - 1, 1):
    for k in range(j + 1, n, 1):
        w = 1. / np.exp((mjd_list[k] - mjd_list[j]) / avg_time)
        w_sum = w_sum + w
        P = delta_vec[j] * delta_vec[k]
        j_index = j_index + w * np.sign(P) * np.sqrt(np.abs(P))

3 个答案:

答案 0 :(得分:3)

当然,最快的方法是不使用Python中的任何for循环或来自itertools的生成器,而是使用Numpy中的矢量化数学,如下所示:

n = len(delta_vec)
jj, kk = np.triu_indices(n, 1)
ww = 1. / np.exp((mjd_list[kk] - mjd_list[jj]) / avg_time)
w_sum = np.sum(ww)
PP = delta_vec[jj] * delta_vec[kk]
j_index = np.sum(ww * np.sign(PP) * np.sqrt(np.abs(PP)))

其中所有带双字母的变量都是长度为n的向量。请注意,您必须将列表转换为numpy-arrays才能使其正常工作。您可以通过1. / exp((a - b) / c)替换exp((b - a) / c)来获得更快的速度。

说明:您正在使用最多n的所有索引对进行计算,而不使用相同索引的对(因此没有1,1),并且该对中的第一个始终是最小的数字(所以没有2,1)。如果你要制作所有可能对的二维矩阵,你会看到你只是在对角线以上的元素上进行计算,所以你可以使用函数np.triu_index。为了证明这是有效的,你的循环会生成这些索引:

In [18]: n = 4
    ...: for j in range(0, n - 1, 1):
    ...:     for k in range(j + 1, n, 1):
    ...:         print j, k
0 1
0 2
0 3
1 2
1 3
2 3

您可以使用triu_indices生成完全相同的索引:

In [19]: np.triu_indices(n, 1)
Out[19]: (array([0, 0, 0, 1, 1, 2]), array([1, 2, 3, 2, 3, 3]))

快速测试正确性和速度:

import numpy as np
# generate some fake data
n = 300
mjd_vec = np.random.rand(n)
delta_vec = np.random.rand(n)
mjd_list = list(mjd_vec)
delta_list = list(delta_vec)
avg_time = 123

def f1():
    w_sum = j_index = 0
    n = len(delta_list)
    for j in range(0, n - 1, 1):
        for k in range(j + 1, n, 1):
            w = 1. / np.exp((mjd_list[k] - mjd_list[j]) / avg_time)
            w_sum = w_sum + w
            P = delta_vec[j] * delta_vec[k]
            j_index = j_index + w * np.sign(P) * np.sqrt(np.abs(P))
    return w_sum, j_index

def f2():
    n = len(delta_vec)
    jj, kk = np.triu_indices(n, 1)
    ww = 1. / np.exp((mjd_vec[kk] - mjd_vec[jj]) / avg_time)
    w_sum = np.sum(ww)
    PP = delta_vec[jj] * delta_vec[kk]
    j_index = np.sum(ww * np.sign(PP) * np.sqrt(np.abs(PP)))
    return w_sum, j_index

结果:

In [3]: f1()
Out[3]: (44839.864280781003, 18985.189058689775)

In [4]: f2()
Out[4]: (44839.864280781003, 18985.189058689775)

In [5]: timeit f1()
1 loops, best of 3: 629 ms per loop

In [6]: timeit f2()
100 loops, best of 3: 7.88 ms per loop

所以numpy版本会产生相同的结果,并且快80倍

答案 1 :(得分:2)

看起来您正在同时迭代两个列表,从每个列表中的相同索引中获取项目,并处理所有索引对。在这种情况下,例如:

>>> from itertools import combinations, izip
>>> for x, y in combinations(izip([1, 2, 3], [4, 5, 6]), 2):
    print zip(x, y)


[(1, 2), (4, 5)]
[(1, 3), (4, 6)]
[(2, 3), (5, 6)]

此处的每个输出列表包含来自第一个列表的该对的两元组,然后是来自第二个列表的该对的两元组。使用itertools可以实现此目的,而无需构建整个列表(内部zip除外)。

使用您自己的变量名称,如果您迭代:

for d_v, m_l in combinations(izip(delta_vec, mjd_list)):

然后zip(d_v, m_l)会给你相当于:

[(delta_vec[j], delta_vec[k]), (mjd_list[j], mjd_list[k])]

在你当前的方法中

或者,当您使用numpy时,毫无疑问,我不知道一些基于阵列的超快速方法。

答案 2 :(得分:0)

我会做两处修改:

  1. 更改迭代

  2. 将计算代码移动到函数

  3. 这将有助于清理代码并使其更加“pythonic”。"至于,瓶颈只是帽子,你有很多计算要做。 Python并不是设计得很快,所以如果速度确实是一个问题,可以考虑将计算外包给另一个更快的代码。通常,编译代码(C系列,FORTRAN等)非常适合这样的东西。

    def computations(x, y, i, j):
        w = 1. / np.exp((mjd_list[i] - mjd_list[j]) / avg_time)
        w_sum = w_sum + w
        P = x * y
        j_index = j_index + w * np.sign(P) * np.sqrt(np.abs(P))
    
        return [stuff you want to keep]
    
    for i, x in enumerate(delta_vec):
        for j, y in delta_vec[i:]:
             result = computations(x, y, i, j)