使用numpy.bincount和数组权重

时间:2011-03-05 17:10:04

标签: python numpy

我想使用bincount对数组求和,但它只支持双精度数。 例如,这有效:

np.bincount([1, 1, 0],weights=np.array([1, 2, 4]))
Out: array([ 4.,  3.])

但是我想使用维度2数组:

np.bincount([1, 1, 0],weights=np.array([[1,1], [2,2], [4,4]]))
ValueError: object too deep for desired array

所需的输出是:

Out: array([[ 4.,  4.],[3., 3.]])

评论后更好的解释:

我想将数组的每一行加到相应的索引中。

循环将是:

Bin=np.zeros(2,2)
for i in [1,1,0]:
    Bin[i]+=a[i]

a是之前的3x2矩阵 有没有一种有效的方法来获得这个结果?

3 个答案:

答案 0 :(得分:3)

根据numpy文档:

numpy.bincount(x, weights=None, minlength=None)
  

权重:array_like,可选;   权重,与x相同形状的数组。

所以你不能以这种方式直接使用bincount,除非你以某种方式改变x

编辑:所以我提出了一个稍微有点棘手的方法,但是当你去大型数组时,无法保证性能。基本上我将利用scipy稀疏矩阵处理相同索引处的重复条目(它们求和):

 from scipy.sparse import *
 a = np.array([[1,1], [2,2], [4,4]])
 ii = np.array([1, 1, 0])

 ares = a.reshape((-1,),order='F')
 # ares == array([1, 2, 4, 1, 2, 4])

 col = np.tile(ii,(a.shape[1],))
 # col == np.array([1, 1, 0, 1, 1, 0])

 row = np.tile([0,1],(a.shape[0],1)).reshape((-1,),order='F') 
 # row == np.array([0,0,0,1,1,1]) 

 g = coo_matrix((ares,(col,row)),shape=(2,2))
 print g.todense()     

现在,您必须将此概括为您的精确数据。基本思想是你想要将每个数据点映射到结果数组的正确元素,然后让稀疏数组处理对重复条目的求和。

否则,如果你被迫使用循环来解决这个问题,我会考虑使用Cython。

编辑2:对于踢球,我计时两种不同的方法:

import numpy as np
from scipy.sparse import *

def method1():
    return np.array([np.bincount(ii, r) for r in a.T]).T

def method2():
    ares = a.reshape((-1,),order='F')
    col = np.tile(ii,(a.shape[1],))
    row = np.tile(np.arange(a.shape[1]),(a.shape[0],1)).reshape((-1,),order='F') 

    return coo_matrix((ares,(col,row)),shape=(np.unique(ii).size,a.shape[1])).todense()

if __name__ == '__main__':
    from timeit import Timer

    a = np.random.randint(0,1000,(1000000,3))
    ii = np.random.randint(0,10,(a.shape[0],))

    N = 100
    t1 = Timer("method1()", "from __main__ import method1")
    t2 = Timer("method2()", "from __main__ import method2")
    print 't2/t1: %f' % (t2.timeit(N)/t1.timeit(N))

在我的机器上,method2method1慢约3-5倍,具体取决于输入的形状,因此循环不一定是不好的选择。

答案 1 :(得分:1)

我认为这是基于bincount的更简单的版本。本质上,您要按列划分权重,以使其具有与初始数组相同的大小,然后将不同的列堆叠在一起。

a = np.array([1, 1, 0])
b = np.array([[1,1], [2,2], [4,4]])
uni = np.unique(a)

a_x = np.bincount(a,  weights=b[:,0], minlength=len(uni))
a_y = np.bincount(a,  weights=b[:,1], minlength=len(uni))

final = np.column_stack((a_x.T, a_y.T))
# final = np.array([[ 4.,  4.],[3., 3.]])

答案 2 :(得分:0)

您应该使用scipy csr矩阵表示索引,然后使用点积与数据。在我的笔记本电脑上,大型矩阵的速度比@JoshAdel的method1快14倍,比@JoshAdel的method2快54倍。

def method1():
    return np.array([np.bincount(ii, r) for r in a.T]).T

def method2():
    ares = a.reshape((-1,),order='F')
    col = np.tile(ii,(a.shape[1],))
    row = np.tile(np.arange(a.shape[1]),(a.shape[0],1)).reshape((-1,),order='F') 

    return coo_matrix((ares,(col,row)),shape=(ii.max()+1,a.shape[1])).todense()

def method3():
    csr = csr_matrix((np.ones(ii.shape[0]), (ii, np.arange(ii.shape[0]))))
    return csr*a

让我们生成随机数据并为其计时:

n = 1<<18
d = 512
ii = np.random.randint(low=1, high=1<<10, size=n)
a = np.random.randn((n, d))

%timeit method1()
# 1 loop, best of 3: 3.13 s per loop

%timeit method2()
# 1 loop, best of 3: 11.7 s per loop

%timeit method3()
# 1 loop, best of 3: 216 ms per loop

# sanity checks:
assert (method1() == method2()).all()
assert (method1() == method3()).all()

注意:我将np.unique(ii).size中的method2替换为ii.max()+1