Numpy:通过组合前一个数组

时间:2017-04-24 21:29:41

标签: python numpy scipy sparse-matrix

我有一个二进制稀疏的CSR数组。我想通过组合这个原始数组中的列来创建一个新数组。也就是说,我有一个"列组列表":[[1,10,3],[5,54,202],[12,199],[5],...]

对于这些"列组中的每一个"我想将原始数组中的列与OR运算结合起来(np.max适用于此)并将组合列添加到新矩阵中。

我目前的解决方案是使用hstack,但速度很慢:

for cg in column_groups:
    tmp = np.max(data_orig[:,cg].toarray(), axis=1, keepdims=True)
    data = np.hstack((data, tmp))

3 个答案:

答案 0 :(得分:2)

你基本上在每次迭代时选择max列。因此,我们可以选择所有列,然后使用np.maximum.reduceat来获得“intervaled-maximum”列,从而为我们提供矢量化解决方案,就像这样 -

def grouped_max(data_orig, column_groups):
    cols = np.hstack((column_groups))
    clens = np.hstack((0,np.cumsum(map(len,column_groups))[:-1]))
    all_data = data_orig[:,cols].toarray()
    return np.maximum.reduceat(all_data, clens,axis=1)

对于python 3.x版本,我们需要计算clens,就像这样 -

clens = np.hstack((0,np.cumsum(list(map(len,column_groups)))[:-1]))

因为loopy版本是沿着组迭代的,所以这个向量化的解决方案在使用大量组时会显示出它的好处。

示例运行 -

In [303]: # Setup sample csr matrix
     ...: a = np.random.randint(0,3,(12,28))
     ...: data_orig = sparse.csr_matrix(a)
     ...: 
     ...: # Random column IDs
     ...: column_groups = [[1,10,3], [5,14],[2]]
     ...: 
     ...: data = np.empty((12,0),dtype=int)
     ...: for cg in column_groups:
     ...:     tmp = np.max(data_orig[:,cg].toarray(), axis=1, keepdims=True)
     ...:     data = np.hstack((data, tmp))
     ...:     

In [304]: out = grouped_max(data_orig, column_groups)

In [305]: # Verify results between original and propsed ones
     ...: print np.allclose(out, data)
True

答案 1 :(得分:1)

我想,主要问题是hstack正在构建一个新的矩阵 - 在每次迭代中复制大量数据。

我还没有使用稀疏矩阵,所以我可能不合适,但据我从文档中可以理解,可以将它们切成普通的numpy数组。在这种情况下,预先分配数组并按列添加结果是个好主意:

rows = data_orig.shape[0]
cols = len(column_groups)
data = scipy.sparse.csr_matrix((rows, cols))
for cg in enumerate(column_groups):
    tmp = np.max(data_orig[:,cg[1]].toarray(), axis=1, keepdims=True)
    data[:, cg[0]] = tmp

答案 2 :(得分:1)

In [412]: data_orig=sparse.random(10,300,.2,'csr')
In [413]: data_orig = (data_orig>.5).astype(int)
In [414]: cg = [[1,10,3], [5,54,202], [12,199], [5]]
In [420]: data_orig
Out[420]: 
<10x300 sparse matrix of type '<class 'numpy.int32'>'
   with 299 stored elements in Compressed Sparse Row format>

对于重复加入,最好附加到列表,并叠加一次:

def test1(data_orig, cg):
    data = []
    for g in cg:
        temp=np.max(data_orig[:,g].A, axis=1,keepdims=True)
        data.append(temp)
    return np.hstack(data)

In [429]: test1(data_orig, cg)
Out[429]: 
array([[0, 1, 0, 1],
       [1, 1, 0, 0],
       [0, 0, 0, 0],
       [0, 1, 0, 0],
       [0, 1, 0, 0],
       [0, 0, 0, 0],
       [0, 0, 0, 0],
       [0, 1, 0, 0],
       [1, 0, 0, 0],
       [0, 0, 0, 0]], dtype=int32)

我可以将稀疏矩阵转换为密集矩阵,并得到相同的结果

In [431]: dataM=data_orig.todense()
In [432]: test1(dataM, cg)

我可以使用data_orig.A但是我必须省略函数中的.A

In [433]: timeit test1(data_orig, cg)
100 loops, best of 3: 2.52 ms per loop
In [434]: timeit test1(dataM, cg)
10000 loops, best of 3: 118 µs per loop

这些时间证实了我的猜测,即稀疏列索引相对较慢。

使用Divakar的reduceat版本:

In [451]: timeit grouped_max(data_orig, cg)
1000 loops, best of 3: 706 µs per loop
In [452]: timeit grouped_max(dataM, cg)
10000 loops, best of 3: 90.3 µs per loop

使用我自己的Py3自适应(差异只是风格):

def grouped_max(data_orig, column_groups):
    cols = np.hstack((column_groups))
    clens = np.hstack((0,np.cumsum([len(i) for i in cg])[:-1]))
    all_data = data_orig[:,cols].A
    return np.maximum.reduceat(all_data, clens,axis=1)

稀疏矩阵的加速很快。显然,一个较大的列选择比许多较小的列更快。密集矩阵的加速并不是那么重要。

这样的稀疏矩阵列选择实际上是用矩阵乘法执行的。它构造了另一个稀疏矩阵,其中包含所需列的1s,并执行dot乘积。行/列总和也使用矩阵乘积进行。

这是一个纯粹的稀疏版本:

def test2(data_orig, cg):
    data = []
    for g in cg:
        temp=data_orig[:,g].max(axis=1)
        data.append(temp)          # alt append(temp.A)
    return sparse.hstack(data)     #      np.hstack

In [465]: timeit test2(data_orig, cg).A
100 loops, best of 3: 3.21 ms per loop  (2.99 with np.hstack)