【问题标题】:Applying functions to multidimensional numpy arrays without loops将函数应用于没有循环的多维 numpy 数组
【发布时间】:2015-12-17 13:52:58
【问题描述】:

我正在使用 numpy 处理栅格数据(从 GDAL 读取后),它表示海拔。我的目标是使用 numpy 计算数组中每个像素的水流方向,主要根据给定像素与其 8 个相邻像素之间的高程差异确定。

我已经实现了一个滚动窗口技术来生成一个包含每个像素及其邻居的多维数组,其工作原理如下:

def rolling_window(array, window_size):
    itemsize = array.itemsize
    shape = (array.shape[0] - window_size + 1,
             array.shape[1] - window_size + 1,
             window_size, window_size)
    strides = (array.shape[1] * itemsize, itemsize,
               array.shape[1] * itemsize, itemsize)
    return np.lib.stride_tricks.as_strided(array, shape=shape, strides=strides)

array = np.arange(100)
array = array.reshape(10, 10)
w = rolling_window(array, 3)

# produces array with shape (8, 8, 3, 3) - edge cases are not currently dealt with.

因此,一系列 3 x 3 阵列,以 1,1 处的研究像素为中心,每个阵列位于栅格“行”阵列的另一个维度内,例如,从输入的一个像素开始,表示它的阵列可以如下,其中像素值为 4 是研究像素,其他值是它的直接邻居。

array([[[[ 0,  1,  2],
         [ 3,  4,  5],
         [ 6,  7,  8]]]])

我当前处理这个多维数组的方法的简化版本是以下函数:

def flow_dir(array):

    # Value to assign output based on element index.
    flow_idx_dict = {0: 32,
                     1: 64,
                     2: 128,
                     3: 16,
                     5: 1,
                     6: 8,
                     7: 4,
                     8: 2}

    # Generates the rolling window array as mentioned above.
    w = rolling_window(array, 3)

    # Iterate though each pixel array.
    for x, i in enumerate(w, 1):
        for y, j in enumerate(i, 1):
            j = j.flatten()

            # Centre pixel value after flattening.
            centre = j[4]

            # Some default values.
            idx = 4
            max_drop = 0

            # Iterate over pixel values in array.
            for count, px in enumerate(j):

                # Calculate difference between centre pixel and neighbour.
                drop = centre - px

                # Find the maximum difference pixel index.
                if count != 4:
                    if drop > max_drop:
                        max_drop = drop
                        idx = count

            # Assign a value from a dict, matching index to flow direction category.
            value = flow_idx_dict[idx]

            # Update each pixel in the input array with the flow direction.
            array[x, y] = value
    return array

可以理解,所有这些 for 循环和 if 语句都非常慢。我知道必须有一个矢量化的 numpy 方法来做到这一点,但我正在努力寻找我需要的确切功能,或者可能不了解如何正确实现它们。我尝试过 np.apply_along_axis、np.where、np.nditer 等,但到目前为止都无济于事。我认为我需要的是:

  1. 一种将函数应用于滚动窗口生成的每个像素数组的方法,而无需使用 for 循环来访问它们。

  2. 查找最大drop index值,不使用if语句和枚举。

  3. 能够批量更新输入数组,而不是单个元素。

【问题讨论】:

  • 你能分享rolling_window函数定义吗?另外,flow_idx_dict 是什么?您能否添加可用于运行flow_dir 的示例输入?
  • 我在 rolling_window 和 flow 字典中添加了。将 np.arange(100) 重整为 (10, 10) 的示例足以作为 flow_dir 的输入,尽管实际上我的数组要大得多,并且它们的值变化更大。
  • 那么,我会先使用arr = np.arange(90),然后再使用flow_dir(arr)?我认为这会引发错误。
  • 你看过numpy.gradient吗?
  • 想了很多。查看 np.pad 以使您能够反映边缘值以帮助处理边缘影响。因此,我假设,您只需要找到最小差异(您的窗口 - 中间)即可将您的值从字典中提取出来,但目前尚不清楚您是单独使用基数还是考虑重复甚至相反的最大下降。

标签: python arrays numpy multidimensional-array raster


【解决方案1】:

我认为这里可以避免滚动窗口;在 NxN 数组上进行矢量化比 NxNx3x3 更容易且更易读。

考虑这些数据:

array = np.array([[78, 72, 69, 71, 58, 49],
       [74, 67, 56, 49, 46, 50],
       [69, 53, 44, 37, 38, 48],
       [64, 58, 55, 22, 33, 24],
       [68, 61, 47, 21, 16, 19],
       [74, 53, 34, 12, 11, 12]])
N=6

首先,以这种方式计算 8 个梯度和代码:

gradient = np.empty((8,N-2,N-2),dtype=np.float)
code = np.empty(8,dtype=np.int)
for k in range(8):
    theta = -k*np.pi/4
    code[k] = 2**k
    j, i = np.int(1.5*np.cos(theta)),-np.int(1.5*np.sin(theta))
    d = np.linalg.norm([i,j])
    gradient[k] = (array[1+i: N-1+i,1+j: N-1+j]-array[1: N-1,1: N-1])/d

速度很快,因为外部循环很少 (8)。 (-gradient).argmax(axis=0) 为每个像素指定流向。

最后,take 代码:

direction = (-gradient).argmax(axis=0)
result = code.take(direction)

结果:

array([[  2,   2,   4,   4],
       [  1,   2,   4,   8],
       [128,   1,   2,   4],
       [  2,   1,   4,   4]])

【讨论】:

  • 肯定会很高兴避免滚动窗口,这正是我的思维在空间分析背景下的工作方式,而不是数学或计算机科学。我认为这非常接近我的需要,只是编码不如预期,例如给定窗口 [3, 2, 0], [1, 6, 9], [4, 4, 4],中心像素 (6) 的输出为 4(即南),但应为 128(NE ),就像这里的方向编码resources.arcgis.com/en/help/main/10.2/index.html#/…一样。可能的原因是梯度数组似乎有一半的数据设置为零。
  • 我需要进行一些编辑才能运行,但它仍然不完全存在 - 对角线仍然出现零梯度差异。对于那些 j, i 出来的结果是 0、0,而不是 -1、-1 等。我还发现 argmax 实际上是正确使用的函数,因为它是寻求的最大下降。忽略对角线,这现在可以正常工作,所以我认为只是分配 i 和 j 的线需要进一步工作。
  • 等一下,您必须使用 Python 3。问题是 3/2,而我的 Python 2.7 安装返回 1。将其更改为 3/2.0 可以解决问题。
  • 是的,我相信是的,感谢您抽出宝贵时间。使用 1000 x 1000 单元格的测试数组,您的代码比迭代滚动窗口方法快大约 100 倍,绝对满足我的要求。现在,我需要看看边缘情况和接收器......
猜你喜欢
  • 2019-12-31
  • 1970-01-01
  • 1970-01-01
  • 2020-08-22
  • 1970-01-01
  • 1970-01-01
  • 2014-12-15
  • 1970-01-01
  • 2017-01-11
相关资源
最近更新 更多