简短问题
给定一个大的稀疏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)
答案 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)。他们说行切片是有效的(而不是列拼接),所以坚持非转置矩阵可能更好。然后,如果向下浏览,则可以指定轴的和函数。使用对象附带的方法可能更好,因为它们可能使用已编译的代码。这是以循环群集为代价的(我假设它没有太多)。