我知道有几个关于如何加速嵌套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))
答案 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)
我会做两处修改:
更改迭代
将计算代码移动到函数
这将有助于清理代码并使其更加“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)