这感觉有点粗糙和混乱,但它确实可以在没有循环的情况下工作。
有两个主要任务:
- 扩展
data,以便可以使用masks 对其进行索引 - 从 (5,4) 到 (5,3,4)
- 将
means 应用于行组;我能找到的最接近的是np.sum.reduceat。
构造reduceat 索引:
In [253]: cnt = masks.sum(axis=1)
In [254]: cnt1=np.concatenate(([0],np.cumsum(cnt)[:-1]))
In [255]: cnt
Out[255]: array([2, 1, 2, 1, 2]) # True count per row
In [256]: cnt1
Out[256]: array([0, 2, 3, 5, 6]) # reduceat index positions
展开data和mask:
In [257]: mdata=data[None,...].repeat(masks.shape[0],0)[masks,:]
add 行并除以每组的行数
In [258]: np.add.reduceat(mdata,cnt1,0)/cnt[:,None]
Out[258]:
array([[ 2., 3., 4., 5.],
[ 4., 5., 6., 7.],
[ 6., 7., 8., 9.],
[ 0., 1., 2., 3.],
[ 4., 5., 6., 7.]])
如果有帮助:
In [263]: mdata
Out[263]:
array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 4, 5, 6, 7],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11],
[ 0, 1, 2, 3],
[ 0, 1, 2, 3],
[ 8, 9, 10, 11]])
获得此mdata 的可能更好的方法是
In [285]: data[np.where(masks)[1],:]
Out[285]:
array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 4, 5, 6, 7],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11],
[ 0, 1, 2, 3],
[ 0, 1, 2, 3],
[ 8, 9, 10, 11]])
where(...)[1] 是 masks 中 True 的列位置,也就是我们要从 data 中选择的行。
============================
@capitalistcuttle 也创建了一个 (5,3,4) 数组,但通过清零 False 行来避免对 reduceat 的需要。这样就可以进入mean 或sum 而不影响值。这让我想起了屏蔽数组是如何执行这样的任务的。他们fill 屏蔽值,其值类似于 0 或 1,不会影响计算。
受此启发,这是一个 MaskedArray 解决方案
将 data 和 masks 都扩展为 (5,3,4) 大小:
In [322]: data1=data[None,:,:].repeat(5,0)
In [323]: masks1=masks[:,:,None].repeat(4,-1)
In [324]: data1.shape, masks1.shape
Out[324]: ((5, 3, 4), (5, 3, 4))
从中制作掩码数组:
In [325]: madata=np.ma.MaskedArray(data1,~masks1)
In [326]: madata
Out[326]:
masked_array(data =
[[[0 1 2 3]
[4 5 6 7]
[-- -- -- --]]
[[-- -- -- --]
[4 5 6 7]
[-- -- -- --]]
...
[[0 1 2 3]
[-- -- -- --]
[8 9 10 11]]],
mask =
[[[False False False False]
[False False False False]
[ True True True True]]
[[ True True True True]
[False False False False]
[ True True True True]]
...],
fill_value = 999999)
现在我们可以简单地使用 mean 方法,让它负责填充 0 并调整有效行数。
In [327]: madata.mean(axis=1)
Out[327]:
masked_array(data =
[[2.0 3.0 4.0 5.0]
[4.0 5.0 6.0 7.0]
[6.0 7.0 8.0 9.0]
[0.0 1.0 2.0 3.0]
[4.0 5.0 6.0 7.0]],
mask =
[[False False False False]
[False False False False]
[False False False False]
[False False False False]
[False False False False]],
fill_value = 1e+20)
即.data属性转换回常规数组。
这种 MaskedArray 方法可能更慢,因为它创建了一个更大的数组,但它可能更通用 - 它可以用于操作,只要它们在 np.ma 或其方法中定义。