スパース行列のゼロ以外のエントリをscipy.sparseで反復処理するのが最善の方法だと思います。たとえば、次のことを行う場合:
from scipy.sparse import lil_matrix
x = lil_matrix( (20,1) )
x[13,0] = 1
x[15,0] = 2
c = 0
for i in x:
print c, i
c = c+1
出力は
0
1
2
3
4
5
6
7
8
9
10
11
12
13 (0, 0) 1.0
14
15 (0, 0) 2.0
16
17
18
19
したがって、ゼロ以外のエントリだけでなく、反復子がすべての要素に触れているように見えます。私はAPIを見てきました
http://docs.scipy.org/doc/scipy/reference/generated/scipy.sparse.lil_matrix.html
少し検索してみましたが、うまくいく解決策が見つからないようです。
編集: bbtrbのメソッド ( coo_matrix を使用)は nonzero を使用して、元の提案よりもはるかに高速です。 Sven Marnachの提案itertools.izip
は速度も改善します。現在の最速はusing_tocoo_izip
:
import scipy.sparse
import random
import itertools
def using_nonzero(x):
rows,cols = x.nonzero()
for row,col in Zip(rows,cols):
((row,col), x[row,col])
def using_coo(x):
cx = scipy.sparse.coo_matrix(x)
for i,j,v in Zip(cx.row, cx.col, cx.data):
(i,j,v)
def using_tocoo(x):
cx = x.tocoo()
for i,j,v in Zip(cx.row, cx.col, cx.data):
(i,j,v)
def using_tocoo_izip(x):
cx = x.tocoo()
for i,j,v in itertools.izip(cx.row, cx.col, cx.data):
(i,j,v)
N=200
x = scipy.sparse.lil_matrix( (N,N) )
for _ in xrange(N):
x[random.randint(0,N-1),random.randint(0,N-1)]=random.randint(1,100)
これらのtimeit
結果を生成します。
% python -mtimeit -s'import test' 'test.using_tocoo_izip(test.x)'
1000 loops, best of 3: 670 usec per loop
% python -mtimeit -s'import test' 'test.using_tocoo(test.x)'
1000 loops, best of 3: 706 usec per loop
% python -mtimeit -s'import test' 'test.using_coo(test.x)'
1000 loops, best of 3: 802 usec per loop
% python -mtimeit -s'import test' 'test.using_nonzero(test.x)'
100 loops, best of 3: 5.25 msec per loop
最速の方法はcoo_matrix
に変換することです:
cx = scipy.sparse.coo_matrix(x)
for i,j,v in Zip(cx.row, cx.col, cx.data):
print "(%d, %d), %s" % (i,j,v)
scipy.sparse
コードセクションからさまざまなスパース行列をループするには、この小さなラッパー関数を使用します(Python-2の場合は、xrange
およびizip
を使用して、大きな行列でのパフォーマンス):
from scipy.sparse import *
def iter_spmatrix(matrix):
""" Iterator for iterating the elements in a ``scipy.sparse.*_matrix``
This will always return:
>>> (row, column, matrix-element)
Currently this can iterate `coo`, `csc`, `lil` and `csr`, others may easily be added.
Parameters
----------
matrix : ``scipy.sparse.sp_matrix``
the sparse matrix to iterate non-zero elements
"""
if isspmatrix_coo(matrix):
for r, c, m in Zip(matrix.row, matrix.col, matrix.data):
yield r, c, m
Elif isspmatrix_csc(matrix):
for c in range(matrix.shape[1]):
for ind in range(matrix.indptr[c], matrix.indptr[c+1]):
yield matrix.indices[ind], c, matrix.data[ind]
Elif isspmatrix_csr(matrix):
for r in range(matrix.shape[0]):
for ind in range(matrix.indptr[r], matrix.indptr[r+1]):
yield r, matrix.indices[ind], matrix.data[ind]
Elif isspmatrix_lil(matrix):
for r in range(matrix.shape[0]):
for c, d in Zip(matrix.rows[r], matrix.data[r]):
yield r, c, d
else:
raise NotImplementedError("The iterator for this sparse matrix has not been implemented")
tocoo()は、マトリックス全体を別の構造に具体化します。これは、python 3の場合は優先MOではありません。このイテレーターも検討できます。
from itertools import chain, repeat
def iter_csr(matrix):
for (row, col, val) in Zip(
chain(*(
repeat(i, r)
for (i,r) in enumerate(comparisons.indptr[1:] - comparisons.indptr[:-1])
)),
matrix.indices,
matrix.data
):
yield (row, col, val)
私はおそらくnumpy-constructs(特に列挙)に置き換えられるべきである多くのpython-constructsを使用していることを認めなければなりません。
[〜#〜] nb [〜#〜]:
In [43]: t=time.time(); sum(1 for x in rather_dense_sparse_matrix.data); print(time.time()-t)
52.48686504364014
In [44]: t=time.time(); sum(1 for x in enumerate(rather_dense_sparse_matrix.data)); print(time.time()-t)
70.19013023376465
In [45]: rather_dense_sparse_matrix
<99829x99829 sparse matrix of type '<class 'numpy.float16'>'
with 757622819 stored elements in Compressed Sparse Row format>
はい、enumerateはやや遅い(ish)
イテレーターの場合:
In [47]: it = iter_csr(rather_dense_sparse_matrix)
In [48]: t=time.time(); sum(1 for x in it); print(time.time()-t)
113.something something
したがって、このオーバーヘッドを許容できるかどうかを判断します。私の場合、tocooはMemoryOverflows
を引き起こしました。
私見:そのようなイテレーターは、dict()のitems()に似たcsr_matrixインターフェースの一部でなければなりません:)
私は同じ問題を抱えていましたが、実際に、あなたの懸念が速度のみである場合、最速の方法(1桁以上速い)は、疎行列を密行列に変換し(x.todense())、非ゼロで反復することです密行列内の要素。 (もちろん、このアプローチではより多くのメモリが必要です)
x
の代わりにfilter(lambda x:x, x)
を試してください。