单机环境下,如果特征较为稀疏且矩阵较大,那么就会出现内存问题,如果不上分布式 + 不用Mars/Dask/CuPy等工具,那么稀疏矩阵就是一条比较容易实现的路。
参考: SciPy 稀疏矩阵笔记 Sparse稀疏矩阵主要存储格式总结 Python数据分析----scipy稀疏矩阵
SciPy 中有 7 种存储稀疏矩阵的数据结构:
bsr_matrix: Block Sparse Row matrixcoo_matrix: COOrdinate format matrixcsc_matrix: Compressed Sparse Column matrixcsr_matrix: Compressed Sparse Row matrixdia_matrix: Sparse matrix with DIAgonal storagedok_matrix: Dictionary Of Keys based sparse matrixlil_matrix: Row-based LInked List sparse matrix各个类型的用途:
如果想创建一个新的稀疏矩阵,lil_matrix,dok_matrix和coo_matrix会比高效,但是它们不适合做矩阵运算。如果想做矩阵运算,例如矩阵乘法、求逆等,应该用 CSC 或者 CSR 类型的稀疏矩阵。由于在内存中存储顺序的差异,csc_matrix 矩阵更适合取列切片,而 csr_matrix 矩阵更适合用来取行切片。这里只说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• 1Sparse data structures
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 1The 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 1Sparse-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 '<class 'numpy.float64'>' 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 '<class 'numpy.float64'>' 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