将 scipy 稀疏矩阵存储为 HDF5
Posted
技术标签:
【中文标题】将 scipy 稀疏矩阵存储为 HDF5【英文标题】:Storing scipy sparse matrix as HDF5 【发布时间】:2017-09-09 10:40:02 【问题描述】:我想以 HDF5 格式压缩和存储一个巨大的 Scipy 矩阵。我该怎么做呢?我试过下面的代码:
a = csr_matrix((dat, (row, col)), shape=(947969, 36039))
f = h5py.File('foo.h5','w')
dset = f.create_dataset("init", data=a, dtype = int, compression='gzip')
我收到类似的错误,
TypeError: Scalar datasets don't support chunk/filter options
IOError: Can't prepare for writing data (No appropriate function for conversion path)
我无法将其转换为 numpy 数组,因为会有内存溢出。最好的方法是什么?
【问题讨论】:
需要保存矩阵的数据属性,而不是矩阵本身。a
不是 numpy 数组或子类。首先,只需保存输入 dat
,row,
col. A recent
scipy.sparse` 版本有一个 save_npz
函数可以用作模型 - 查看它的代码。
最近关于save_npz
, ***.com/q/43014503的问题
Storing numpy sparse matrix in HDF5 (PyTables)的可能重复
【参考方案1】:
你可以使用scipy.sparse.save_npz方法
或者考虑使用Pandas.SparseDataFrame,但要注意这种方法非常慢(感谢@hpaulj for testing and pointing it out)
演示:
生成稀疏矩阵和SparseDataFrame
In [55]: import pandas as pd
In [56]: from scipy.sparse import *
In [57]: m = csr_matrix((20, 10), dtype=np.int8)
In [58]: m
Out[58]:
<20x10 sparse matrix of type '<class 'numpy.int8'>'
with 0 stored elements in Compressed Sparse Row format>
In [59]: sdf = pd.SparseDataFrame([pd.SparseSeries(m[i].toarray().ravel(), fill_value=0)
...: for i in np.arange(m.shape[0])])
...:
In [61]: type(sdf)
Out[61]: pandas.sparse.frame.SparseDataFrame
In [62]: sdf.info()
<class 'pandas.sparse.frame.SparseDataFrame'>
RangeIndex: 20 entries, 0 to 19
Data columns (total 10 columns):
0 20 non-null int8
1 20 non-null int8
2 20 non-null int8
3 20 non-null int8
4 20 non-null int8
5 20 non-null int8
6 20 non-null int8
7 20 non-null int8
8 20 non-null int8
9 20 non-null int8
dtypes: int8(10)
memory usage: 280.0 bytes
将 SparseDataFrame 保存到 HDF 文件
In [64]: sdf.to_hdf('d:/temp/sparse_df.h5', 'sparse_df')
从 HDF 文件中读取
In [65]: store = pd.HDFStore('d:/temp/sparse_df.h5')
In [66]: store
Out[66]:
<class 'pandas.io.pytables.HDFStore'>
File path: d:/temp/sparse_df.h5
/sparse_df sparse_frame
In [67]: x = store['sparse_df']
In [68]: type(x)
Out[68]: pandas.sparse.frame.SparseDataFrame
In [69]: x.info()
<class 'pandas.sparse.frame.SparseDataFrame'>
Int64Index: 20 entries, 0 to 19
Data columns (total 10 columns):
0 20 non-null int8
1 20 non-null int8
2 20 non-null int8
3 20 non-null int8
4 20 non-null int8
5 20 non-null int8
6 20 non-null int8
7 20 non-null int8
8 20 non-null int8
9 20 non-null int8
dtypes: int8(10)
memory usage: 360.0 bytes
【讨论】:
您的样本矩阵有 0 个非零元素。这个迭代表达式[pd.SparseSeries(m[i].toarray().ravel(), fill_value=0) for i in np.arange(m.shape[0])])
对于大型矩阵来说会非常慢。
@hpaulj,感谢您的评论!我去测试一下
@hpaulj,是的 - 这很慢 - 我用稀疏矩阵测试了它:M = sparse.random(10**4, 10**3, .01, 'csr')
- 它花了1min 2s
所以对于 OPs 矩阵它需要更长的时间。不幸的是,我还不能让它更快......
此答案以“您可以使用 scipy.sparse.save_npz 方法”开头,但从未解释如何使用此方法保存到 HDF5。据我所知,该方法保存到扩展名为“.npz”的常规文件中。【参考方案2】:
一个 csr 矩阵将它的值存储在 3 个数组中。它不是数组或数组子类,所以h5py
不能直接保存。你能做的最好的就是保存属性,并在加载时重新创建矩阵:
In [248]: M = sparse.random(5,10,.1, 'csr')
In [249]: M
Out[249]:
<5x10 sparse matrix of type '<class 'numpy.float64'>'
with 5 stored elements in Compressed Sparse Row format>
In [250]: M.data
Out[250]: array([ 0.91615298, 0.49907752, 0.09197862, 0.90442401, 0.93772772])
In [251]: M.indptr
Out[251]: array([0, 0, 1, 2, 3, 5], dtype=int32)
In [252]: M.indices
Out[252]: array([5, 7, 5, 2, 6], dtype=int32)
In [253]: M.data
Out[253]: array([ 0.91615298, 0.49907752, 0.09197862, 0.90442401, 0.93772772])
coo
格式有data
、row
、col
属性,与你用来创建a
的(dat, (row, col))
基本相同。
In [254]: M.tocoo().row
Out[254]: array([1, 2, 3, 4, 4], dtype=int32)
新的save_npz
函数可以:
arrays_dict = dict(format=matrix.format, shape=matrix.shape, data=matrix.data)
if matrix.format in ('csc', 'csr', 'bsr'):
arrays_dict.update(indices=matrix.indices, indptr=matrix.indptr)
...
elif matrix.format == 'coo':
arrays_dict.update(row=matrix.row, col=matrix.col)
...
np.savez(file, **arrays_dict)
换句话说,它在字典中收集相关属性并使用savez
创建 zip 存档。
同样的方法可以用于h5py
文件。更多关于 save_npz
在最近的 SO 问题中的信息,以及源代码的链接。
save_npz method missing from scipy.sparse
看看你能不能得到这个工作。如果您可以创建csr
矩阵,则可以从其属性(或coo
等效项)重新创建它。如果需要,我可以做一个工作示例。
csr 到 h5py 示例
import numpy as np
import h5py
from scipy import sparse
M = sparse.random(10,10,.2, 'csr')
print(repr(M))
print(M.data)
print(M.indices)
print(M.indptr)
f = h5py.File('sparse.h5','w')
g = f.create_group('Mcsr')
g.create_dataset('data',data=M.data)
g.create_dataset('indptr',data=M.indptr)
g.create_dataset('indices',data=M.indices)
g.attrs['shape'] = M.shape
f.close()
f = h5py.File('sparse.h5','r')
print(list(f.keys()))
print(list(f['Mcsr'].keys()))
g2 = f['Mcsr']
print(g2.attrs['shape'])
M1 = sparse.csr_matrix((g2['data'][:],g2['indices'][:],
g2['indptr'][:]), g2.attrs['shape'])
print(repr(M1))
print(np.allclose(M1.A, M.A))
f.close()
生产
1314:~/mypy$ python3 stack43390038.py
<10x10 sparse matrix of type '<class 'numpy.float64'>'
with 20 stored elements in Compressed Sparse Row format>
[ 0.13640389 0.92698959 .... 0.7762265 ]
[4 5 0 3 0 2 0 2 5 6 7 1 7 9 1 3 4 6 8 9]
[ 0 2 4 6 9 11 11 11 14 19 20]
['Mcsr']
['data', 'indices', 'indptr']
[10 10]
<10x10 sparse matrix of type '<class 'numpy.float64'>'
with 20 stored elements in Compressed Sparse Row format>
True
酷替代
Mo = M.tocoo()
g = f.create_group('Mcoo')
g.create_dataset('data', data=Mo.data)
g.create_dataset('row', data=Mo.row)
g.create_dataset('col', data=Mo.col)
g.attrs['shape'] = Mo.shape
g2 = f['Mcoo']
M2 = sparse.coo_matrix((g2['data'], (g2['row'], g2['col'])),
g2.attrs['shape']) # don't need the [:]
# could also use sparse.csr_matrix or M2.tocsr()
【讨论】:
非常好!我认为如果您可以添加一个小的 sn-p 以将稀疏矩阵保存为 HDF 格式,这将非常有帮助 非常感谢。不复制HDF5读取的数据,是否可以构造coo或csr矩阵? 在最后一个示例中,我将g2['data']
数据集传递给coo_matrix
函数,但该函数将np.array(obj, ...)
应用于加载数据的输入。所以,不,你不能在不加载的情况下创建矩阵。
谢谢!我有一些最大的稀疏数组,我正在尝试更快地加载,但尝试避免复制数组似乎工作量太大。以上是关于将 scipy 稀疏矩阵存储为 HDF5的主要内容,如果未能解决你的问题,请参考以下文章
如何将 numpy.matrix 或数组转换为 scipy 稀疏矩阵