如何在屏蔽数组上做相当于 block_reduce 的操作?
How to do equivalent of block_reduce on a masked array?
我正在计算 2D numpy 数组中较小块的聚合值。我想以一种有效的方式(而不是 for 和 if 语句)从聚合操作中排除值 0。
我正在使用 skimage.measure.block_reduce
和 numpy.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.]])
我正在寻找一种 python 式的方法来获得正确的结果,使用 skimage 是可选的。当然我的实际数组和块比这个例子大得多,因此需要效率。
感谢您的关注。
您可以使用 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.]])
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.data
或 np.asarray(masked)
。有些动作保留了子类,这个没有。
In [533]: skimage.util.view_as_blocks(masked,(2,2))
Out[533]:
array([[[[ 2, 4],
[ 6, 0]],
...
这就是为什么应用于 (2,3) 轴的 np.mean
不响应掩码的原因。
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.nanmean
与 view_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)
我正在计算 2D numpy 数组中较小块的聚合值。我想以一种有效的方式(而不是 for 和 if 语句)从聚合操作中排除值 0。
我正在使用 skimage.measure.block_reduce
和 numpy.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.]])
我正在寻找一种 python 式的方法来获得正确的结果,使用 skimage 是可选的。当然我的实际数组和块比这个例子大得多,因此需要效率。
感谢您的关注。
您可以使用 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.]])
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.data
或 np.asarray(masked)
。有些动作保留了子类,这个没有。
In [533]: skimage.util.view_as_blocks(masked,(2,2))
Out[533]:
array([[[[ 2, 4],
[ 6, 0]],
...
这就是为什么应用于 (2,3) 轴的 np.mean
不响应掩码的原因。
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.nanmean
与 view_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)