numpy Python replace嵌套for循环

yyhrrdl8  于 2023-05-17  发布在  Python
关注(0)|答案(4)|浏览(202)

我有以下代码:

import itertools
import numpy as np

t = np.random.rand(3,3,3) 

def foo(T):    

    res = np.zeros((3,3,3))
    
    for a in range(3):
        for b in range(3):
            for c in range(3):
                idx = [a,b,c]
                combinations = list(itertools.permutations(idx, len(idx)))
                idx_arrays = tuple(np.array(idx) for idx in zip(*combinations))     
                res[a, b, c] = (1.0/len(combinations))*np.sum(T[idx_arrays])
        
        
    return res

sol = foo(t)

上面的代码应该相当于这样做:

for a in range(3):
        for b in range(3):
            for c in range(3):
                res2[a,b,c] = (1.0/6)*(
                                        t[a,b,c]
                                        + t[a,c,b]
                                        + t[b,a,c]
                                        + t[b,c,a]
                                        + t[c, a, b]
                                        + t[c,b,a]
                                        )

为了使代码更快,我想替换外部的for loops。这可以做到吗?理想情况下,这应该在不使用numpy的einsum方法的情况下实现。

kx5bkwkv

kx5bkwkv1#

您可以通过不进行重复计算来开始改进:

def foo(t):
    res = np.zeros_like(t).astype(t.dtype)
    A, B, C = t.shape
    for a in range(A):
        for b in range(a, B):
            for c in range(b, C):
                r = (1.0 / 6) * (
                    t[a, b, c]
                    + t[a, c, b]
                    + t[b, a, c]
                    + t[b, c, a]
                    + t[c, a, b]
                    + t[c, b, a]
                )
                res[a, b, c] = r
                res[a, c, b] = r
                res[b, a, c] = r
                res[b, c, a] = r
                res[c, a, b] = r
                res[c, b, a] = r
    return res
zbwhf8kr

zbwhf8kr2#

一行代码,使用np.transpose进行向量化:

np.stack([t.transpose(i) for i in itertools.permutations(np.arange(3), 3)]).mean(0)

如果你想要比3更高的维度,这些3可以被改变或功能化
import numpy as np from itertools import permutations as perm

import numpy as np
from itertools import permutations as perm

def foo(T):
    dims = len(T.shape)
    assert np.all(np.array(T.shape) == T.shape[0])
    all_perms = np.stack([T.transpose(i) for i in perm(np.arange(dims), dims)])
    return all_perms.mean(0)
nzrxty8p

nzrxty8p3#

我不知道这是否比3个嵌套的for循环快。无论如何,你可以试试这个:

import itertools
import numpy as np

t = np.random.rand(3,3,3)

def foo(T):    
    t = np.random.rand(3,3,3) 
    res = np.zeros((3,3,3))
    
    for a, b, c in set(itertools.permutations((0, 0, 0, 1, 1, 1, 2, 2, 2), 3)):
        idx = [a,b,c]
        combinations = list(itertools.permutations(idx, len(idx)))
        idx_arrays = tuple(np.array(idx) for idx in zip(*combinations))     
        res[a, b, c] = (1.0/len(combinations))*np.sum(t[idx_arrays])
        
        
    return res

sol = foo(t)

这里,我不是使用3 nested for loops,而是从元组(0, 0, 0, 1, 1, 1, 2, 2, 2)创建3 elementsunique permutations

42fyovps

42fyovps4#

这个解决方案怎么样?

def foo2(T):
    indices = np.indices((3, 3, 3)).reshape(3, -1)
    all_permutations = np.array(list(itertools.permutations(indices, len(indices))))
    idx_arrays = tuple(np.array(idx) for idx in zip(*all_permutations))
    summed = np.sum(T[tuple(idx_arrays)], axis=0)
    res = (1.0 / len(all_permutations)) * summed
    return res.reshape(3, 3, 3)

简而言之,您需要修改np.indices的输出以生成idx_arrays。然后使用高级索引对原始数组中的值求和,再除以排列数。最后,将结果重新整形为3x3x3数组。
此代码产生与原始代码相同的结果,但通过利用NumPy的向量化操作的功能,以更有效的方式产生。
在我的机器上:

%%timeit foo(t)
%%timeit foo2(t)
261 µs ± 4.36 µs per loop (mean ± std. dev. of 7 runs, 1,000 loops each)
36.7 µs ± 296 ns per loop (mean ± std. dev. of 7 runs, 10,000 loops each)

相关问题