从numpy数组的每一行索引不同的索引范围

wtlkbnrh  于 2023-05-17  发布在  其他
关注(0)|答案(3)|浏览(147)

我有两个形状为m的incidies数组。我需要取一个形状为m x n的数组的指数之间的值的平均值。可以不遍历每一行来完成这个任务吗?最快的方法是什么?

idx0 = np.array([1, 3, 2, 5])
idx1 = np.array([5, 8, 6, 7])
d = np.array([[1,2,3,4,5,6,7,8,9], [1,2,3,4,5,6,7,8,9], [1,2,3,4,5,6,7,8,9], [1,2,3,4,5,6,7,8,9]])

预期结果:

<< d[np.arange(d.shape[0]), idx0:idx1]
>> np.array([3.5, 6, 4.5, 6.5])

我发现的最好的方法是列表解析或使用numba的for循环,但这似乎是一个常见的问题?谢谢。

txu3uszq

txu3uszq1#

在您的情况下,您仍然需要从idx0idx1中获取索引的成对范围的切片。
zip + slice是一个简单的方法:

[d[:, slice(*a)].mean() for a in zip(idx0, idx1)]
[3.5, 6.0, 4.5, 6.5]
j13ufse2

j13ufse22#

如果你使用d.ravel()而不是d,事情会更容易,因为现在你可以使用np.add.reduceat找到元素的和,并直接计算平均值。
d.ravel()中对应于idx0idx1的线性索引为

idx0r = idx0 + d.shape[1] * np.arange(d.shape[0])
idx1r = idx1 + d.shape[1] * np.arange(d.shape[0])

要获得要求和的范围的线性索引,可以执行以下操作

idx = (np.stack((idx0, idx1), axis=-1) + d.shape[1] * np.arange(d.shape[0])[:, None]).ravel()

你想要一个1D数组的原因是,如果最后一个元素等于d.size,你需要能够删除它,因为np.add.reduceat不接受超过数组末尾的索引:

if idx[-1] == d.size:
    idx = idx[:-1]

最后一个操作不复制数据:它只是创建了一个少了一个元素的视图。
现在可以直接求和:

sums = np.add.reduceat(d.ravel(), idx)[::2]

需要每隔一个元素取一次,因为idx1的第n个元素与idx0的第n+1个元素之间的和也存在于和中。
现在,您可以通过每个段的长度进行归一化:

means = sums / (idx1 - idx0)

TL;DR

idx = (np.stack((idx0, idx1), axis=-1) + d.shape[1] * np.arange(d.shape[0])[:, None]).ravel()
if idx[-1] == d.size:
    idx = idx[:-1]
means = np.add.reduceat(d.ravel(), idx)[::2] / (idx1 - idx0)
czfnxgou

czfnxgou3#

另一种方法是屏蔽而不是索引。首先创建遮罩:

mask = np.zeros((d.shape[0], d.shape[1] + 1), np.int8)
mask[np.arange(d.shape[0]), idx0] = 1
mask[np.arange(d.shape[0]), idx1] = -1
mask = mask.cumsum(axis=1)[:, :-1].astype(bool)

初始数组创建时多了一列,以适应idx1包含位于d.size[1]的元素的情况。
有许多方法你可以采取前进。
1.第一个可能是最简单的:使用掩码数组:

means = np.ma.array(d, mask=~mask).mean(axis=1)

1.另一种方法是将数据预乘以掩码并计算自己的总和:

means = (d * mask).sum(axis=1) / (idx1 - idx0)

1.第三种方法是应用掩码并使用np.add.reduceat,类似于另一个答案的建议,但在掩码数据上,这避免了添加不必要的数字:

n = (idx1 - idx0)
means = np.add.reduceat(d[mask], np.r_[0, n[:-1].cumsum()]) / n

相关问题