【问题标题】:Windowed maximum in numpynumpy 中的窗口最大值
【发布时间】:2013-09-05 20:08:41
【问题描述】:

我有一个数组,我想通过扫描 2x2 非重叠窗口并获得最大值来生成一个较小的数组。这是一个例子:

import numpy as np

np.random.seed(123)
np.set_printoptions(linewidth=1000,precision=3)
arr = np.random.uniform(-1,1,(4,4))
res = np.zeros((2,2))
for i in xrange(res.shape[0]):
    for j in xrange(res.shape[1]):
        ii = i*2
        jj = j*2
        res[i][j] = max(arr[ii][jj],arr[ii+1][jj],arr[ii][jj+1],arr[ii+1][jj+1])

print arr
print res

所以一个像这样的矩阵:

[[ 0.393 -0.428 -0.546  0.103]
 [ 0.439 -0.154  0.962  0.37 ]
 [-0.038 -0.216 -0.314  0.458]
 [-0.123 -0.881 -0.204  0.476]]

应该变成这样:

[[ 0.439  0.962]
 [-0.038  0.476]]    

我怎样才能更有效地做到这一点?

【问题讨论】:

  • 你能告诉我们你尝试了什么以及为什么没有奏效吗?
  • 上面的代码完成了所需的工作,但这需要很快,因此我想删除 for 循环
  • 考虑使用NumBa。你可以让你的双循环保持原样,在装饰器中添加大约 10 个字符,并为此获得类似 C 的性能。如果您使用 Continuum Analytics 的 "Anaconda" Python 分发版,开箱即用即可轻松使用。
  • 最佳答案在 cmets 中! stackoverflow.com/questions/18645013/…

标签: python numpy


【解决方案1】:

你可以这样做:

print arr.reshape(2,2,2,2).swapaxes(1,2).reshape(2,2,4).max(axis=-1)

[[ 0.439  0.962]
 [-0.038  0.476]]

从以下开始解释:

arr=np.array([[0.393,-0.428,-0.546,0.103],
[0.439,-0.154,0.962,0.37,],
[-0.038,-0.216,-0.314,0.458],
[-0.123,-0.881,-0.204,0.476]])

我们首先要将轴分组到相关部分。

tmp = arr.reshape(2,2,2,2).swapaxes(1,2)
print tmp    

[[[[ 0.393 -0.428]
   [ 0.439 -0.154]]

  [[-0.546  0.103]
   [ 0.962  0.37 ]]]


 [[[-0.038 -0.216]
   [-0.123 -0.881]]

  [[-0.314  0.458]
   [-0.204  0.476]]]]

再次整形以获得我们想要的数据组:

tmp = tmp.reshape(2,2,4)
print tmp

[[[ 0.393 -0.428  0.439 -0.154]
  [-0.546  0.103  0.962  0.37 ]]

 [[-0.038 -0.216 -0.123 -0.881]
  [-0.314  0.458 -0.204  0.476]]]

最后沿最后一个轴取最大值。

对于方阵,这可以概括为:

k = arr.shape[0]/2
arr.reshape(k,2,k,2).swapaxes(1,2).reshape(k,k,4).max(axis=-1)

按照 Jamie 和 Dougal 的 cmets,我们可以进一步概括这一点:

n = 2                   #Height of window
m = 2                   #Width of window
k = arr.shape[0] / n    #Must divide evenly
l = arr.shape[1] / m    #Must divide evenly
arr.reshape(k,n,l,m).max(axis=(-1,-3))              #Numpy >= 1.7.1
arr.reshape(k,n,l,m).max(axis=-3).max(axis=-1)      #Numpy <  1.7.1

【讨论】:

  • 请注意,数组没有理由必须是方形的,只要它们是可整除的;我想你可以把它改成k = arr.shape[0] / n; l = arr.shape[1] / n; arr.reshape(k, n, l, n).swapaxes(1, 2).reshape(k, l, n*n).max(axis=-1)
  • 交换轴后的最后一次整形会触发完整数组的副本,这对于大型数组来说可能代价高昂。最好的选择是完全跳过它并(使用 numpy > 1.7)给.max 一个轴元组,即arr.reshape(2,2,2,2).max(axis=(-1, -3)).max打两次电话,即arr.reshape(2,2,2,2).max(axis=-3).max(axis=-1)
  • @Jaime,谢谢。使用 numpy 1.7.1 和 40x40 的 arr,arr.reshape(2,2,2,2).max(axis=(-1, -3)) 不会提高性能,但 arr.reshape(2,2,2,2).max(axis=-3).max(axis=-1) 大约快 1.5 倍
  • @Dougal 好主意,也可以将其进一步推广到任意窗口!。
  • @Jamie 好点。更新了最终函数,但保留其余的用于教学解释。
【解决方案2】:

正如我在评论区提到的,考虑使用 NumBa。你可以让你的双循环保持原样,在装饰器中添加大约 10 个字符,并为此获得类似 C 的性能。如果您使用 Continuum Analytics 的“Anaconda”Python 发行版,开箱即用即可轻松使用。

这几乎是 NumBa 的完美用例,因为这种算法用双循环更自然地表达。重塑方法利用了快速数组操作,但除非您已经知道程序的目标,否则它极难阅读。非常希望将这样的函数保留在扩展形式中,并通过让其他东西在事后转换为低级语言来实现速度。

【讨论】:

  • 我知道 numba 和相应的速度,但我正在寻找一个纯粹的 numpy 解决方案。谢谢
  • 我很好奇 numba 在这个问题上的实际表现,所以我试了一下:nbviewer.ipython.org/c22a894f260d17876f01。在我对这些函数的略微修改版本的测试中,在 200x200 矩阵上,原始代码耗时 100 毫秒,numba JIT 版本耗时约 85 毫秒,@Ophion 版本耗时 0.5 毫秒。扩大规模,在 2k x 2k 矩阵上,numba 需要 8 秒,而 Ophion 需要 64 毫秒。大约 150 倍的加速可能值得在可读性上有所损失;你知道我在这里做错了什么导致 numba 做得这么差吗?
  • 不确定,但对于这种内核,这听起来很不寻常。我去看看笔记本。
  • 实际上,我将 max 改写为数组操作(因为这似乎是最自然的);我只是将它作为一个显式循环进行了尝试,无论如何它更像是 OP 的代码,而且它做得更好(大约是 Ophion 的 4 倍,而不是 125 倍)。更新了笔记本。
  • 不幸的是,您似乎必须自己写出寻找最大值的操作;您不能调用 max(arr[ii, jj] for ii in ... for jj in ...),因为不支持生成器表达式,并且使用列表推导式执行此操作会减慢 6 倍。
猜你喜欢
  • 2017-09-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-05-30
  • 2017-10-13
  • 1970-01-01
  • 1970-01-01
  • 2020-06-21
相关资源
最近更新 更多