以可移植数据格式保存/加载 scipy sparse csr_matrix
Posted
技术标签:
【中文标题】以可移植数据格式保存/加载 scipy sparse csr_matrix【英文标题】:Save / load scipy sparse csr_matrix in portable data format 【发布时间】:2012-02-15 20:19:53 【问题描述】:如何以可移植格式保存/加载 scipy 稀疏 csr_matrix
? 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.save
和numpy.load
以及scipy.io.mmwrite()
和scipy.io.mmread()
,但这些方法都没有奏效。
【问题讨论】:
mmwrite/mmread 应该可以工作,因为它是一种文本文件格式。 Linux 与 Windows 的可能问题可能是行尾,CRLF 与 LF 【参考方案1】:编辑: scipy 0.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
。都是简单的 ndarray,所以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.savemat 方法似乎足够可靠,但 ... 注意:如果 save_sparse_csr 中的文件名没有扩展名 .npz,则会自动添加。这不会在 load_sparse_csr 函数中自动完成。 @physicalattraction 一个简单的解决方案是在加载器函数的开头添加它if not filename.endswith('.npz'): filename += '.npz'
Scipy 1.19 现在有 scipy.sparse.save_npz
和 load
。
@hpaulj 新用户更正答案可能有用:版本是 scipy 0.19【参考方案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)
【讨论】:
与其他答案相比,这是最新的解决方案吗? 是的,目前是最新的。您可以通过单击问题下方选项卡中的最旧来按创建时间排序答案。 这个方法在只写import scipy
时会失败。需要明确的 from scipy import io
或 import scipy.io
。
这似乎比 np.savez
和 cPickle
解决方案慢得多,并产生约 3 倍大的文件。测试详情请见my answer。【参考方案3】:
这是使用 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 不适用于非常大的对象(请参阅this answer)。
根据我的经验,它不适用于具有 270M 非零值的 2.7M x 50k 矩阵。
np.savez
解决方案效果很好。
结论
(基于这个对 CSR 矩阵的简单测试)
cPickle
是最快的方法,但它不适用于非常大的矩阵,np.savez
只是稍微慢一些,而io.mmwrite
则要慢得多,生成更大的文件并恢复到错误的格式。所以np.savez
是这里的赢家。
【讨论】:
谢谢!请注意,至少对我(Py 2.7.11)而言,from scipy.sparse import io
行不起作用。相反,只需执行from scipy import io
。 Docs
@patrick 感谢您的更新。导入更改必须在 scipy
中完成。【参考方案4】:
现在您可以使用 scipy.sparse.save_npz
:
https://docs.scipy.org/doc/scipy/reference/generated/scipy.sparse.save_npz.html
【讨论】:
【参考方案5】:假设你在两台机器上都有 scipy,你可以使用pickle
。
但是,在 pickling 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)
【讨论】:
使用 pickle 是我最初的解决方案(使用协议 = 2 和 fix_imports = True),但从 Python 3.2.2 到 Python 2.7.2 无法正常工作。已将此信息添加到问题中。 请注意,尽管这似乎是最快的解决方案(根据my answer 中的简单测试),cPickle
不适用于非常大的矩阵 (link)。跨度>
【参考方案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】:编辑显然它很简单:
def sparse_matrix_tuples(m):
yield from m.todok().items()
这将产生一个((i, j), value)
元组,它们易于序列化和反序列化。不确定它如何将性能方面与下面的 csr_matrix
代码进行比较,但它肯定更简单。我将在下面留下原始答案,因为我希望它提供信息。
加上我的两分钱:对我来说,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 倍。
【讨论】:
【参考方案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】:这对我有用:
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 对象数组转换为原始对象。
【讨论】:
【参考方案10】:我被要求以简单通用的格式发送矩阵:
<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]]))
【讨论】:
以上是关于以可移植数据格式保存/加载 scipy sparse csr_matrix的主要内容,如果未能解决你的问题,请参考以下文章
Python使用scipy包将稀疏矩阵保存为Mtx格式和npz格式文件实战