寻找2-D numpy数组的相对最大值

4

我有一个二维的numpy数组,可以分成64个盒子(类似于棋盘)。 目标是编写一个函数,返回每个盒子中最大值的位置和值。例如:

FindRefs(array) --> [(argmaxX00, argmaxY00, Max00), ...,(argmaxX63, argmaxY63, Max63)]

其中argmaxXnnargmaxYnn是整个数组的索引(而不是盒子的索引),Maxnn是每个盒子中的最大值。换句话说,

Maxnn = array[argmaxYnn,argmaxYnn]

我尝试了显而易见的“嵌套for”解决方案:
def FindRefs(array):
    Height, Width = array.shape
    plumx = []
    plumy = []
    lum = []
    w = int(Width/8)
    h = int(Height/8)
    for n in range(0,8):    # recorrer boxes
        x0 = n*w
        x1 = (n+1)*w
        for m in range(0,8):
            y0 = m*h
            y1 = (m+1)*h
            subflatind = a[y0:y1,x0:x1].argmax() # flatten index of box
            y, x = np.unravel_index(subflatind, (h, w))
            X = x0 + x
            Y = y0 + y
            lum.append(a[Y,X])
            plumx.append(X)
            plumy.append(Y)

    refs = []
    for pt in range(0,len(plumx)):
        ptx = plumx[pt]
        pty = plumy[pt]
        refs.append((ptx,pty,lum[pt]))
    return refs

这个版本虽然能够工作,但是既不优雅也不高效。 因此,我尝试了更符合Python风格的版本:

def FindRefs(a):
    box = [(n*w,m*h) for n in range(0,8) for m in range(0,8)]
    flatinds = [a[b[1]:h+b[1],b[0]:w+b[0]].argmax() for b in box]
    unravels = np.unravel_index(flatinds, (h, w))
    ur = [(unravels[1][n],unravels[0][n]) for n in range(0,len(box))]
    absinds = [map(sum,zip(box[n],ur[n])) for n in range(0,len(box))]
    refs = [(absinds[n][0],absinds[n][1],a[absinds[n][1],absinds[n][0]]) for n in range(0,len(box))] 
    return refs

它可以正常运行,但令我惊讶的是,它并没有比以前的版本更有效率!

问题是:有没有更聪明的方法来完成这个任务?

请注意,效率很重要,因为我有许多大型数组需要处理。

欢迎任何线索。 :)


1个回答

2

试试这个:

from numpy.lib.stride_tricks import as_strided as ast
import numpy as np
def FindRefs3(a):
    box = tuple(x/8 for x in a.shape)
    z=ast(a, \
          shape=(8,8)+box, \
          strides=(a.strides[0]*box[0],a.strides[1]*box[1])+a.strides)
    v3 = np.max(z,axis=-1)
    i3r = np.argmax(z,axis=-1)
    v2 = np.max(v3,axis=-1)
    i2 = np.argmax(v3,axis=-1)
    i2x = np.indices(i2.shape)
    i3 = i3r[np.ix_(*[np.arange(x) for x in i2.shape])+(i2,)]
    i3x = np.indices(i3.shape)
    ix0 = i2x[0]*box[0]+i2
    ix1 = i3x[1]*box[1]+i3
    return zip(np.ravel(ix0),np.ravel(ix1),np.ravel(v2))

请注意,您的第一个FindRefs反转了索引。对于元组(i1,i2,v),a [i1,i2]不会返回正确的值,而a [i2,i1]将返回正确的值。
以下是代码的作用:
  • 首先,它通过给定数组的大小计算每个盒子需要具有的尺寸 (box)。请注意,这不进行任何检查: 您需要有一个数组,可以均匀分成8×8的网格。
  • 然后使用 astz 是最混乱的部分。它将2D数组转换为 4D 数组。4D数组的尺寸为(8,8,box[0],box[1]),因此可以选择要使用哪个盒子(前两个轴)以及要在盒子中使用哪个位置(下两个轴)。这样,我们可以通过对最后两个轴执行操作来一次性处理所有框。
  • v3 给出了沿着最后一个轴的最大值:换句话说,它包含了每个盒子每列的最大值。i3r 包含了该最大值所在的行号。
  • v2 沿着自己的最后一个轴取 v3 的最大值,现在正在处理盒子中的行:它找到列最大值并找到其最大值,以便 v2 是一个 2D 数组,其中包含每个盒子的最大值。如果您只需要最大值,那么这就是您所需要的全部。
  • i2 是包含最大值的列的索引。
  • 现在我们需要获取盒子中行的索引...那更加棘手了。 i3r 包含了盒子中每列最大值的行索引,但我们要找到指定在 i2 中的特定列的行。为此,我们使用 i2i3r 中选择一个元素,这给了我们 i3
  • 此时,i2i3 是 8×8 的数组,它们包含相对于每个盒子的最大值的行和列索引。我们需要绝对索引。因此,我们创建了 i2xi3x (实际上,这是无意义的;我们可以只创建一个,因为它们是相同的),它们只是 i2i3 索引的数组(在一个维度中为0,1,2,...,8等),然后将其乘以盒子尺寸,并添加相对最大索引,以获取绝对最大索引。
  • 然后我们将它们组合起来以获得与您拥有的相同输出。请注意,如果您将它们保留为数组而不是创建元组,它会更快。

请注意,在调用ast时,由于它被括号包围,因此不需要反斜杠。 - Veedrac
出于某种原因,我倾向于过度地在任何地方都添加反斜杠,可能是担心如果不这样做,在切换到需要它们的另一种语言时会忘记。 - cge
哇,绝对是步幅的关键!我必须更彻底地学习Numpy。同时,我正在努力理解你的代码逻辑。 - Rodolfo Leibner
由于该行代码“i3 = i2.choose(np.rollaxis(i3r,-1))”报告:ValueError: 需要2到32个数组对象(含)之间。 - Rodolfo Leibner
1
巴 - 我早该知道会出现这个未记录的限制。请参见https://dev59.com/8Yjca4cB1Zd3GeqPyIzt以获取更多有关此问题的信息。我将尝试将np.choose上的NPY_MAXARGS限制记录下来,因为突然出现这个限制真是相当烦人。无论如何,修改后的版本更加令人困惑,但应该可以工作。 - cge
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接