csr_matrix
が大きく、各行の上位10個の値とそのインデックスに関心があります。しかし、私はマトリックスを操作するための適切な方法を見つけられませんでした。
これが私の現在の解決策であり、主なアイデアはそれらを行ごとに処理することです。
row = csr_matrix.getrow(row_number).toarray()[0].ravel()
top_ten_indicies = row.argsort()[-10:]
top_ten_values = row[row.argsort()[-10:]]
これを行うことにより、csr_matrix
の利点が十分に活用されません。それは、力ずくの解決策のようなものです。
この場合、csr
形式の利点が何であるかわかりません。確かに、すべての非ゼロ値は1つの_.data
_配列に収集され、対応する列インデックスは_.indices
_にあります。しかし、それらはさまざまな長さのブロックになっています。つまり、並列またはnumpy
配列ストライドで処理することはできません。
1つの解決策は、これらのブロックを共通の長さのブロックにパッドすることです。それが.toarray()
が行うことです。次に、argsort(axis=1) or with
argpartition`を使用して最大値を見つけることができます。
もう1つは、それらを行サイズのブロックに分割し、それぞれを処理することです。それが_.getrow
_で行っていることです。それらを分割する別の方法は、lil
形式に変換し、_.data
_および_.rows
_配列のサブリストを処理することです。
考えられる3番目のオプションは、ufunc
reduceat
メソッドを使用することです。これにより、ufunc
reduction
メソッドを配列のシーケンシャルブロックに適用できます。これを利用する_np.add
_のような確立されたufunc
があります。 argsort
はそのような関数ではありません。しかし、Python関数からufunc
を構築し、通常のPython反復よりも適度な速度を得る方法があります。[必要です最近のSOこれを説明する質問。]を検索します。
このいくつかを、行を合計するより単純な関数で説明します。
_A2
_がcsr行列の場合。
_A2.sum(axis=1) # the fastest compile csr method
A2.A.sum(axis=1) # same, but with a dense intermediary
[np.sum(l.data) for l in A2] # iterate over the rows of A2
[np.sum(A2.getrow(i).data) for i in range(A2.shape[0])] # iterate with index
[np.sum(l) for l in A2.tolil().data] # sum the sublists of lil format
np.add.reduceat(A2.data, A2.indptr[:-1]) # with reduceat
_
A2.sum(axis=1)
は行列乗算として実装されます。これはソートの問題とは関係ありませんが、それでも合計の問題を見る興味深い方法です。 csr
形式は効率的な乗算のために開発されたことを忘れないでください。
私の現在のサンプルマトリックスの場合(別のSOまばらな質問用に作成)
_<8x47752 sparse matrix of type '<class 'numpy.float32'>'
with 32 stored elements in Compressed Sparse Row format>
_
いくつかの比較時間は
_In [694]: timeit np.add.reduceat(A2.data, A2.indptr[:-1])
100000 loops, best of 3: 7.41 µs per loop
In [695]: timeit A2.sum(axis=1)
10000 loops, best of 3: 71.6 µs per loop
In [696]: timeit [np.sum(l) for l in A2.tolil().data]
1000 loops, best of 3: 280 µs per loop
_
それ以外はすべて1ms以上です。
次のような1行関数の開発に焦点を当てることをお勧めします。
_def max_n(row_data, row_indices, n):
i = row_data.argsort()[-n:]
# i = row_data.argpartition(-n)[-n:]
top_values = row_data[i]
top_indices = row_indices[i] # do the sparse indices matter?
return top_values, top_indices, i
_
次に、ifがこれらの反復法の1つにどのように適合するかを確認します。 tolil()
は最も有望に見えます。
これらの結果をどのように収集するかという問題には取り組んでいません。それらはリストのリスト、10列の配列、行ごとに10個の値を持つ別のスパース行列などである必要がありますか?
大きなスパースの各行を並べ替えて上位K値と列インデックスを保存する -数年前の同様の質問ですが、回答がありません。
scipyスパース行列の各行または列のArgmax -argmax
の行に対してcsr
を求める最近の質問。同じ問題のいくつかについて説明します。
numpyでループを高速化する方法は? --_np.frompyfunc
_を使用してufunc
を作成する方法の例。結果の関数に_.reduceat
_メソッドがあるかどうかはわかりません。
スパース行列の上位k要素の値を増やす -csrの上位k要素を取得します(行ではありません)。 argpartition
の場合。
_np.frompyfunc
_で実装された行の合計:
_In [741]: def foo(a,b):
return a+b
In [742]: vfoo=np.frompyfunc(foo,2,1)
In [743]: timeit vfoo.reduceat(A2.data,A2.indptr[:-1],dtype=object).astype(float)
10000 loops, best of 3: 26.2 µs per loop
_
それはかなりの速度です。しかし、リダクションを介してargsort
を実装するバイナリ関数(2つの引数を取る)を作成する方法を考えることはできません。したがって、これはおそらくこの問題の行き詰まりです。
元の質問に答えるために(この質問を見つけてcopy-pastaを探していた私のような人のために)、@ hpauljのlil_matrix
への変換の提案に基づくマルチプロセッシングを使用し、行を反復処理するソリューションを次に示します。
from multiprocessing import Pool
def _top_k(args):
"""
Helper function to process a single row of top_k
"""
data, row = args
data, row = Zip(*sorted(Zip(data, row), reverse=True)[:k])
return data, row
def top_k(m, k):
"""
Keep only the top k elements of each row in a csr_matrix
"""
ml = m.tolil()
with Pool() as p:
ms = p.map(_top_k, Zip(ml.data, ml.rows))
ml.data, ml.rows = Zip(*ms)
return ml.tocsr()
行を反復処理し、各行の上位インデックスを個別に取得する必要があります。しかし、このループをjited(および並列化)して、非常に高速な関数を取得することができます。
@nb.njit(cache=True)
def row_topk_csr(data, indices, indptr, K):
m = indptr.shape[0] - 1
max_indices = np.zeros((m, K), dtype=indices.dtype)
max_values = np.zeros((m, K), dtype=data.dtype)
for i in nb.prange(m):
top_inds = np.argsort(data[indptr[i] : indptr[i + 1]])[::-1][:K]
max_indices[i] = indices[indptr[i] : indptr[i + 1]][top_inds]
max_values[i] = data[indptr[i] : indptr[i + 1]][top_inds]
return max_indices, max_values
このように呼んでください:
top_pred_indices, _ = row_topk_csr(csr_mat.data, csr_mat.indices, csr_mat.indptr, K)
この操作を頻繁に実行する必要があります。この関数は十分に高速で、1mil x400kのスパース行列で1秒未満で実行されます。
HTH。