scipy sparsecsr_matrixをポータブルデータ形式で保存/ロードします


80

scipyスパースcsr_matrixをポータブル形式でどのように保存/ロードしますか?scipyスパース行列は、Python 2(Linux 64ビット)で実行するためにPython 3(Windows 64ビット)で作成されます。最初は、pickle(protocol = 2およびfix_imports = True)を使用しましたが、Python 3.2.2(Windows 64ビット)からPython 2.7.2(Windows 32ビット)に移行すると機能せず、エラーが発生しました。

TypeError: ('data type not understood', <built-in function _reconstruct>, (<type 'numpy.ndarray'>, (0,), '[98]')).

次に、numpy.saveandnumpy.loadと同様にscipy.io.mmwrite()andscipy.io.mmread()を試しましたが、これらの方法はいずれも機能しませんでした。


2
mmwrite / mmreadはテキストファイル形式なので、機能するはずです。Windowsの対Linuxの持つ可能性の問題は、行末、LF対CRLFかもしれ
PV。

回答:


121

編集: scipyのダウンロード1.19は今持っているscipy.sparse.save_npzscipy.sparse.load_npz

from scipy import sparse

sparse.save_npz("yourmatrix.npz", your_matrix)
your_matrix_back = sparse.load_npz("yourmatrix.npz")

どちらの関数でも、file引数はopenファイル名ではなくファイルのようなオブジェクト(つまりの結果)にすることができます。


Scipyユーザーグループから回答を得ました:

csr_matrixはその問題を属性3つのデータを持っています.data.indices.indptr。すべて単純なndarrayなので、numpy.saveそれらで動作します。numpy.saveまたはを使用して3つの配列を保存し、を使用してnumpy.savezそれらをロードし直してnumpy.loadから、次を使用してスパース行列オブジェクトを再作成します。

new_csr = csr_matrix((data, indices, indptr), shape=(M, N))

したがって、たとえば:

def save_sparse_csr(filename, array):
    np.savez(filename, data=array.data, indices=array.indices,
             indptr=array.indptr, shape=array.shape)

def load_sparse_csr(filename):
    loader = np.load(filename)
    return csr_matrix((loader['data'], loader['indices'], loader['indptr']),
                      shape=loader['shape'])

3
何らかの理由でこれがスパース行列オブジェクトのメソッドとして実装されなかった場合、何か考えはありますか?scipy.io.savemat方法は...確実に十分にかかわらず、動作しているようです
mathtick

6
注:save_sparse_csrのファイル名に拡張子.npzがない場合、これは自動的に追加されます。これは、load_sparse_csr関数では自動的には実行されません。
物理的な

@physicalattraction簡単な解決策は、ローダー関数の先頭にこれを追加することですif not filename.endswith('.npz'): filename += '.npz'
Alexander Shchur 2017年

11
Scipy1.19にはとがscipy.sparse.save_npzありloadます。
hpaulj 2017年

3
@hpaulj新しいユーザーが正解すると役立つ場合があります。バージョンはscipy0.19
P. Camilleri 2017

37

あなたが書いた、けれどもscipy.io.mmwriteそしてscipy.io.mmreadあなたのための作業をしないで、私はちょうど彼らがどのように機能するかを追加したいです。この質問はノーです。私自身は、使用を開始して1 Googleは、ヒットnp.savezpickle.dumpシンプルかつ明白なscipyのダウンロード-機能に切り替える前に。それらは私のために働きます、そしてそれらをまだ試したことがない人々によって監視されるべきではありません。

from scipy import sparse, io

m = sparse.csr_matrix([[0,0,0],[1,0,0],[0,1,0]])
m              # <3x3 sparse matrix of type '<type 'numpy.int64'>' with 2 stored elements in Compressed Sparse Row format>

io.mmwrite("test.mtx", m)
del m

newm = io.mmread("test.mtx")
newm           # <3x3 sparse matrix of type '<type 'numpy.int32'>' with 2 stored elements in COOrdinate format>
newm.tocsr()   # <3x3 sparse matrix of type '<type 'numpy.int32'>' with 2 stored elements in Compressed Sparse Row format>
newm.toarray() # array([[0, 0, 0], [1, 0, 0], [0, 1, 0]], dtype=int32)

これは他の回答と比較して最新のソリューションですか?
dineshdileep 2015

はい、現在は最新のものです。質問の下のタブで最も古いものをクリックすると、作成時までに回答を並べ替えることができます。
フランクザルコウ2015

このメソッドは、を書き込むだけで失敗しますimport scipy。明示的なfrom scipy import ioまたはimport scipy.ioが必要です。
blootsvoets 2015年

1
これは、np.savezおよびcPickleソリューションよりもはるかに遅く動作し、最大3倍のファイルを生成するようです。テストの詳細については、私の回答をご覧ください。
デニスゴロマゾフ2017年

26

これは、Jupyterノートブックを使用した最も賛成の3つの回答のパフォーマンス比較です。入力は、密度0.001の1M x 100Kのランダムなスパース行列で、100Mの非ゼロ値が含まれています。

from scipy.sparse import random
matrix = random(1000000, 100000, density=0.001, format='csr')

matrix
<1000000x100000 sparse matrix of type '<type 'numpy.float64'>'
with 100000000 stored elements in Compressed Sparse Row format>

io.mmwrite / io.mmread

from scipy.sparse import io

%time io.mmwrite('test_io.mtx', matrix)
CPU times: user 4min 37s, sys: 2.37 s, total: 4min 39s
Wall time: 4min 39s

%time matrix = io.mmread('test_io.mtx')
CPU times: user 2min 41s, sys: 1.63 s, total: 2min 43s
Wall time: 2min 43s    

matrix
<1000000x100000 sparse matrix of type '<type 'numpy.float64'>'
with 100000000 stored elements in COOrdinate format>    

Filesize: 3.0G.

(形式がcsrからcooに変更されていることに注意してください)。

np.savez / np.load

import numpy as np
from scipy.sparse import csr_matrix

def save_sparse_csr(filename, array):
    # note that .npz extension is added automatically
    np.savez(filename, data=array.data, indices=array.indices,
             indptr=array.indptr, shape=array.shape)

def load_sparse_csr(filename):
    # here we need to add .npz extension manually
    loader = np.load(filename + '.npz')
    return csr_matrix((loader['data'], loader['indices'], loader['indptr']),
                      shape=loader['shape'])


%time save_sparse_csr('test_savez', matrix)
CPU times: user 1.26 s, sys: 1.48 s, total: 2.74 s
Wall time: 2.74 s    

%time matrix = load_sparse_csr('test_savez')
CPU times: user 1.18 s, sys: 548 ms, total: 1.73 s
Wall time: 1.73 s

matrix
<1000000x100000 sparse matrix of type '<type 'numpy.float64'>'
with 100000000 stored elements in Compressed Sparse Row format>

Filesize: 1.1G.

cPickle

import cPickle as pickle

def save_pickle(matrix, filename):
    with open(filename, 'wb') as outfile:
        pickle.dump(matrix, outfile, pickle.HIGHEST_PROTOCOL)
def load_pickle(filename):
    with open(filename, 'rb') as infile:
        matrix = pickle.load(infile)    
    return matrix    

%time save_pickle(matrix, 'test_pickle.mtx')
CPU times: user 260 ms, sys: 888 ms, total: 1.15 s
Wall time: 1.15 s    

%time matrix = load_pickle('test_pickle.mtx')
CPU times: user 376 ms, sys: 988 ms, total: 1.36 s
Wall time: 1.37 s    

matrix
<1000000x100000 sparse matrix of type '<type 'numpy.float64'>'
with 100000000 stored elements in Compressed Sparse Row format>

Filesize: 1.1G.

:cPickleは、非常に大きなオブジェクトでは機能しません(この回答を参照)。私の経験では、270Mの非ゼロ値を持つ2.7M x50kの行列では機能しませんでした。 np.savezソリューションはうまくいきました。

結論

(CSRマトリックスのこの簡単なテストに基づく) cPickleは最速の方法ですが、非常に大きなマトリックスでnp.savezは機能せず、わずかに遅くなりますが、io.mmwriteはるかに遅く、ファイルが大きくなり、間違った形式に復元されます。ここnp.savezで勝者もそうです。


2
ありがとう!少なくとも私(Py 2.7.11)にとっては、この行from scipy.sparse import ioは機能しないことに注意してください。代わりに、を実行してくださいfrom scipy import ioドキュメント
パトリック2017年

1
@patrickアップデートしてくれてありがとう。インポートの変更はで行われている必要がありますscipy
デニスゴロマゾフ2017年


11

両方のマシンにscipyがあると仮定すると、次を使用できます。 pickle

ただし、numpy配列をピクルスにするときは、必ずバイナリプロトコルを指定してください。そうしないと、巨大なファイルになってしまいます。

とにかく、あなたはこれを行うことができるはずです:

import cPickle as pickle
import numpy as np
import scipy.sparse

# Just for testing, let's make a dense array and convert it to a csr_matrix
x = np.random.random((10,10))
x = scipy.sparse.csr_matrix(x)

with open('test_sparse_array.dat', 'wb') as outfile:
    pickle.dump(x, outfile, pickle.HIGHEST_PROTOCOL)

次に、次のものをロードできます。

import cPickle as pickle

with open('test_sparse_array.dat', 'rb') as infile:
    x = pickle.load(infile)

pickleの使用は私の元の解決策(protocol = 2およびfix_imports = True)でしたが、Python3.2.2からPython2.7.2に移行しても機能しませんでした。この情報を質問に追加しました。
ヘンリーソーントン

これは(私の答えの簡単なテストによると)最速の解決策のように見えますが、cPickle非常に大きな行列では機能しないことに注意してください(リンク)。
デニスゴロマゾフ2017年

9

scipy 0.19.0以降、次の方法でスパース行列を保存およびロードできます。

from scipy import sparse

data = sparse.csr_matrix((3, 4))

#Save
sparse.save_npz('data_sparse.npz', data)

#Load
data = sparse.load_npz("data_sparse.npz")

2

編集どうやらそれは十分に簡単です:

def sparse_matrix_tuples(m):
    yield from m.todok().items()

これにより、((i, j), value)シリアル化と逆シリアル化が容易なタプルが生成されます。パフォーマンスに関して以下のコードとどのように比較されるかはわかりませんcsr_matrixが、間違いなく簡単です。有益な情報になることを願って、以下に元の回答を残しておきます。


私の2セントを追加する:私にとってnpzは、マトリックスをPython以外のクライアントに簡単にエクスポートするために使用できないため移植性がありません(例:PostgreSQL-修正されてうれしいです)。したがって、スパース行列のCSV出力を取得したかったのです(スパース行列を取得するのと同じようにprint())。これを実現する方法は、スパース行列の表現によって異なります。CSRマトリックスの場合、次のコードはCSV出力を吐き出します。他の表現に適応することができます。

import numpy as np

def csr_matrix_tuples(m):
    # not using unique will lag on empty elements
    uindptr, uindptr_i = np.unique(m.indptr, return_index=True)
    for i, (start_index, end_index) in zip(uindptr_i, zip(uindptr[:-1], uindptr[1:])):
        for j, data in zip(m.indices[start_index:end_index], m.data[start_index:end_index]):
            yield (i, j, data)

for i, j, data in csr_matrix_tuples(my_csr_matrix):
    print(i, j, data, sep=',')

save_npz私がテストしたところ、現在の実装よりも約2倍遅くなっています。


1

これは私が保存するために使用したものlil_matrixです。

import numpy as np
from scipy.sparse import lil_matrix

def save_sparse_lil(filename, array):
    # use np.savez_compressed(..) for compression
    np.savez(filename, dtype=array.dtype.str, data=array.data,
        rows=array.rows, shape=array.shape)

def load_sparse_lil(filename):
    loader = np.load(filename)
    result = lil_matrix(tuple(loader["shape"]), dtype=str(loader["dtype"]))
    result.data = loader["data"]
    result.rows = loader["rows"]
    return result

NumPyのnp​​.load(..)が非常に遅いことがわかったと言わなければなりません。これは私の現在の解決策であり、実行速度がはるかに速いと感じています。

from scipy.sparse import lil_matrix
import numpy as np
import json

def lil_matrix_to_dict(myarray):
    result = {
        "dtype": myarray.dtype.str,
        "shape": myarray.shape,
        "data":  myarray.data,
        "rows":  myarray.rows
    }
    return result

def lil_matrix_from_dict(mydict):
    result = lil_matrix(tuple(mydict["shape"]), dtype=mydict["dtype"])
    result.data = np.array(mydict["data"])
    result.rows = np.array(mydict["rows"])
    return result

def load_lil_matrix(filename):
    result = None
    with open(filename, "r", encoding="utf-8") as infile:
        mydict = json.load(infile)
        result = lil_matrix_from_dict(mydict)
    return result

def save_lil_matrix(filename, myarray):
    with open(filename, "w", encoding="utf-8") as outfile:
        mydict = lil_matrix_to_dict(myarray)
        json.dump(mydict, outfile)

1

これは私のために働きます:

import numpy as np
import scipy.sparse as sp
x = sp.csr_matrix([1,2,3])
y = sp.csr_matrix([2,3,4])
np.savez(file, x=x, y=y)
npz = np.load(file)

>>> npz['x'].tolist()
<1x3 sparse matrix of type '<class 'numpy.int64'>'
    with 3 stored elements in Compressed Sparse Row format>

>>> npz['x'].tolist().toarray()
array([[1, 2, 3]], dtype=int64)

トリックは.tolist()、形状0のオブジェクト配列を元のオブジェクトに変換するために呼び出すことでした。


0

マトリックスを単純で一般的な形式で送信するように依頼されました。

<x,y,value>

私はこれで終わった:

def save_sparse_matrix(m,filename):
    thefile = open(filename, 'w')
    nonZeros = np.array(m.nonzero())
    for entry in range(nonZeros.shape[1]):
        thefile.write("%s,%s,%s\n" % (nonZeros[0, entry], nonZeros[1, entry], m[nonZeros[0, entry], nonZeros[1, entry]]))
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.