如何在蒙版数组上做等价于block_reduce?

时间:2019-05-27 06:26:22

标签: python numpy scikit-image

我正在计算2D numpy数组中较小块的聚合值。我想以一种有效的方式(而不是for和if语句)从聚合操作中排除值0。

我正在使用skimage.measure.block_reducenumpy.ma.masked_equal,但似乎block_reduce忽略了掩码。

import numpy as np
import skimage
a = np.array([[2,4,0,12,5,7],[6,0,8,4,3,9]])
zeros_included = skimage.measure.block_reduce(a,(2,2),np.mean)

包括0并(正确)产生

zeros_included
array([[3., 6., 6.]])

我希望

masked = np.ma.masked_equal(a,0)
zeros_excluded = skimage.measure.block_reduce(masked,(2,2),np.mean)

会成功的,但仍然会产生

zeros_excluded
array([[3., 6., 6.]])

所需的结果将是:

array([[4., 8., 6.]])

我正在寻找一种实现正确结果的pythonesque方式,使用skimage是可选的。当然,我实际的数组和块比该示例大得多,因此需要效率。

感谢您的关注。

2 个答案:

答案 0 :(得分:1)

您可以使用np.nanmean,但必须修改原始数组或创建一个新数组:

import numpy as np
import skimage

a = np.array([[2,4,0,12,5,7],[6,0,8,4,3,9]])


b = a.astype("float")
b[b==0] = np.nan
zeros_excluded = skimage.measure.block_reduce(b,(2,2), np.nanmean)
zeros_excluded

# array([[4., 8., 6.]])

答案 1 :(得分:0)

block_reduce的核心代码是

blocked = view_as_blocks(image, block_size)
return func(blocked, axis=tuple(range(image.ndim, blocked.ndim)))

view_as_blocks使用as_strided创建数组的另一个视图:

In [532]: skimage.util.view_as_blocks(a,(2,2))                                                        
Out[532]: 
array([[[[ 2,  4],
         [ 6,  0]],

        [[ 0, 12],
         [ 8,  4]],

        [[ 5,  7],
         [ 3,  9]]]])

应用于蒙版数组时,它会产生相同的结果。实际上,它适用于masked.datanp.asarray(masked)。有些动作会保留子类,而不会。

In [533]: skimage.util.view_as_blocks(masked,(2,2))  
Out[533]: 
array([[[[ 2,  4],
         [ 6,  0]],
         ...

这就是为什么将np.mean应用于(2,3)轴时对屏蔽没有响应的原因。

np.mean应用于被屏蔽的数组会将操作委托给数组自己的方法,因此对屏蔽敏感:

In [544]: np.mean(masked[:,:2])                                                                       
Out[544]: 4.0
In [545]: masked[:,:2].mean()                                                                         
Out[545]: 4.0
In [547]: [masked[:,i:i+2].mean() for i in range(0,6,2)]                                              
Out[547]: [4.0, 8.0, 6.0]

np.nanmeanview_as_blocks一起使用,因为它不依赖于数组是特殊的子类。

我可以定义一个将遮罩应用于块视图的函数:

def foo(arr,axis):
    return np.ma.masked_equal(arr,0).mean(axis)

In [552]: skimage.measure.block_reduce(a,(2,2),foo)                                                   
Out[552]: 
masked_array(data=[[4.0, 8.0, 6.0]],
             mask=[[False, False, False]],
       fill_value=1e+20)

====

由于您的块不重叠,因此我将创建具有重塑和交换轴的块。

In [554]: masked.reshape(2,3,2).transpose(1,0,2)                                                      
Out[554]: 
masked_array(
  data=[[[2, 4],
         [6, --]],

        [[--, 12],
         [8, 4]],

        [[5, 7],
         [3, 9]]],
  mask=[[[False, False],
         [False,  True]],

        [[ True, False],
         [False, False]],

        [[False, False],
         [False, False]]],
  fill_value=0)

,然后将mean应用于最后两个轴:

In [555]: masked.reshape(2,3,2).transpose(1,0,2).mean((1,2))                                          
Out[555]: 
masked_array(data=[4.0, 8.0, 6.0],
             mask=[False, False, False],
       fill_value=1e+20)