矢量化 Numpy 切片操作



>假设我有一个 Numpy 向量,

A = zeros(100)

例如,我通过索引为A的断点列表将其划分为子向量,

breaks = linspace(0, 100, 11, dtype=int)

因此,第 i 个子向量将位于索引breaks[i](包含)和breaks[i+1](排除)之间。中断不一定是等距的,这只是一个例子。但是,它们将始终严格增加。

现在我想对这些子向量进行操作。例如,如果我想将第 i 个子向量的所有元素设置为 i ,我可能会这样做:

for i in range(len(breaks) - 1):
    A[breaks[i] : breaks[i+1]] = i

或者我可能想计算子向量均值:

b = empty(len(breaks) - 1)
for i in range(len(breaks) - 1):
    b = A[breaks[i] : breaks[i+1]].mean()

等等。

如何避免使用for循环,而是对这些操作进行矢量化处理?

您可以使用

简单的np.cumsum -

import numpy as np
# Form zeros array of same size as input array and 
# place ones at positions where intervals change
A1 = np.zeros_like(A)
A1[breaks[1:-1]] = 1
# Perform cumsum along it to create a staircase like array, as the final output
out = A1.cumsum()

示例运行 -

In [115]: A
Out[115]: array([3, 8, 0, 4, 6, 4, 8, 0, 2, 7, 4, 9, 3, 7, 3, 8, 6, 7, 1, 6])
In [116]: breaks
Out[116]: array([ 0,  4,  9, 11, 18, 20])
In [142]: out
Out[142]: array([0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 3, 3, 3, 3, 3, 3, 3, 4, 4]..)
<小时 />

如果你想从A获得这些子向量的平均值,你可以使用 np.bincount -

mean_vals = np.bincount(out, weights=A)/np.bincount(out)

如果您希望扩展此功能并改用自定义函数,您可能需要查看 MATLAB 的 accumarray 等效项 Python/Numpynumpy_groupies其源代码可在此处获得。

您的问题实际上没有一个单一的答案,但有几种技术可以用作构建块。另一个您可能会发现有用的:

所有 numpy ufuncs 都有一个.reduceat方法,您可以利用它来进行一些计算:

>>> a = np.arange(100)
>>> breaks = np.linspace(0, 100, 11, dtype=np.intp)
>>> counts = np.diff(breaks)
>>> counts
array([10, 10, 10, 10, 10, 10, 10, 10, 10, 10])
>>> sums = np.add.reduceat(a, breaks[:-1], dtype=np.float)
>>> sums
array([  45.,  145.,  245.,  345.,  445.,  545.,  645.,  745.,  845.,  945.])
>>> sums / counts  # i.e. the mean
array([  4.5,  14.5,  24.5,  34.5,  44.5,  54.5,  64.5,  74.5,  84.5,  94.5])
您可以使用

np.repeat

In [35]: np.repeat(np.arange(0, len(breaks)-1), np.diff(breaks))
Out[35]: 
array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2,
       2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4,
       4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6,
       6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9,
       9, 9, 9, 9, 9, 9, 9, 9])

要计算任意分箱统计信息,您可以使用scipy.stats.binned_statistic

import numpy as np
import scipy.stats as stats
breaks = np.linspace(0, 100, 11, dtype=int)
A = np.random.random(100)
means, bin_edges, binnumber = stats.binned_statistic(
    x=np.arange(len(A)), values=A, statistic='mean', bins=breaks)

stats.binned_statistic可以计算均值、中位数、计数、总和;或者,要计算每个箱的任意统计信息,可以将可调用对象传递给 statistic 参数:

def func(values):
    return values.mean()
funcmeans, bin_edges, binnumber = stats.binned_statistic(
    x=np.arange(len(A)), values=A, statistic=func, bins=breaks)
assert np.allclose(means, funcmeans)

最新更新