2015-04-27 27 views
5

说我有一个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循环,而是将这些操作向量化?

+0

是'breaks'预排序? – Divakar

+0

@Divakar:是的,他们正在严格增加。 – cfh

+0

另外,中断的限制是否覆盖了整个“A”,即是否会有一些A的元素在这个操作之后不会被改变? – Divakar

回答

5

对于你的问题确实没有一个单一的答案,但你可以使用几种技巧作为构建模块。另外一个可能对您有用:

所有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]) 
3

你可以使用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) 
+0

但是,我现在如何在避开for循环的同时将'i'-th部分设置为'i'? – cfh

6

您可以使用简单的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等效:accum其源代码可用here

+0

我喜欢你的方法;它比我的快。你也可以使用'A1 = np.zeros(break [-1])'。 – unutbu

+0

@unutbu啊谢谢!很高兴知道提示! – Divakar

+0

这解决了将每个子向量设置为常量的简单用例(这是作为示例的)。例如,如果我想计算每个子向量的平均值? – cfh