web-dev-qa-db-fra.com

Enregistrer / charger scipy sparse csr_matrix au format de données portable

Comment enregistrer/charger un scipy clairsemé csr_matrix Dans un format portable? La matrice éparse scipy est créée sur Python 3 (Windows 64 bits) pour fonctionner sur Python 2 (Linux 64 bits). Initialement, j'ai utilisé des cornichons (avec protocol = 2 et fix_imports = True) mais cela n'a pas fonctionné en passant de Python 3.2.2 (Windows 64 bits) à Python 2.7. 2 (Windows 32 bits) et a obtenu l'erreur:

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

Ensuite, essayé numpy.save Et numpy.load Ainsi que scipy.io.mmwrite() et scipy.io.mmread() et aucune de ces méthodes ne fonctionnait non plus.

75
Henry Thornton

modifier: SciPy 1.19 a maintenant scipy.sparse.save_npz et scipy.sparse.load_npz .

from scipy import sparse

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

Pour les deux fonctions, l'argument file peut également être un objet de type fichier (c'est-à-dire le résultat de open) au lieu d'un nom de fichier.


Vous avez une réponse du groupe d'utilisateurs Scipy:

Un csr_matrix a 3 attributs de données qui comptent: .data, .indices, et .indptr. Tous sont de simples ndarrays, donc numpy.save y travaillera. Enregistrez les trois tableaux avec numpy.save ou numpy.savez, chargez-les avec numpy.load, puis recréez l'objet matrice clairsemée avec:

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

Ainsi, par exemple:

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'])
103
Henry Thornton

Bien que vous écriviez, scipy.io.mmwrite et scipy.io.mmread ne fonctionne pas pour vous, je veux juste ajouter comment ils fonctionnent. Cette question est le non. 1 hit sur Google, j'ai donc moi-même commencé par np.savez et pickle.dump avant de passer aux fonctions scipy simples et évidentes. Ils travaillent pour moi et ne devraient pas être supervisés par ceux qui ne les ont pas encore essayés.

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)
36
Frank Zalkow

Voici une comparaison des performances des trois réponses les plus votées à l'aide du bloc-notes Jupyter. L'entrée est une matrice clairsemée aléatoire de 1 M x 100 K avec une densité de 0,001, contenant 100 M de valeurs non nulles:

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.

(notez que le format a été changé de 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.

Remarque : cPickle ne fonctionne pas avec de très gros objets (voir cette réponse ). D'après mon expérience, cela n'a pas fonctionné pour une matrice de 2,7M x 50k avec 270M de valeurs non nulles. np.savez la solution a bien fonctionné.

Conclusion

(basé sur ce test simple pour les matrices CSR) cPickle est la méthode la plus rapide, mais elle ne fonctionne pas avec de très grandes matrices, np.savez n'est que légèrement plus lent, tandis que io.mmwrite est beaucoup plus lent, produit un fichier plus gros et restaure au mauvais format. Alors np.savez est le gagnant ici.

25
Dennis Golomazov

Vous pouvez maintenant utiliser scipy.sparse.save_npz: https://docs.scipy.org/doc/scipy/reference/generated/scipy.sparse.save_npz.html

16
Victor Deplasse

En supposant que vous ayez scipy sur les deux machines, vous pouvez simplement utiliser pickle.

Cependant, assurez-vous de spécifier un protocole binaire lors du décapage des tableaux numpy. Sinon, vous vous retrouverez avec un énorme fichier.

En tout cas, vous devriez pouvoir faire ceci:

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)

Vous pouvez ensuite le charger avec:

import cPickle as pickle

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

Depuis scipy 0.19.0, vous pouvez enregistrer et charger des matrices éparses de cette façon:

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")
9
x0s

Ajout de mes deux cents: pour moi, npz n'est pas portable car je ne peux pas l'utiliser pour exporter ma matrice facilement vers des clients non-Python (par exemple PostgreSQL - heureux d'être corrigé). J'aurais donc aimé obtenir une sortie CSV pour la matrice clairsemée (un peu comme si vous l'obteniez, vous print() la matrice clairsemée). Comment y parvenir dépend de la représentation de la matrice clairsemée. Pour une matrice CSR, le code suivant crache la sortie CSV. Vous pouvez vous adapter à d'autres représentations.

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=',')

C'est environ 2 fois plus lent que save_npz dans l'implémentation actuelle, d'après ce que j'ai testé.

2
Yuval

C'est ce que j'ai utilisé pour enregistrer un 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

Je dois dire que j'ai trouvé que le np.load de NumPy (..) était très lent. Ceci est ma solution actuelle, je pense qu'elle fonctionne beaucoup plus rapidement:

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
dlorch

On m'a demandé d'envoyer la matrice dans un format simple et générique:

<x,y,value>

Je me suis retrouvé avec ceci:

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]]))
0
Guy s

Cela fonctionne pour moi:

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)

L'astuce consistait à appeler .tolist() pour convertir le tableau d'objets de forme 0 en l'objet d'origine.

0
Thomas Ahle