我在(3,m)数组中有一些数据。
我有另一个(n,3)形状的面具阵列。此掩码的行是布尔过滤器,需要在执行某些功能之前应用于数据阵列。是否有矢量化方法来应用过滤器并计算函数?
这是一个使用循环的示例,为清晰起见,假设函数是一个mean()。我想使用纯粹的Numpy(没有列表理解)这样做。
(显然,阵列的大小实际上要大得多。)
import numpy as np
data = np.array([
[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]
])
masks = np.array([
[True, True, False],
[False, True, False],
[False, True, True],
[True, False, False],
[True, False, True]
])
means = np.array([data[mask].mean(axis=0) for mask in masks])
# means
array([[ 2., 3., 4., 5.],
[ 4., 5., 6., 7.],
[ 6., 7., 8., 9.],
[ 0., 1., 2., 3.],
[ 4., 5., 6., 7.]])
答案 0 :(得分:0)
这感觉有点粗糙和混乱,但它确实没有循环。
有两个主要任务:
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)数组,但通过将reduceat
行清零来避免需要False
。这样,可以在不影响价值的情况下进入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
或其方法中定义。
答案 1 :(得分:0)
所以,在玩了一段时间之后,似乎这种广播适用于mean()作为具体功能:
means = (masks[:, :, np.newaxis] * data).sum(axis=1) / masks.sum(axis=1)[:, np.newaxis]
# means
array([[ 2., 3., 4., 5.],
[ 4., 5., 6., 7.],
[ 6., 7., 8., 9.],
[ 0., 1., 2., 3.],
[ 4., 5., 6., 7.]])
对于更普遍的其他功能,您可以使用此格式(其中mean()可以替换为所需的功能):
means = (masks[:, :, np.newaxis] * data).mean(axis=1) * masks.shape[1] / masks.sum(axis=1)[:, np.newaxis]
# means
array([[ 2., 3., 4., 5.],
[ 4., 5., 6., 7.],
[ 6., 7., 8., 9.],
[ 0., 1., 2., 3.],
[ 4., 5., 6., 7.]])
答案 2 :(得分:0)
matrix-multiplication
使用np.dot
可以轻松解决该问题,因此必须非常高效。这是实施 -
np.true_divide(masks.dot(data),masks.sum(1)[:,None])