【问题标题】:What is a more efficient way to process numpy arrays based on multiple criteria?基于多个标准处理 numpy 数组的更有效方法是什么?
【发布时间】:2012-03-01 06:53:29
【问题描述】:

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

对不起,如果这有点令人困惑。我实际上已经使用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

【问题讨论】:

    标签: python arrays multidimensional-array numpy convolution


    【解决方案1】:

    我怀疑如果您开始使用广播,您可能会获得显着的加速。例如,从您的# 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 列表更好的选择。

    【讨论】:

    • 这非常有效!我进行了几次测试,比较了这个速度和我原来的速度,这需要 18 秒而不是超过 12 分钟!所以它肯定有显着的效果。谢谢(它看起来也好多了)
    • @TravisMoon 太棒了!非常感谢您发布结果,当人们询问 numpy 是否值得为基本的数组操作而烦恼时,我会指导他们。我预计会加速,但没有达到 40 倍的速度!
    【解决方案2】:

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

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

    【讨论】:

    • 感谢您了解 python 如何处理布尔表达式,我工作中的其他人也提到了 np.logical_and 用于 numpy 数组。对于这项工作,我使用了上面给出的答案,但如果我在某个阶段有机会,我也会试试这个,看看哪个更快。我不确定我在工作中使用的计算机是否有 cpython,但我可能仍然会研究它。谢谢。
    猜你喜欢
    • 2018-06-24
    • 1970-01-01
    • 1970-01-01
    • 2014-06-27
    • 2014-02-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多