Development Tip

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

yourdevel 2020. 10. 15. 21:52
반응형

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


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()이러한 방법 중 어느 것도 하나 일했다.


편집 : 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'])

당신이 쓰는, 비록 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)

다음은 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여기서 승자도 마찬가지 입니다.


이제 다음을 사용할 수 있습니다 scipy.sparse.save_npz: https://docs.scipy.org/doc/scipy/reference/generated/scipy.sparse.save_npz.html


두 컴퓨터에 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)

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 센트 추가 : 나에게는 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 배 느립니다 .


This is what I used to save a 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

I must say I found NumPy's np.load(..) to be very slow. This is my current solution, I feel runs much faster:

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)

I was asked to send the matrix in a simple and generic format:

<x,y,value>

I ended up with this:

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]]))

This works for me:

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)

The trick was to call .tolist() to convert the shape 0 object array to the original object.

참고URL : https://stackoverflow.com/questions/8955448/save-load-scipy-sparse-csr-matrix-in-portable-data-format

반응형