web-dev-qa-db-ja.com

scipy.sparse行列にブロードキャストされた密な1D配列を要素ごとに乗算する方法は?

2次元のスパース配列があるとします。私の実際のユースケースでは、行と列の両方の数がはるかに多いため(たとえば、20000と50000)、密な表現を使用するとメモリに収まりません。

>>> import numpy as np
>>> import scipy.sparse as ssp

>>> a = ssp.lil_matrix((5, 3))
>>> a[1, 2] = -1
>>> a[4, 1] = 2
>>> a.todense()
matrix([[ 0.,  0.,  0.],
        [ 0.,  0., -1.],
        [ 0.,  0.,  0.],
        [ 0.,  0.,  0.],
        [ 0.,  2.,  0.]])

ここで、サイズ3(または実際の場合は50000)のすべての非ゼロコンポーネントを含む高密度の1次元配列があるとします。

>>> d = np.ones(3) * 3
>>> d
array([ 3.,  3.,  3.])

Numpyの通常のブロードキャストセマンティクスを使用して、aとdの要素ごとの乗算を計算したいと思います。ただし、scipyのスパース行列はnp.matrixのものです。「*」演算子は、elementwise-multiplyではなくmatrix-multiplyのように動作するようにオーバーロードされています。

>>> a * d
array([ 0., -3.,  0.,  0.,  6.])

1つの解決策は、「a」を「*」演算子の配列セマンティクスに切り替えることです。これにより、期待される結果が得られます。

>>> a.toarray() * d
array([[ 0.,  0.,  0.],
       [ 0.,  0., -3.],
       [ 0.,  0.,  0.],
       [ 0.,  0.,  0.],
       [ 0.,  6.,  0.]])

しかし、toarray()を呼び出すと、メモリに収まらない「a」の密なバージョンが実現されるため、これを行うことはできません(結果も密になります)。

>>> ssp.issparse(a.toarray())
False

スパースなデータ構造のみを維持し、「a」の列で非効率的なpythonループを実行する必要なしに、これを構築する方法はありますか?

42
ogrisel

私もscipy.orgで返信しましたが、他の人が検索時にこのページを見つけた場合に備えて、ここに回答を追加する必要があると思いました。

ベクトルをスパース対角行列に変換してから、行列の乗算(*を使用)を使用して、ブロードキャストと同じことを効率的に行うことができます。

>>> d = ssp.lil_matrix((3,3))
>>> d.setdiag(np.ones(3)*3)
>>> a*d
<5x3 sparse matrix of type '<type 'numpy.float64'>'
 with 2 stored elements in Compressed Sparse Row format>
>>> (a*d).todense()
matrix([[ 0.,  0.,  0.],
        [ 0.,  0., -3.],
        [ 0.,  0.,  0.],
        [ 0.,  0.,  0.],
        [ 0.,  6.,  0.]])

お役に立てば幸いです。

45
mitmatt

A.multiply(B)はscipysparseで機能するはずだと思います。メソッドmultiplyは、行列の乗算ではなく、「ポイントごとの」乗算を実行します。

HTH

24
salma

さて、ここにあなたが望むことをする簡単なコードがあります。それがあなたが望むほど効率的であるかどうかはわかりませんので、それを取るか、それを残してください:

import scipy.sparse as ssp
def pointmult(a,b):
    x = a.copy()
    for i in xrange(a.shape[0]):
        if x.data[i]:
            for j in xrange(len(x.data[i])):
                x.data[i] *= b[x.rows[i]]
    return x

これはlil行列でのみ機能するため、他の形式で機能させるには、いくつかの変更を加える必要があります。

1
Justin Peel