CSR是Compressed Sparse Row的缩写,是一种稀疏矩阵的压缩存储格式。稀疏矩阵是指其中大部分元素为0的矩阵。在机器学习中,由于特征维度通常很高,因此特征矩阵往往是稀疏矩阵。使用CSR格式可以节省存储空间并加快矩阵运算的速度。
在CSR格式中,矩阵被视为三个数组的组合:data、indices和indptr。
例如,对于一个3行4列的稀疏矩阵,其data、indices和indptr数组分别为:
from scipy.sparse import csr_matrix
data = [1, 2, 3, 4, 5]
indices = [0, 2, 1, 1, 2]#由于只有4列,所有indices数组中的元素在0,1,2,3中取值(这不是说元素个数不超过4)
indptr = [0, 2, 3, 5]
SM=csr_matrix((data, indices, indptr), shape=(3,4)).toarray()
SM
array([[1, 0, 2, 0],
[0, 3, 0, 0],
[0, 4, 5, 0]])
NNZ是n维数组中非零元素的数量,是衡量稀疏矩阵中元素数量的一个指标。在稀疏矩阵中,大部分元素的值为0,因此NNZ通常远小于矩阵的总元素数。
NNZ可以用来衡量稀疏矩阵的稠密程度。通常情况下,NNZ与稀疏矩阵中非零元素的分布、密度以及矩阵的大小等因素有关。因为计算机存储非零元素需要占用内存空间,所以NNZ也可以用来评估稀疏矩阵的存储空间需求。
在机器学习中,很多算法都需要处理高维稀疏数据,例如文本分类、推荐系统等。因此,NNZ也是评估这些算法性能的一个重要指标之一。通常情况下,NNZ越小,算法处理稀疏数据的效率越高,但同时也可能会牺牲一定的精度。
例如,对于一个3行4列的稀疏矩阵,其NNZ为5,因为矩阵中共有5个非零元素。
NNZ:Number of nonzero matrix elements
ri=R[i+1]-R[i]
,其中ri
就表示第i行有ri
个非0元素ri
个非0元素分别要对齐(align)到哪一列(放到哪一列),则由c=COL_INDEX[R[i+1]:R[i]]
中的元素决定(c
中的元素格式也是ri
个)data,indices,indptr
For example, the matrix
( 5 0 0 0 0 8 0 0 0 0 3 0 0 6 0 0 ) {\displaystyle {\begin{pmatrix}5&0&0&0\\ 0&8&0&0\\ 0&0&3&0\\ 0&6&0&0\\ \end{pmatrix}}} 5000080600300000
这个稀疏矩阵比较简单,它的SCR编码和COO编码恰好一致
这个例子似乎看不出来CSR和COO的区别,**Don’t worry about it **下一节将介绍更多例子
is a 4 × 4 matrix with 4 nonzero elements, hence
V = [ 5 8 3 6 ]
COL_INDEX = [ 0 1 2 1 ]
ROW_INDEX = [ 0 1 2 3 4 ]
assuming a zero-indexed language.
我查看wikipedie的这篇文章时,给出的ROW_INDEX是[0,1,2,3]这应该是错误的,长度上就不符合CSR编码特征
所以我将其纠正为[0,1,2,3,4],包含m+1=5个元素
To extract a row, we first define:
row_start = ROW_INDEX[row]
row_end = ROW_INDEX[row + 1]
Vi=V[row_start:row_end]
Then we take slices from V and COL_INDEX starting at row_start and ending at row_end.
To extract the row 1 (the second row) of this matrix we set row_start=1 and row_end=2. Then we make the slices V[1:2] = [8]
and COL_INDEX[1:2] = [1]
.
Another example, the matrix
( 10 20 0 0 0 0 0 30 0 40 0 0 0 0 50 60 70 0 0 0 0 0 0 80 ) {\displaystyle {\begin{pmatrix}10&20&0&0&0&0\\ 0&30&0&40&0&0\\ 0&0&50&60&70&0\\ 0&0&0&0&0&80\\ \end{pmatrix}}} 10000203000005000406000070000080
is a 4 × 6 matrix (24 entries) with 8 nonzero elements, so
V = [ 10 20 30 40 50 60 70 80 ]
COL_INDEX = [ 0 1 1 3 2 3 4 5 ]
ROW_INDEX = [ 0 2 4 7 8 ]
The whole is stored as 21 entries: 8 in V, 8 in COL_INDEX, and 5 in ROW_INDEX.
The (old and new) Yale sparse matrix formats are instances of the CSR scheme. The old Yale format works exactly as described above, with three arrays; (旧和新的)Yale稀疏矩阵格式是CSR格式的一种实例。旧的Yale格式与上述描述完全相同,使用三个数组表示;新的格式将行索引和列索引合并成一个单独的数组,并将矩阵的对角线单独处理。
the new format combines ROW_INDEX and COL_INDEX into a single array and handles the diagonal of the matrix separately.
For logical adjacency matrices, the data array can be omitted, as the existence of an entry in the row array is sufficient to model a binary adjacency relation.
It is likely known as the Yale format because it was proposed in the 1977 Yale Sparse Matrix Package report from Department of Computer Science at Yale University.
对于逻辑邻接矩阵,可以省略数据数组,因为在行数组中存在一个条目就足以表示二元邻接关系。
Yale格式可能因为它是在耶鲁大学的计算机科学系于1977年发布的Yale稀疏矩阵包报告中提出的,而得名。
编码任意一个矩阵为CSR格式需要的存储的entries为 S = 2 N N T + ( m + 1 ) S=2NNT+(m+1) S=2NNT+(m+1)
In this case above, the CSR representation contains 13 entries, compared to 16 in the original matrix.
The CSR format saves on memory only when N N Z < ( m ( n − 1 ) − 1 ) / 2 NNZ < (m (n − 1) − 1) / 2 NNZ<(m(n−1)−1)/2.
只有满足上述等式的情况下,CSR
scipy.sparse.csr_matrix — SciPy v1.10.1 Manual
csr_matrix((data, indices, indptr), [shape=(M, N)])
indices[indptr[i]:indptr[i+1]]
data[indptr[i]:indptr[i+1]]
.from scipy.sparse import csr_matrix
indptr = np.array([0, 2, 3, 6])
indices = np.array([0, 2, 2, 0, 1, 2])
data = np.array([1, 2, 3, 4, 5, 6])
csr_matrix((data, indices, indptr), shape=(3, 3)).toarray()
# array([[1, 0, 2],
# [0, 0, 3],
# [4, 5, 6]])
上述矩阵中,共有3行3列
第i行 | 非零元素 | 非零元素个数 | 从0开始累加各行非零元素个数 | indptr(index_column) | slice |
---|---|---|---|---|---|
0 | 1,2 | 2 | 0+2=2 | 0 | 0:2 |
1 | 3 | 1 | 2+1=3 | 2 | 2:3 |
2 | 4,5,6 | 3 | 3+3=6 | 3 | 3:6 |
fictitious | 6 |
现在,我们在第二行的位置插入一个全0向量:(只需要修改indptr数组)
from scipy.sparse import csr_matrix
indptr = np.array([0, 2,2, 3, 6])
indices = np.array([0, 2, 2, 0, 1, 2])
data = np.array([1, 2, 3, 4, 5, 6])
csr_matrix((data, indices, indptr), shape=(4, 3)).toarray()
# array([[1, 0, 2],
# [0, 0, 0],
# [0, 0, 3],
# [4, 5, 6]])
还可以通过shape参数,追加全零列,而不需要改动三个数组
from scipy.sparse import csr_matrix
indptr = np.array([0, 2,2, 3, 6])
indices = np.array([0, 2, 2, 0, 1, 2])
data = np.array([1, 2, 3, 4, 5, 6])
SM=csr_matrix((data, indices, indptr), shape=(4, 5)).toarray()
SM
# array([[1, 0, 2, 0, 0],
# [0, 0, 0, 0, 0],
# [0, 0, 3, 0, 0],
# [4, 5, 6, 0, 0]])
观察每行的非零元素:
# 列出矩阵M每行的非零元素的列索引以及值
for i in range(len(indptr)-1):
r_slice=slice(indptr[i],indptr[i+1])#r_slice表示row_slice
print(f'{str(indices[r_slice]):>10}',
f'{r_slice}',
f'{str(data[r_slice])}'
)
[0 2] slice(0, 2, None) [1 2]
[2] slice(2, 3, None) [3]
[0 1 2] slice(3, 6, None) [4 5 6]
上面的输出分为3列,第一列是第i行非零元素的索引
第2列是第i行以及该行之前的非零元素,对indptr数组进行切片
第3列表示第i行非零元素的取值
由三个数组计算出系数矩阵
from scipy.sparse import csr_matrix
indptr = np.array([0, 2,2, 3, 6])
indices = np.array([0, 2, 2, 0, 1, 2])
data = np.array([1, 2, 3, 4, 5, 6])
SM=csr_matrix((data, indices, indptr), shape=(4,3)).toarray()
print(SM)
将给定的稀疏矩阵编码成三个数组
SM=csr_matrix((data, indices, indptr), shape=(4, 3)).toarray()
res=csr_matrix(SM)
print(res)
print(f'{res.indices=}\n{res.indptr=}\n{res.data=}')