numpy.repeat()创建块对角线索引?

时间:2015-06-09 17:31:38

标签: python numpy scipy

我试图弄清楚如何加速以下Python代码。
基本上,代码构建矩阵visibleChildren的外积的矩阵并将其存储为块对角稀疏矩阵。 br />我使用C在块对角线中构建索引。
对代码进行分析后发现,对numpy.repeat()的调用大约占执行时间的50%。

numpy.repeat()

最初,我正在构建稀疏矩阵,如下所示

import numpy as np
import scipy.sparse as spspar

L = 1000
K = 100
C = np.random.randn(L,K)

# From the matrix of outter products of C and store in block_diagonal 
# sparse matrix
CCt = np.einsum('ij...,i...->ij...',C,C)

# create indices into the block diagonal sparse coo matrix
i = np.tile(np.tile(np.arange(K),K),L) + K*np.repeat(np.arange(L),K*K)
j = np.tile(np.repeat(np.arange(K),K),L) + K*np.repeat(np.arange(L),K*K)

# store as block diagonal sparse coo matrix
BlckCCt = spspar.coo_matrix((CCt.flatten(),(j,i)),shape=(K*K*L,K*K*L))

这太慢而且内存密集。

感谢您的任何意见。

编辑:我使用ipython timeit比较了@ hjpaul的建议。这是我能报告的内容

BlckCCt = spspar.block_diag(CCt,"coo")

所以看起来它们都需要大约相同的数量(我对ipython性能分析很新,所以也许我并没有以正确的方式比较它们。)

1 个答案:

答案 0 :(得分:3)

仅供参考,您的

CCt = np.einsum('ij...,i...->ij...',C,C)

相同
CCt1=C[:,None,:]*C[:,:,None]

生成(L,K,K)数组。对于我较小的测试用例np.einsum快2倍。

sparse.block_diag将每个子矩阵转换为coo,并将其传递给sparse.bmatbmat将所有子矩阵的rowscolsdata收集到类似于j, i的大数组中,并使用coo_matrix调用ipython那些。

对各个部分执行timeit K*np.repeat(np.arange(L),K*K),我同意tile是最慢的代码块。例如,比repeat一块要慢得多。

由于您对ij执行相同的kk= K*np.repeat(np.arange(L),K*K) ii=np.tile(np.tile(np.arange(K),K),L) + kk jj=np.tile(np.repeat(np.arange(K),K),L) + kk ,您只能执行一次,并使用该变量两次吗?

repeat

我会更多地看一下那件作品,但那是一个开始。

(np.zeros((K*K,),int)+np.arange(L)[:,None]).flatten()*K

略有改善(20%)
np.tile(np.arange(L)*K,K*K).reshape(L,K*K).T.flatten()

甚至更好(> 2x)

*K

我已将arange(L)移至较小的tile,并使用更快的.T.flatten(K*K,L)负责更改订单。

根据评论,重塑应为K。我正在测试那些并不重要的价值观。这些替代方案的相对速度因Li的相对大小而异。

jkk的第一部分的平铺是可选的,如果CCt(第2部分)是形状(L,K,K)(如(0,4,0) })。是否节省时间尚不清楚。跨度比完全平铺版本(4,) v。i = (np.arange(K)[None,None,:] + kk.reshape(L,K,K)).flatten() j = (np.arange(K)[None,:,None] + kk.reshape(L,K,K)).flatten() 更复杂。)

kk

我们可以对k1 = K*np.arange(L)[:,None,None]

做同样的事情
np.arange(K)[None,None,:] + k1

i = np.tile( np.arange(K)[None,None,:] + k1, (1,K,1)).flatten() j = np.tile( np.arange(K)[None,:,None] + k1, (1,1,K)).flatten() 是(L,1,K),所以我们需要将其平铺

np.ix_

生成这些数组的另一种方法是使用i = np.sum(np.ix_(K*np.arange(L), np.arange(K), np.zeros(K))) j = np.sum(np.ix_(K*np.arange(L), np.zeros(K), np.arange(K))) 重新整形范围,然后只对值进行求和。

.flatten

(根据需要添加{{1}})。我已经在小尺寸上对它进行了测试,看起来是正确的。我不知道速度。