이식 가능한 데이터 형식으로 scipy sparse csr_matrix 저장 /로드


80

csr_matrix이식 가능한 형식으로 scipy sparse 를 어떻게 저장 /로드 합니까? scipy 희소 행렬은 Python 3 (Windows 64 비트)에서 생성되어 Python 2 (Linux 64 비트)에서 실행됩니다. 처음에는 pickle (프로토콜 = 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.savenumpy.load뿐만 아니라 scipy.io.mmwrite()scipy.io.mmread()이러한 방법 중 어느 것도 하나 일했다.


2
mmwrite / mmread는 텍스트 파일 형식이므로 작동합니다. Linux 대 Windows의 가능한 문제는 줄 끝, CRLF 대 LF
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. 모두 간단한 ndarrays이므로 numpy.save작업 할 것입니다. numpy.save또는로 세 배열을 저장 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

@physicalattraction 쉬운 솔루션은 로더 함수의 시작 부분에 이것을 추가하는 것입니다if not filename.endswith('.npz'): filename += '.npz'
알렉산더 Shchur

11
Scipy 1.19에는 이제 scipy.sparse.save_npzload.
hpaulj

3
그것은 정답에 새 사용자에게 유용 할 수 있습니다 @hpaulj : 버전은 scipy 0.19입니다
P. 카밀레

37

당신이 쓰는, 비록 scipy.io.mmwritescipy.io.mmread당신을 위해 일을하지, 난 그냥 작동 방법을 추가 할. 이 질문은 아니오입니다. 나 자신이 시작되도록 한 구글은 공격 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 년

예, 현재 최신 버전입니다. 질문 아래의 탭 에서 가장 오래된 항목 을 클릭하여 생성 시간별로 답변을 정렬 할 수 있습니다 .
Frank Zalkow

이 메서드는 import scipy. 명시 적 from scipy import io또는 import scipy.io필요합니다.
blootsvoets

1
이것은 np.savezcPickle솔루션 보다 훨씬 느리게 작동 하며 ~ 3 배 더 큰 파일을 생성합니다. 테스트 세부 사항 은 내 답변 을 참조 하십시오.
Dennis Golomazov

26

다음은 Jupyter 노트북을 사용하여 가장 많이 찬성 된 세 답변의 성능 비교입니다. 입력은 0이 아닌 100M 값을 포함하는 밀도 0.001의 1M x 100K 랜덤 희소 행렬입니다.

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 0이 아닌 값을 가진 2.7M x 50k 매트릭스에서는 작동하지 않았습니다. np.savez솔루션이 잘 작동했습니다.

결론

(CSR 매트릭스에 대한이 간단한 테스트를 기반으로 함) cPickle가장 빠른 방법이지만 매우 큰 매트릭스에서는 작동하지 않고 np.savez약간 느리지 만 io.mmwrite훨씬 느리고 더 큰 파일을 생성하고 잘못된 형식으로 복원합니다. np.savez여기에서 승자도 마찬가지 입니다.


2
감사! 적어도 나를 위해 (Py 2.7.11) 라인 from scipy.sparse import io이 작동하지 않는다는 점에 유의하십시오 . 대신 from scipy import io. Docs
patrick

1
@patrick 업데이트에 감사드립니다. 가져 오기 변경은에서 수행되어야합니다 scipy.
Dennis Golomazov


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 사용) 이었지만 Python 3.2.2에서 Python 2.7.2로 이동하지 못했습니다. 이 정보를 질문에 추가했습니다.
Henry Thornton

이것이 가장 빠른 솔루션 인 것 같지만 ( 내 대답 의 간단한 테스트에 따르면 ) cPickle매우 큰 행렬 ( 링크 ) 에서는 작동하지 않습니다 .
Dennis Golomazov

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.