如何加速Python中的嵌套循环

时间:2015-04-05 16:20:40

标签: python performance numpy nested-loops

我正在尝试在我的算法中编写一个嵌套循环,并且由于这些嵌套循环而遇到整个算法花费太长时间的一些问题。我对Python很新(正如你可能从我的下面的非专业代码中找到的那样:()希望有人可以指导我加速我的代码!

整个算法用于多个1500 * 6400阵列的火灾探测。在遍历整个阵列时应用小的上下文分析。上下文分析以动态分配的窗口大小方式执行。窗口大小可以从11 * 11到31 * 31,直到采样窗口内的验证值足以进行下一轮计算,例如如下所示:

def ContextualWindows (arrb4,arrb5,pfire):
####arrb4,arrb5,pfire are 31*31 sampling windows from large 1500*6400 numpy array
    i=5
    while i in range (5,16):
        arrb4back=arrb4[15-i:16+i,15-i:16+i]
        ## only output the array data when it is 'large' enough 
        ## to have enough good quality data to do calculation       
        if np.ma.count(arrb4back)>=min(10,0.25*i*i):
            arrb5back=arrb5[15-i:16+i,15-i:16+i]
            pfireback=pfire[15-i:16+i,15-i:16+i]
            canfire=0
            i=20
        else: 
            i=i+1

###unknown pixel: background condition could not be characterized
    if i!=20: 
        canfire=1
        arrb5back=arrb5
        pfireback=pfire
        arrb4back=arrb4
    return (arrb4back,arrb5back,pfireback,canfire)

然后,这个动态窗口将被输入下一轮测试,例如:

b4backave=np.mean(arrb4Windows)
b4backdev=np.std(arrb4Windows)
if b4>b4backave+3.5*b4backdev:
    firetest=True

要将整个代码运行到我的多个1500 * 6400 numpy阵列,它花了半个多小时甚至更长时间。只是想知道是否有人知道如何处理它?我应该付出努力的一般性想法会非常有用!

非常感谢!

2 个答案:

答案 0 :(得分:1)

如果速度是一个问题,请避免while循环。循环适用于for循环,因为开始和结束是固定的。此外,您的代码进行了大量复制,这并非真正必要。重写的功能:

def ContextualWindows (arrb4,arrb5,pfire):
    ''' arrb4,arrb5,pfire are 31*31 sampling windows from 
        large 1500*6400 numpy array '''

    for i in range (5, 16):
        lo = 15 - i  # 10..0
        hi = 16 + i  # 21..31
        #  only output the array data when it is 'large' enough
        #  to have enough good quality data to do calculation
        if np.ma.count(arrb4[lo:hi, lo:hi]) >= min(10, 0.25*i*i):
            return (arrb4[lo:hi, lo:hi], arrb5[lo:hi, lo:hi], pfire[lo:hi, lo:hi], 0)
    else:    #  unknown pixel: background condition could not be characterized
        return (arrb4, arrb5, pfire, 1)

为了清楚起见,我使用了PEP 8的样式指南(如扩展注释,注释字符数,运算符周围的空格等)。复制窗口arrb4在此处出现两次,但仅在条件满足时才会发生,并且每次函数调用只会发生一次。 else子句只有在for - 循环已经结束时才会执行。当我们完全退出函数时,我们甚至不需要循环break 如果这样可以加快代码的速度,请告诉我们。我认为它不会很多,但不管怎样我都没有多少代码。

答案 1 :(得分:0)

我使用ContextualWindows和变体进行了一些时间测试。一个i步骤大约需要50us,全部大约需要500个。

这个简单的迭代需要大约相同的时间:

[np.ma.count(arrb4[15-i:16+i,15-i:16+i]) for i in range(5,16)]

迭代机制,以及'复制'数组是时间的一小部分。在可能的情况下numpy正在制作观点,而非副本。

我专注于尽量减少这些count步骤的数量,或加快count


比较这些窗口上各种操作的时间:

第一次进行一步:

In [167]: timeit [np.ma.count(arrb4[15-i:16+i,15-i:16+i]) for i in range(5,6)]
10000 loops, best of 3: 43.9 us per loop

现在进行10个步骤:

In [139]: timeit [arrb4[15-i:16+i,15-i:16+i].shape for i in range(5,16)]
10000 loops, best of 3: 33.7 us per loop

In [140]: timeit [np.sum(arrb4[15-i:16+i,15-i:16+i]>500) for i in range(5,16)]
1000 loops, best of 3: 390 us per loop

In [141]: timeit [np.ma.count(arrb4[15-i:16+i,15-i:16+i]) for i in range(5,16)]
1000 loops, best of 3: 464 us per loop

简单的索引不需要花费太多时间,但对条件的测试需要更多时间。

cumsum有时用于加速滑动窗口的总和。您不是在每个窗口上取和(或平均值),而是计算cumsum,然后使用窗口前端和末端之间的差异。

尝试类似的东西,但在2d - 两个维度的cumsum中,然后是对角线对角线之间的差异:

In [164]: %%timeit
   .....: cA4=np.cumsum(np.cumsum(arrb4,0),1)
   .....: [cA4[15-i,15-i]-cA4[15+i,15+i] for i in range(5,16)]
   .....: 
10000 loops, best of 3: 43.1 us per loop

这几乎比(几乎)等效sum快10倍。价值观并不完全匹配,但时间表明这可能值得提炼。