Сохранить/загрузить scipy sparse csr_matrix в переносном формате данных
Как вы сохраняете/загружаете scipy разреженный 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 (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()
, и ни один из этих методов не работал.
Ответы
Ответ 1
редактировать: 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
, а затем заново 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'])
Ответ 2
Хотя вы пишете, 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)
Ответ 3
Ниже приведено сравнение производительности трех наиболее употребительных ответов с помощью ноутбука Jupyter. Вход представляет собой случайную разрешенную матрицу размером 1 М х 100 КБ с плотностью 0,001, содержащую 100 М ненулевых значений:
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
является победителем здесь.
Ответ 4
Теперь вы можете использовать scipy.sparse.save_npz
:
https://docs.scipy.org/doc/scipy/reference/generated/scipy.sparse.save_npz.html
Ответ 5
Предполагая, что у вас есть scipy на обеих машинах, вы можете просто использовать pickle
.
Однако не забудьте указать двоичный протокол при травлении массивов numpy. В противном случае вы получите огромный файл.
Во всяком случае, вы должны это сделать:
import cPickle as pickle
import numpy as np
import scipy.sparse
# Just for testing, let 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)
Ответ 6
С 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")
Ответ 7
Добавляя два моих цента: для меня 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
в текущей реализации, из того, что я тестировал.
Ответ 8
Это то, что я использовал для сохранения 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)
Ответ 9
Мне было предложено отправить матрицу в простой и общий формат:
<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]]))
Ответ 10
Это работает для меня:
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 в исходный объект.