web-dev-qa-db-ja.com

Scipy.sparse.csr_matrix:上位10個の値とインデックスを取得する方法は?

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の利点が十分に活用されません。それは、力ずくの解決策のようなものです。

14
Patrick

この場合、csr形式の利点が何であるかわかりません。確かに、すべての非ゼロ値は1つの_.data_配列に収集され、対応する列インデックスは_.indices_にあります。しかし、それらはさまざまな長さのブロックになっています。つまり、並列またはnumpy配列ストライドで処理することはできません。

1つの解決策は、これらのブロックを共通の長さのブロックにパッドすることです。それが.toarray()が行うことです。次に、argsort(axis=1) or withargpartition`を使用して最大値を見つけることができます。

もう1つは、それらを行サイズのブロックに分割し、それぞれを処理することです。それが_.getrow_で行っていることです。それらを分割する別の方法は、lil形式に変換し、_.data_および_.rows_配列のサブリストを処理することです。

考えられる3番目のオプションは、ufuncreduceatメソッドを使用することです。これにより、ufuncreductionメソッドを配列のシーケンシャルブロックに適用できます。これを利用する_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つの引数を取る)を作成する方法を考えることはできません。したがって、これはおそらくこの問題の行き詰まりです。

6
hpaulj

元の質問に答えるために(この質問を見つけて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()
1
maxymoo

行を反復処理し、各行の上位インデックスを個別に取得する必要があります。しかし、このループを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。

0
Deepak Saini