我想迭代地构建稀疏矩阵,并注意到根据SciPy文档有两个合适的选项:
class scipy.sparse.lil_matrix(arg1,shape = None,dtype = None, copy = False)[source]基于行的链表稀疏矩阵
这是构造稀疏矩阵的有效结构 递增。
class scipy.sparse.dok_matrix(arg1,shape = None,dtype = None, copy = False)[source]基于密钥的字典稀疏矩阵。
这是构造稀疏矩阵的有效结构 递增。
但是,当我运行基准测试比建立一个价值字典字典(后来可以很容易地转换为稀疏矩阵)时,后者比使用任何一个字符串快10到20倍。稀疏矩阵模型:
from scipy.sparse import dok_matrix, lil_matrix
from timeit import timeit
from collections import defaultdict
def common_dict(rows, cols):
freqs = defaultdict(lambda: defaultdict(int))
for row, col in zip(rows, cols):
freqs[row][col] += 1
return freqs
def dok(rows, cols):
freqs = dok_matrix((1000,1000))
for row, col in zip(rows, cols):
freqs[row,col] += 1
return freqs
def lil(rows, cols):
freqs = lil_matrix((1000,1000))
for row, col in zip(rows, cols):
freqs[row,col] += 1
return freqs
def benchmark():
cols = range(1000)
rows = range(1000)
res = timeit("common_dict({},{})".format(rows, cols),
"from __main__ import common_dict",
number=100)
print("common_dict: {}".format(res))
res = timeit("dok({},{})".format(rows, cols),
"from __main__ import dok",
number=100)
print("dok: {}".format(res))
res = timeit("lil({},{})".format(rows, cols),
"from __main__ import lil",
number=100)
print("lil: {}".format(res))
结果:
benchmark()
common_dict: 0.11778324202168733
dok: 2.2927695910912007
lil: 1.3541790939634666
什么导致矩阵模型产生这样的开销,是否有某种方法可以加速它?是否存在使用dok或lil比使用dicts的常见词典更喜欢的用例?
答案 0 :(得分:13)
当我将2个稀疏数组的+=
更改为=
时:
for row, col in zip(rows, cols):
#freqs[row,col] += 1
freqs[row,col] = 1
他们各自的时间减少了一半。消耗最多时间的是索引。使用+=
时,必须同时执行__getitem__
和__setitem__
。
当文档说dok
和lil
更适合迭代构建时,它们意味着扩展其基础数据结构比其他格式更容易。
当我尝试使用您的代码制作csr
矩阵时,我得到了一个:
/usr/lib/python2.7/dist-packages/scipy/sparse/compressed.py:690:SparseEfficiencyWarning:更改csr_matrix的稀疏结构非常昂贵。 lil_matrix效率更高。 SparseEfficiencyWarning)
速度减慢30倍。
因此速度声明与csr
等格式相关,而不是相对于纯Python或numpy
结构。
您可能希望查看dok_matrix.__get_item__
和dok_matrix.__set_item__
的Python代码,了解执行freq[r,c]
时会发生什么。
构建dok
的更快捷方式是:
freqs = dok_matrix((1000,1000))
d = dict()
for row, col in zip(rows, cols):
d[(row, col)] = 1
freqs.update(d)
利用dok
是子类字典的事实。请注意,dok
矩阵不是字典字典。它的键是(50,50)
等元组。
构建相同稀疏数组的另一种快速方法是:
freqs = sparse.coo_matrix((np.ones(1000,int),(rows,cols)))
换句话说,既然您已经拥有rows
和cols
数组(或范围),请计算相应的data
数组,然后构造稀疏数组。
但是如果您必须在增量增长步骤之间对矩阵执行稀疏操作,那么dok
或lil
可能是您的最佳选择。
为线性代数问题开发了稀疏矩阵,例如求解具有大稀疏矩阵的线性方程。几年前我在MATLAB中使用它们来解决有限差分问题。对于这项工作,计算友好csr
格式是最终目标,coo
格式是一种方便的初始化格式。
现在,许多SO scipy稀疏问题都来自scikit-learn
和文本分析问题。它们还用于生物数据库文件中。但仍然(data),(row,col)
定义方法效果最好。
因此,稀疏矩阵从未用于快速增量创建。像字典和列表这样的传统Python结构要好得多。
这是一个利用其字典方法的更快dok
次迭代。 update
似乎与普通词典一样快。 get
的等效索引(freq[row,col]
)快约3倍。索引可能使用get
,但必须有很多开销。
def fast_dok(rows, cols):
freqs = dok_matrix((1000,1000))
for row, col in zip(rows,cols):
i = freqs.get((row,col),0)
freqs.update({(row,col):i+1})
return freqs
跳过get
,然后执行
freqs.update({(row,col): 1)
甚至更快 - 比defaultdict示例的defaultdict更快,并且几乎与简单字典初始化({(r, c):1 for r,c in zip(rows, cols)}
)
答案 1 :(得分:1)
您的测试不公平的原因有多种。首先,您将包含构造稀疏矩阵的开销作为定时循环的一部分。
其次,可以说更重要的是,您应该使用设计用的数据结构,同时对整个数组进行操作。也就是说,不是迭代行和列并且每次添加1,而只需将1添加到整个数组。