3

我已经编写了一些代码,用于在几年范围内(例如 15 年)ndimage.filters.convolve对数组(例如 array1)进行卷积,然后在生成的数组(例如 array2)高于随机生成的数字时,另一个数组(例如,array3) 被赋值为 1。一旦 array3 被赋值为 1,它每年都会向上计数,当它最终达到某个值(例如 5)时,array1 会在此位置更新。

对不起,如果这有点令人困惑。实际上,我已经通过 using 使脚本正常工作numpy.where(boolean expression, value, value),但是在我需要多个表达式(例如where array2 == 1 and array3 == 0)的地方,我使用了 for 循环来遍历数组中的每个值。这在此处的示例中效果很好,但是当我将数组替换为更大的数组时(完整的脚本会导入 GIS 网格并将它们转换为数组),这个 for 循环每年都需要几分钟的时间来处理。由于我们必须将模型运行 60 年 1000 次,因此我需要找到一种更有效的方法来处理这些数组。

我尝试在 numpy.where 中使用多个表达式,但无法弄清楚如何让它工作。我还尝试了 zip(array) 将数组压缩在一起,但我无法更新它们,我认为这是因为这创建了数组元素的元组。

我附上了脚本的副本,如前所述,它完全按照我的需要工作。但是,它需要更有效地执行此操作。如果有人有任何建议,那就太好了。这是我关于 python 的第一篇文章,所以我仍然认为自己是一个新手。

import numpy as np
from scipy import ndimage
import random
from pylab import *

###################### FUNCTIONS ###########################

def convolveArray1(array1, kern1):

    newArray = ndimage.filters.convolve(array1, kern1, mode='constant')

    return newArray


######################## MAIN ##############################

## Set the number of years
nYears = range(1,16)

## Cretae array1
array1 = np.zeros((10,10), dtype=np.int) # vegThreshMask

# Add some values to array1
array1[[4,4],[4,5]] = 8
array1[5,4] = 8
array1[5,5] = 8

## Create kerna; array
kernal = np.ones((3,3), dtype=np.float32)

## Create an empty array to be used as counter
array3 = np.zeros((10,10), dtype=np.int)

## iterate through nYears
for y, yea in enumerate(nYears):

    # Create a random number for the year
    randNum = randint(7, 40)
    print 'The random number for year %i is %i' % (yea, randNum)
    print

    # Call the convolveArray function
    convArray = convolveArray1(array1, kernal)

    # Update array2 where it is greater than the random number    
    array2 = np.where(convArray > randNum, 1, 0)
    print 'Where convArray > randNum in year %i' % (yea)
    print array2
    print 

    # Iterate through array2 
    for a, ar in enumerate(array2):
        for b, arr in enumerate(ar):
            if all(arr == 1 and array3[a][b] == 0):
                array3[a][b] = 1
            else:
                if array3[a][b] > 0:
                    array3[a][b] = array3[a][b] + 1
            if array3[a][b] == 5:
                array1[a][b] = 8

    # Remove the initial array (array1) from the updated array3   
    array3 = np.where(array1 > 0, 0, array3)
    print 'New array3 after %i years' % (yea)
    print '(Excluding initial array)'
    print array3
    print    

print 'The final output of the initial array'
print array1
4

2 回答 2

3

我怀疑如果您开始使用广播,您可以获得显着的加速。例如,从您的行开始,# Iterate through array2我们可以删除显式循环并简单地广播我们想要更改的变量。请注意,为了清楚起见,我使用AX而不是arrayX

# Iterate through A2

idx  = (A2==1) & (A3==0)
idx2 = (~idx)  & (A3>0)
A3[idx ]  = 1
A3[idx2] += 1
A1[A3==5] = 8

此外,一旦您习惯了这种风格,这会极大地提高代码的清晰度,因为您没有明确地处理索引(您的ab此处的)。

值得麻烦吗?

在尝试了上面的代码后,我要求 OP 进行速度测试:

如果您确实实现了循环更改,请让我知道您的实际代码的加速。知道给出的建议是否只是美化的语法糖,或者有显着的效果会很有用。

经过测试,响应速度大幅提升了 40 倍!在处理正在执行简单掩码的大型连续数据数组时,numpy它是比原生 python 列表更好的选择。

于 2012-03-01T15:00:02.830 回答
1

听起来您在尝试使用多个条件来np.where使用诸如array1 > 0 and array2 < 0. 这不起作用,因为布尔运算在 Python 中的工作方式,如此所述。首先,array1 > 0被评估,然后使用该__nonzero__方法将其转换为布尔值(__bool__在 Python 3 中重命名为)。没有一种将数组转换为布尔值的独特有用方法,并且目前没有办法覆盖布尔运算符的行为(尽管我相信这将在未来的版本中进行讨论),因此在 numpy 中,ndarray.__nonzero__定义为引发异常。相反,您可以使用np.logical_andnp.logical_ornp.logical_not, 具有您期望的行为。

不过,我不知道这会给你带来多大的加速。如果您最终在循环中执行大量数组索引操作,则可能值得研究cython,您可以通过将数组操作移动到 C 扩展中来轻松加速数组操作。

于 2012-03-01T12:31:04.733 回答