Python 使用numpy数组避免for循环-组合数学

Python 使用numpy数组避免for循环-组合数学,python,arrays,numpy,Python,Arrays,Numpy,必须有一种更像蟒蛇的方式: r = np.arange(100) results = [] for i in r: for j in r: for k in r: for l in r: #Here f is some predefined function if f(i,j,k,l) < 5.0: results.append(f(

必须有一种更像蟒蛇的方式:

r = np.arange(100)
results = []

for i in r:
    for j in r:
        for k in r:
            for l in r:

                #Here f is some predefined function
                if f(i,j,k,l) < 5.0:
                     results.append(f(i,j,k,l))
r=np.arange(100)
结果=[]
对于r中的i:
对于j in r:
对于r中的k:
对于r中的l:
#这里是一些预定义的函数
如果f(i,j,k,l)<5.0:
结果.附加(f(i,j,k,l))

我确信使用数组可以在某种程度上简化这一过程,但我不知道如何简化。谢谢

使用
itertools
笛卡尔乘积:

import itertools
r = np.arange(100)
results = []
for (i,j,k,l) in itertools.product(r,repeat=4):
    if f(i,j,k,l) < 5.0:
         results.append(f(i,j,k,l))
导入itertools
r=np.arange(100)
结果=[]
对于itertools.product(r,repeat=4)中的(i,j,k,l):
如果f(i,j,k,l)<5.0:
结果.附加(f(i,j,k,l))
或者更紧凑的方式,使用列表理解:

[ f(i,j,k,l) for (i,j,k,l) in itertools.product(r,repeat=4) if f(i,j,k,l) < 5.0 ]
[f(i,j,k,l)表示itertools中的(i,j,k,l)。如果f(i,j,k,l)<5.0,则乘积(r,repeat=4]

使用NumPy的and可以避免for循环和if语句。建议的方法被包装在
comb\u np(n)
中,而@Ohad Eytan提出的基于
itertools
的解决方案被包装在
comb\u it(n)
中。为方便起见,每个For循环的迭代次数(
100
,在您的示例中)作为参数传递给两个函数(
n
)。为了比较分析这两种方法,我使用了一个简单的政治函数
f(x,y,z,t)

两种方法产生相同的结果。到目前为止,一切顺利。但现在让我们来评估在效率方面是否存在任何差异:

In [1304]: import timeit

In [1305]: timeit.timeit("comb_np(10)", setup="from numpy import fromfunction;from __main__ import comb_np, f", number=1)
Out[1305]: 0.0008685288485139608

In [1306]: timeit.timeit("comb_it(10)", setup="from itertools import product;from numpy import arange;from __main__ import comb_it, f", number=1)
Out[1306]: 0.05153228418203071

In [1307]: timeit.timeit("comb_np(100)", setup="from numpy import fromfunction;from __main__ import comb_np, f", number=1)
Out[1307]: 3.4775129712652415

In [1308]: timeit.timeit("comb_it(100)", setup="from itertools import product;from numpy import arange;from __main__ import comb_it, f", number=1)
Out[1308]: 354.3811327822914
从上面的结果可以清楚地看出,在这个特定的问题中,NumPy的矢量化代码比迭代器的性能好大约两个数量级


有趣的是,我发现用内置函数
range
简单地替换NumPy的
arange
comb\u it
的性能显著提高:

def comb_it2(n):
    return [f(i,j,k,l) for (i,j,k,l) in product(range(n),repeat=4) if f(i,j,k,l) < 5.0]

你们跳过了f,但这里很重要(看看它是否可以矢量化)。f是矩阵的特征值之一,其元素是i,j,k,l的函数。这些信息够了吗?你真正需要做的是重写
f
,这样它就可以直接接受并使用4d数组了只要
f
仅适用于4个标量输入,您就必须进行类似这样的Pythonic迭代。请注意,所述4d数组的大小大约为GB……感谢您的评论。现在,这是不是比使用for循环更快,或者只是一种更好的书写方式?我希望能找到一个计算效率最高的解决方案。@cracka31我添加了一个更简洁的方法。我不认为你能做得更有效。你可以通过这个重新调用将
f()
调用的数量减半:
[x代表itertools.product中的t.product(r,repeat=4)代表[f(*t)],如果x<5.0]
。singleton_列表中x的
部分是嵌入赋值语句的一种狡猾的方法。@cracka31,您调用
f()
1亿次。除非
f()
便宜得惊人,否则与花费在
f()
上的时间相比,设置1亿个参数列表(无论如何编码)的成本将是微不足道的。
In [1304]: import timeit

In [1305]: timeit.timeit("comb_np(10)", setup="from numpy import fromfunction;from __main__ import comb_np, f", number=1)
Out[1305]: 0.0008685288485139608

In [1306]: timeit.timeit("comb_it(10)", setup="from itertools import product;from numpy import arange;from __main__ import comb_it, f", number=1)
Out[1306]: 0.05153228418203071

In [1307]: timeit.timeit("comb_np(100)", setup="from numpy import fromfunction;from __main__ import comb_np, f", number=1)
Out[1307]: 3.4775129712652415

In [1308]: timeit.timeit("comb_it(100)", setup="from itertools import product;from numpy import arange;from __main__ import comb_it, f", number=1)
Out[1308]: 354.3811327822914
def comb_it2(n):
    return [f(i,j,k,l) for (i,j,k,l) in product(range(n),repeat=4) if f(i,j,k,l) < 5.0]
In [1381]: comb_it2(10)
Out[1381]: [0, 4, 3, 2, 4, 1, 4, 3, 2, 4, 3, 4]

In [1382]: timeit.timeit("comb_it2(10)", setup="from itertools import product;from __main__ import comb_it2, f", number=1)
Out[1382]: 0.009133451094385237

In [1383]: timeit.timeit("comb_it2(100)", setup="from itertools import product;from __main__ import comb_it2, f", number=1)
Out[1383]: 32.556062019226374