单机环境下,如果特征较为稀疏且矩阵较大,那么就会出现内存问题,如果不上分布式 + 不用Mars/Dask/CuPy等工具,那么稀疏矩阵就是一条比较容易实现的路。
参考:
SciPy 稀疏矩阵笔记
Sparse稀疏矩阵主要存储格式总结
Python数据分析----scipy稀疏矩阵
SciPy 中有 7 种存储稀疏矩阵的数据结构:
各个类型的用途:
这里只说lil_matrix,因为笔者用的这款,且比较方便。
lil_matrix 是第二直观的稀疏矩阵存储方式。它的全称是 row-based linked list sparse matrix 。它包含两个要素:rows 和 data
示例代码一:
>>> from scipy.sparse import lil_matrix
>>> l = lil_matrix((6,5))
>>> l[2,3] = 1
>>> l[3,4] = 2
>>> l[3,2] = 3
>>> print l.toarray()
[[ 0. 0. 0. 0. 0.]
[ 0. 0. 0. 0. 0.]
[ 0. 0. 0. 1. 0.]
[ 0. 0. 3. 0. 2.]
[ 0. 0. 0. 0. 0.]
[ 0. 0. 0. 0. 0.]]
>>> print l.data
[[] [] [1.0] [3.0, 2.0] [] []]
>>> print l.rows
[[] [] [3] [2, 4] [] []]
示例代码二:
# 原始矩阵为
array([[1., 0., 0., 0., 0.],
[0., 0., 2., 0., 3.],
[0., 0., 0., 0., 0.],
[0., 0., 0., 4., 0.],
[0., 0., 0., 0., 5.]])
mat_lil = sparse.lil_matrix(mat_coo) # 几种稀疏矩阵之间可以相互转化
# mat_lil 的两要素
mat_lil.rows
array([list([0]), list([2, 4]), list([]), list([3]), list([4])],
dtype=object)
mat_lil.data
array([list([1.0]), list([2.0, 3.0]), list([]), list([4.0]), list([5.0])],
dtype=object)
示例代码三:
# 创建矩阵
lil = sparse.lil_matrix((6, 5), dtype=int)
# 设置数值
# set individual point
lil[(0, -1)] = -1
# set two points
lil[3, (0, 4)] = [-2] * 2
# set main diagonal
lil.setdiag(8, k=0)
# set entire column
lil[:, 2] = np.arange(lil.shape[0]).reshape(-1, 1) + 1
# 转为array
lil.toarray()
'''
array([[ 8, 0, 1, 0, -1],
[ 0, 8, 2, 0, 0],
[ 0, 0, 3, 0, 0],
[-2, 0, 4, 8, -2],
[ 0, 0, 5, 0, 8],
[ 0, 0, 6, 0, 0]])
'''
# 查看数据
lil.data
'''
array([list([0, 2, 4]), list([1, 2]), list([2]), list([0, 2, 3, 4]),
list([2, 4]), list([2])], dtype=object)
'''
lil.rows
'''
array([[list([8, 1, -1])],
[list([8, 2])],
[list([3])],
[list([-2, 4, 8, -2])],
[list([5, 8])],
[list([6])]], dtype=object)
'''
矩阵属性
from scipy.sparse import csr_matrix
### 共有属性
mat.shape # 矩阵形状
mat.dtype # 数据类型
mat.ndim # 矩阵维度
mat.nnz # 非零个数
mat.data # 非零值, 一维数组
### COO 特有的
coo.row # 矩阵行索引
coo.col # 矩阵列索引
### CSR\CSC\BSR 特有的
bsr.indices # 索引数组
bsr.indptr # 指针数组
bsr.has_sorted_indices # 索引是否排序
bsr.blocksize # BSR矩阵块大小
通用方法
import scipy.sparse as sp
### 转换矩阵格式
tobsr()、tocsr()、to_csc()、to_dia()、to_dok()、to_lil()
mat.toarray() # 转为array
mat.todense() # 转为dense
# 返回给定格式的稀疏矩阵
mat.asformat(format)
# 返回给定元素格式的稀疏矩阵
mat.astype(t)
### 检查矩阵格式
issparse、isspmatrix_lil、isspmatrix_csc、isspmatrix_csr
sp.issparse(mat)
### 获取矩阵数据
mat.getcol(j) # 返回矩阵列j的一个拷贝,作为一个(mx 1) 稀疏矩阵 (列向量)
mat.getrow(i) # 返回矩阵行i的一个拷贝,作为一个(1 x n) 稀疏矩阵 (行向量)
mat.nonzero() # 非0元索引
mat.diagonal() # 返回矩阵主对角元素
mat.max([axis]) # 给定轴的矩阵最大元素
### 矩阵运算
mat += mat # 加
mat = mat * 5 # 乘
mat.dot(other) # 坐标点积
resize(self, *shape)
transpose(self[, axes, copy])
存储 - save_npz
scipy.sparse.save_npz('sparse_matrix.npz', sparse_matrix)
sparse_matrix = scipy.sparse.load_npz('sparse_matrix.npz')
读取 - load_npz
# 从npz文件中读取
test_x = sparse.load_npz('./data/npz/test_x.npz')
存储大小比较
a = np.arange(100000).reshape(1000,100)
a[10: 300] = 0
b = sparse.csr_matrix(a)
# 稀疏矩阵压缩存储到npz文件
sparse.save_npz('b_compressed.npz', b, True) # 文件大小:100KB
# 稀疏矩阵不压缩存储到npz文件
sparse.save_npz('b_uncompressed.npz', b, False) # 文件大小:560KB
# 存储到普通的npy文件
np.save('a.npy', a) # 文件大小:391KB
# 存储到压缩的npz文件
np.savez_compressed('a_compressed.npz', a=a) # 文件大小:97KB• 1
Sparse data structures
In [1]: arr = np.random.randn(10)
In [2]: arr[2:-2] = np.nan
In [3]: ts = pd.Series(pd.arrays.SparseArray(arr))
In [4]: ts
Out[4]:
0 0.469112
1 -0.282863
2 NaN
3 NaN
4 NaN
5 NaN
6 NaN
7 NaN
8 -0.861849
9 -2.104569
dtype: Sparse[float64, nan]
pandas中sparse变成一种格式,如dtype: Sparse[float64, nan]
之前Pandas版本有:pd.SparseDataFrame()
,不过这个在新版本被移除了。
SparseSeries and SparseDataFrame were removed in pandas 1.0.0. This migration guide is present to aid in migrating from previous versions.
一种方式:
# Previous way
>>> pd.SparseDataFrame({"A": [0, 1]})
# New way
In [31]: pd.DataFrame({"A": pd.arrays.SparseArray([0, 1])})
Out[31]:
A
0 0
1 1
The SparseDataFrame.default_kind and SparseDataFrame.default_fill_value attributes have no replacement.
另一种方式:
# Previous way
>>> from scipy import sparse
>>> mat = sparse.eye(3)
>>> df = pd.SparseDataFrame(mat, columns=['A', 'B', 'C'])
# New way
In [32]: from scipy import sparse
In [33]: mat = sparse.eye(3)
In [34]: df = pd.DataFrame.sparse.from_spmatrix(mat, columns=['A', 'B', 'C'])
In [35]: df.dtypes
Out[35]:
A Sparse[float64, 0]
B Sparse[float64, 0]
C Sparse[float64, 0]
dtype: object
第三种新建:
In [38]: dense = pd.DataFrame({"A": [1, 0, 0, 1]})
In [39]: dtype = pd.SparseDtype(int, fill_value=0)
In [40]: dense.astype(dtype)
Out[40]:
A
0 1
1 0
2 0
3 1
# SparseDataFrame -> dataframe
In [36]: df.sparse.to_dense()
Out[36]:
A B C
0 1.0 0.0 0.0
1 0.0 1.0 0.0
2 0.0 0.0 1.0
# SparseDataFrame -> spacy.coo
In [37]: df.sparse.to_coo()
Out[37]:
<3x3 sparse matrix of type ''
with 3 stored elements in COOrdinate format>
Sparse-specific properties, like density, are available on the .sparse accessor.
In [41]: df.sparse.density
Out[41]: 0.3333333333333333
从scipy -> pandas
pd.DataFrame.sparse.from_spmatrix
可以使用
In [47]: from scipy.sparse import csr_matrix
In [48]: arr = np.random.random(size=(1000, 5))
In [49]: arr[arr < .9] = 0
In [50]: sp_arr = csr_matrix(arr)
In [51]: sp_arr
Out[51]:
<1000x5 sparse matrix of type ''
with 517 stored elements in Compressed Sparse Row format>
In [52]: sdf = pd.DataFrame.sparse.from_spmatrix(sp_arr)
In [53]: sdf.head()
Out[53]:
0 1 2 3 4
0 0.956380 0.0 0.0 0.000000 0.0
1 0.000000 0.0 0.0 0.000000 0.0
2 0.000000 0.0 0.0 0.000000 0.0
3 0.000000 0.0 0.0 0.000000 0.0
4 0.999552 0.0 0.0 0.956153 0.0
In [54]: sdf.dtypes
Out[54]:
0 Sparse[float64, 0]
1 Sparse[float64, 0]
2 Sparse[float64, 0]
3 Sparse[float64, 0]
4 Sparse[float64, 0]
dtype: object
从pandas -> scipy
In [61]: A, rows, columns = ss.sparse.to_coo(row_levels=['A', 'B'],
....: column_levels=['C', 'D'],
....: sort_labels=True)
....:
In [62]: A
Out[62]:
<3x4 sparse matrix of type ''
with 3 stored elements in COOrdinate format>
In [63]: A.todense()
Out[63]:
matrix([[0., 0., 1., 3.],
[3., 0., 0., 0.],
[0., 0., 0., 0.]])
In [64]: rows
Out[64]: [(1, 1), (1, 2), (2, 1)]
In [65]: columns
Out[65]: [('a', 0), ('a', 1), ('b', 0), ('b', 1)]
一般情况scipy.sparse可以直接使用,进行train_test_split
,
如果pandas.sparse
不行,那么就转成pandas x = x.sparse.to_dense()
应该也是可以的:
fea_datasets = csr_matrix((data, (row, col)), shape=(row_index, max_col+1)).toarray()
#当特征维度过大时,选下面这种方式(加toarray()和不加都是对的),内存不容易爆掉
#fea_datasets = csr_matrix((data, (row, col)), shape=(row_index, max_col+1))
x_train, x_test, y_train, y_test = train_test_split(fea_datasets, target_list, test_size = 0.2, random_state = 0)
return x_train, x_test, y_train, y_test
笔者看到一般scipy中csr_matrix格式一般支持sklearn的模型训练;
如果是pandas.sparse
可能会报错,所以,需要变成dataframe