Сохранить / загрузить scipy sparse csr_matrix в переносимом формате данных - PullRequest
74 голосов
/ 21 января 2012

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

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

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

Ответы [ 9 ]

102 голосов
/ 24 января 2012

изменить: 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'])
36 голосов
/ 12 марта 2015

Хотя вы пишете, scipy.io.mmwrite и scipy.io.mmread у вас не работают, я просто хочу добавить, как они работают.Этот вопрос нет.1 Google ударил, поэтому я сам начал с 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)
25 голосов
/ 08 февраля 2017

Вот сравнение производительности трех самых популярных ответов с использованием ноутбука Jupyter.Вход представляет собой случайную разреженную матрицу размером 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,7M x 50k с ненулевыми значениями 270M.np.savez решение работало хорошо.

Заключение

(на основе этого простого теста для матриц CSR) cPickle - самый быстрый метод, но он не работает с очень большими матрицами, np.savez немного медленнее, а io.mmwrite намного медленнее, создает больший файл и восстанавливает неправильный формат.Так что np.savez здесь победитель.

16 голосов
/ 03 апреля 2017

Теперь вы можете использовать scipy.sparse.save_npz: https://docs.scipy.org/doc/scipy/reference/generated/scipy.sparse.save_npz.html

11 голосов
/ 22 января 2012

Предполагая, что у вас есть 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)
9 голосов
/ 28 апреля 2017

Начиная с версии 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 голосов
/ 09 апреля 2019

Добавление двух моих центов: для меня 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=',')

Это примерно в 2 раза медленнее, чем save_npz в текущей реализации, из того, что я тестировал.

1 голос
/ 27 декабря 2016

Это то, что я использовал для сохранения 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)
0 голосов
/ 15 января 2017

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

<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]]))
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...