稀疏矩阵是一种特殊的矩阵,其非零元素数目远远少于零元素数目,并且非零元素分布没有规律。这种矩阵在实际应用中经常出现,例如在物理学、图形学和网络通信等领域。
稀疏矩阵其实也可以和一般的矩阵一样处理,之所以要把它区分开来进行特殊处理,是因为:一方面稀疏矩阵的存储空间开销通常比稠密矩阵要小得多,可以节省存储空间;另一方面,在计算稀疏矩阵时,可以利用其特殊的结构,采用专门的算法,提高计算效率和准确性。因此,稀疏矩阵在Scipy
库中被单独作为一个模块,以便被更好地处理和应用。
1. 主要功能
稀疏矩阵子模块(scipy.sparse
)的主要功能包括:
类别 | 说明 |
---|---|
稀疏数组类 | 支持各种格式的稀疏数组 |
稀疏矩阵类 | 支持各种格式的稀疏矩阵 |
稀疏矩阵工具 | 构建,保存,加载以及识别稀疏矩阵的各种函数 |
其他 | 包含压缩稀疏图例程,稀疏线性代数等子模块,以及一些异常处理方法 |
这里有个需要注意的地方是稀疏数组和稀疏矩阵的区别。这两个类别中的很多函数名称也类似,比如:bsr_array
和bsr_matrix
,coo_array
和coo_matrix
等等。
只要区别在于:***_matrix
类的函数是一种基于Compressed Sparse Row(CSR)和Compressed Sparse Column(CSC)格式的块稀疏矩阵表示方法。它使用一个字典来存储非零元素,其中每个元素对应于一个包含三个值的元组,分别表示该元素的行索引、列索引和非零元素的值。这种数据结构可以提供更好的计算性能和内存使用效率,特别适合于大规模的块稀疏矩阵计算。
而***_array
类的函数虽然类似于***_matrix
的数据结构,但它允许更大的灵活性。***_array
可以表示任意的稀疏数组,而不仅仅是块稀疏矩阵。它使用一个具有三个数组的元组来表示稀疏数组,其中第一个数组存储行索引,第二个数组存储列索引,第三个数组存储非零元素的值。这种数据结构适用于更通用的稀疏数组计算,但可能不如***_matrix
高效。
总之,***_matrix
和***_array
都是用于表示块稀疏矩阵或稀疏数组的数据结构。***_matrix
更适合于大规模的块稀疏矩阵计算,而***_array
适用于更通用的稀疏数组计算。
2. 使用示例
稀疏矩阵之所以成为单独的一个模块,是因为它的稀疏的特性在很多领域多有广泛的应用。scipy.sparse
子模块中提供了大概7种:
2.1. 使用稀疏矩阵
稀疏矩阵其实在运算上和使用普通矩阵一样。首先,构造一个创建矩阵的方法create_matrix
,这个方法会生成一个10x10
的矩阵,方法的参数N
表示随机在矩阵的N个位置中生成值。
from scipy import sparse
import numpy as np
# 创建一个10x10矩阵,其中有值的元素不超过N个
def create_matrix(N):
data = np.zeros((10, 10))
for _ in range(N):
row = np.random.randint(0, 10, 1)
col = np.random.randint(0, 10, 1)
data[row, col] = np.random.randint(1, 100, 1)
return data
create_matrix
创建的是普通矩阵,我们将生成的矩阵转换为稀疏矩阵后,计算方式差不多。
# 创建两个普通矩阵
m1 = create_matrix(8)
m2 = create_matrix(6)
# 计算点积
m1.dot(m2) # 返回m1和m2的点积结果
# 将普通矩阵变为稀疏矩阵
#(这里的演示用了7种类型中的一种bsr)
d1 = sparse.bsr_matrix(m1)
d2 = sparse.bsr_matrix(m2)
# 计算点积后,用toarray方法转换为二维数组
d1.dot(d2).toarray()
从上面的代码可以看出,用scipy.sparse
中的稀疏矩阵和使用一般矩阵差不多。
2.2. 稀疏矩阵的性能
我们使用稀疏矩阵,就是因为其运算性能比使用一般矩阵强,否则还不如直接用一般矩阵。下面,简单测试下scipy.sparse
模块下稀疏矩阵的性能。
先看其内存占用是否有减少,为了让性能差别能显著看出,先扩大测试矩阵为 1000x1000
。
import sys
def create_matrix(N):
data = np.zeros((1000, 1000))
for _ in range(N):
row = np.random.randint(0, 1000, 1)
col = np.random.randint(0, 1000, 1)
data[row, col] = np.random.randint(1, 100, 1)
return data
m1 = create_matrix(8)
m2 = create_matrix(6)
d1 = sparse.csr_matrix(m1)
d2 = sparse.csr_matrix(m2)
print("一般矩阵 m1 占用的空间:{}".format(sys.getsizeof(m1)))
print("一般矩阵 m2 占用的空间:{}".format(sys.getsizeof(m2)))
print("一般矩阵 d1 占用的空间:{}".format(sys.getsizeof(d1)))
print("一般矩阵 d2 占用的空间:{}".format(sys.getsizeof(d2)))
# 运行结果:
一般矩阵 m1 占用的空间:8000128
一般矩阵 m2 占用的空间:8000128
一般矩阵 d1 占用的空间:56
一般矩阵 d2 占用的空间:56
可以看出占用的空间明显缩小了。
再看点积的运算性能:(运行10轮,每轮100次)
%%timeit -r 10 -n 100
m1.dot(m2)
# 运行结果:
10.6 ms ± 136 µs per loop (mean ± std. dev. of 10 runs, 100 loops each)
稀疏矩阵的点积运算:
%%timeit -r 10 -n 100
d1.dot(d2)
# 运行结果:
137 µs ± 14.3 µs per loop (mean ± std. dev. of 10 runs, 100 loops each)
可以看出,运算性能差别非常大,一个是毫秒级别(10.6ms
)的,一个是微秒级别(137 µs
)的。
3. 总结
稀疏矩阵在矩阵中只是一种特殊的矩阵,然而在实际应用领域中,却应用极广,比如:在数值计算中,可以用于解决大规模线性代数方程组、大规模非线性方程组和非线性优化问题,以及求解大规模约束规划问题。
在模式识别中,如人脸识别、手写数字识别、文本分类等任务,可用于表示高维数据,提取特征并进行降维,提高识别准确率和计算效率。
在推荐系统中,处理大量用户和物品的数据时,稀疏矩阵可以有效地表示这些数据。
在社交网络中,因为一般社交关系都是稀疏的,所以可用于分析社交网络的结构和行为,例如社区检测、影响力传播。
此外,还可以用在计算机视觉,自然语言处理,生物信息学等等领域。所以,研究稀疏矩阵有其重要的实际意义。