加快对矩阵中某些列的求和

时间:2014-09-05 07:49:15

标签: python algorithm numpy sparse-matrix slice

简短问题

给定一个大的稀疏csr_matrix A和一个numpy数组B,构造numpy矩阵C的最快方法是什么,C[i,j] = sum(A[k,j])为所有k 1}} B[k] == i

问题详情

我找到了这样做的解决方案,但我并不满足于需要多长时间。我将首先解释问题,然后是我的解决方案,然后显示我的代码,然后显示我的时间。

问题 我正在研究Python中的聚类算法,我想加快速度。我有一个稀疏的csr_matrix pam,其中每篇文章每个人都有多少他们购买的文章。此外,我有一个numpy数组clustering,其中表示该人所属的群集。例如:

      pam                pam.T              clustering
    article              person
p [[1 0 0 0]      a 
e  [0 2 0 0]      r [[1 0 1 0 0 0]         [0 0 0 0 1 1]
r  [1 1 0 0]      t  [0 2 1 0 0 0]
s  [0 0 1 0]      i  [0 0 0 1 0 1]
o  [0 0 0 1]      c  [0 0 0 0 1 2]]
n  [0 0 1 2]]     l
                  e

我想要计算的是acm:一个群集中所有人一起购买的项目数量。对于i中的每个列acm,这相当于为p添加pam.T的{​​{1}}列。

clustering[p] == i

解决方案 首先,我创建了另一个稀疏矩阵 acm cluster a r [[2 0] t [3 0] i [1 1] c [0 3]] l e ,如果人pcm在群集[i,j]中,我会在其中指示每个元素i。结果(当投射到密集矩阵时):

j

接下来,我将 pcm cluster p [[False True] e [False True] r [ True False] s [False True] o [False True] n [ True False]] pam.T进行矩阵乘以得到我想要的矩阵。

代码的 我编写了以下程序来测试这种方法在实践中的持续时间。

pcm

时序 事实证明,对于这100次运行,我需要5.1秒。其中3.6秒用于创建import numpy as np from scipy.sparse.csr import csr_matrix from timeit import timeit def _clustering2pcm(clustering): ''' Converts a clustering (np array) into a person-cluster matrix (pcm) ''' N_persons = clustering.size m_person = np.arange(N_persons) clusters = np.unique(clustering) N_clusters = clusters.size m_data = [True] * N_persons pcm = csr_matrix( (m_data, (m_person, clustering)), shape = (N_persons, N_clusters)) return pcm def pam_clustering2acm(): ''' Convert a person-article matrix and a given clustering into an article-cluster matrix ''' global clustering global pam pcm = _clustering2pcm(clustering) acm = csr_matrix.transpose(pam).dot(pcm).todense() return acm if __name__ == '__main__': global clustering global pam N_persons = 200000 N_articles = 400 N_shoppings = 400000 N_clusters = 20 m_person = np.random.choice(np.arange(N_persons), size = N_shoppings, replace = True) m_article = np.random.choice(np.arange(N_articles), size = N_shoppings, replace = True) m_data = np.random.choice([1, 2], p = [0.99, 0.01], size = N_shoppings, replace = True) pam = csr_matrix( (m_data, (m_person, m_article)), shape = (N_persons, N_articles)) clustering = np.random.choice(np.arange(N_clusters), size = N_persons, replace = True) print timeit(pam_clustering2acm, number = 100) 。我觉得可以有一种更快的方法来计算这个矩阵而不创建一个临时的稀疏矩阵,但我没有看到没有循环的。有更快的建设方式吗?

修改

在Martino的回答之后,我试图在集群和切片算法上实现循环,但这甚至更慢。现在花费12.5秒计算pcm 100次,如果我删除行acm,则剩余4.1秒。

acm[:,i] = pam[p,:].sum(axis = 0)

2 个答案:

答案 0 :(得分:4)

这比您的_clustering2pcm功能快约50倍:

def pcm(clustering):
    n = clustering.size
    data = np.ones((n,), dtype=bool)
    indptr = np.arange(n+1)
    return csr_matrix((data, clustering, indptr))

我没有查看源代码,但是当你传递CSR构造函数(data, (rows, cols))结构时,几乎可以肯定使用它来创建COO矩阵,然后将其转换为CSR。因为您的矩阵非常简单,所以很容易将实际的CSR矩阵描述数组放在一起,如上所述,并跳过所有这些。

这几乎可以将执行时间缩短三倍:

In [38]: %timeit pam_clustering2acm()
10 loops, best of 3: 36.9 ms per loop

In [40]: %timeit pam.T.dot(pcm(clustering)).A
100 loops, best of 3: 12.8 ms per loop

In [42]: np.all(pam.T.dot(pcm(clustering)).A == pam_clustering2acm())
Out[42]: True

答案 1 :(得分:1)

我推荐你到scipy.sparse文档(http://docs.scipy.org/doc/scipy/reference/generated/scipy.sparse.csr_matrix.html#scipy.sparse.csr_matrix)。他们说行切片是有效的(而不是列拼接),所以坚持非转置矩阵可能更好。然后,如果向下浏览,则可以指定轴的和函数。使用对象附带的方法可能更好,因为它们可能使用已编译的代码。这是以循环群集为代价的(我假设它没有太多)。