Сохранение / загрузка составляющей разреженные матрицы КСО в портативный формат данных


как вы сохраняете / загружаете scipy sparse csr_matrix в портативном формате? Разреженная матрица scipy создается на Python 3 (Windows 64-bit) для запуска на Python 2 (Linux 64-bit). Первоначально я использовал pickle (с протоколом=2 и fix_imports=True), но это не сработало, перейдя от Python 3.2.2 (Windows 64-bit) к Python 2.7.2 (Windows 32-bit) и получил ошибку:

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

далее пробовал numpy.save и numpy.load а также scipy.io.mmwrite() и scipy.io.mmread() и ни один из этих методов работал любой.

8 68

8 ответов:

edit: SciPy 1.19 теперь имеет scipy.sparse.save_npz и scipy.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.mmwrite и scipy.io.mmread не работает для вас, я просто хочу добавить, как они работают. Этот вопрос является хитом Google № 1, поэтому я сам начал с np.savez и pickle.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 notebook. Вход представляет собой 1M x 100K случайную разреженную матрицу с плотностью 0.001, содержащую 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 не работает с очень большие объекты (см. ответ). По моему опыту, это не сработало для матрицы 2.7 M x 50k с ненулевыми значениями 270M. 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")

Это то, что я использовал, чтобы сохранить 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 в НП.нагрузка.(.) быть очень медленно. Это мое текущее решение, я чувствую, работает гораздо быстрее:

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)

меня попросили отправить матрицу в простом и общем формате:

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