3. SciPy创建稀疏矩阵

严格意义上讲ndarray数据类型应属数组而非矩阵,而matrix才是矩阵,这个在NumPy创建matrix一章里有讲述,是最基本的矩阵matrix创建方法,忘记了可以回头看看。 本章利用scipy.sparse模块下的类提供创建稀疏矩阵的方法,例如bsr_matrix稀疏矩阵类。

什么是稀疏矩阵?按数据结构领域知名学者严老师的定义稀疏矩阵是一个矩阵里有小于5%非0数据的矩阵可以视为稀疏矩阵,如果矩阵的总数据量较大,完成存储这个矩阵会有大量的0存储,浪费空间,所以对稀疏矩阵的存储有必要研究用少量的内存存储稀疏矩阵,在数据结构里有时用三元组来解决。

3.1 coo_matrix类创建稀疏矩阵

接下来我们看看在SciPy里如何解决对稀疏矩阵的存储?效率如何? 三元组,即ijv,记录稀疏矩阵里的非零数据的行i、列j坐标以及值v三个数据。下面按三元组的方式来创建稀疏矩阵。

#coding:utf-8
import numpy as np
import scipy.sparse as ss
import random
# 随机产生行、列坐标和值
a = random.sample(range(0, 9), 5)
b = random.sample(range(0, 9), 5)
c = random.sample(range(1, 100), 5)
# 将list数据转为array数组
rows = np.array(a)
print rows,"#rows"
cols = np.array(b)
print cols, "#cols"
v = np.array(c)
print v,"#values"
# coo_matrix函数生成稀疏矩阵
sparseM = ss.coo_matrix((v,(rows,cols)))
print sparseM, "#sparseM,", "shape is ", sparseM.shape
# todense将稀疏矩阵转为完全阵
fullM = sparseM.todense()
print fullM, "#fullM,", "shape is ", fullM.shape

程序执行结果:

[8 0 6 7 4] #rows
[3 8 1 2 7] #cols
[ 1 48 99 62 94] #values
  (8, 3)    1
  (0, 8)    48
  (6, 1)    99
  (7, 2)    62
  (4, 7)    94 #sparseM, shape is  (9, 9)
[[ 0  0  0  0  0  0  0  0 48]
 [ 0  0  0  0  0  0  0  0  0]
 [ 0  0  0  0  0  0  0  0  0]
 [ 0  0  0  0  0  0  0  0  0]
 [ 0  0  0  0  0  0  0 94  0]
 [ 0  0  0  0  0  0  0  0  0]
 [ 0 99  0  0  0  0  0  0  0]
 [ 0  0 62  0  0  0  0  0  0]
 [ 0  0  0  1  0  0  0  0  0]] #fullM, shape is  (9, 9)

需主要sparseM和fullM每次可能都不同,因为行、列、值都是随机产生的。 有关coo_matrix稀疏矩阵的处理方法函数可以参考相应的帮助,示例里给出了一个todense函数的使用方法。

3.2 csc_matrix类创建稀疏矩阵

csc_matrix类提供了很多方法来创建稀疏矩阵。

1). 可以直接调用类的构造函数(参阅"类"一章下的__init__的解析)将一个数组或矩阵转化为稀疏矩阵存储。

import numpy as np
import scipy.sparse as ss
a = np.zeros((3, 4))
a[1, 2] = 12
a[2, 2] = 22
print a
print ss.csc_matrix(a)

程序执行结果:

[[  0.   0.   0.   0.]
 [  0.   0.  12.   0.]
 [  0.   0.  22.   0.]] # a
  (1, 2)    12.0
  (2, 2)    22.0 # csc_matrix

2).可以创建一个空的稀疏矩阵,即全0,然后通过索引赋值获得一个非空的稀疏矩阵,但用csc_matrix这样去做时SciPy建议改为lil_matrix更高效,见执行结果的warning信息。

import scipy.sparse as ss
x = ss.csc_matrix((4, 3))
#x = ss.lil_matrix((4, 3))
print "x --"
print x
x[1, 2] = 12
x[3, 1] = 23
print x
print x.todense()

程序执行结果:

x --

/usr/lib/python2.7/dist-packages/scipy/sparse/compressed.py:730: SparseEfficiencyWarning: Changing the sparsity structure of a csc_matrix is expensive. lil_matrix is more efficient.
  SparseEfficiencyWarning)
  (3, 1)    23.0
  (1, 2)    12.0
[[  0.   0.   0.]
 [  0.   0.  12.]
 [  0.   0.   0.]
 [  0.  23.   0.]]

3). 三元组的方法创建稀疏矩阵,和coo_matrix类创建的方式一样指定i、j、v,只需将3.1节的程序里的coo_matrix改为csc_matrix即可。

import numpy as np
import scipy.sparse as ss
import random
a = random.sample(range(0, 9), 5)
b = random.sample(range(0, 9), 5)
c = random.sample(range(1, 100), 5)
rows = np.array(a)
print rows,"#rows"
cols = np.array(b)
print cols, "#cols"
v = np.array(c)
print v,"#values"
sparseM = ss.csc_matrix((v,(rows,cols)))
print sparseM, "#sparseM,", "shape is ", sparseM.shape
fullM = sparseM.todense()
print fullM, "#fullM,", "shape is ", fullM.shape
print sparseM.sum(), sparseM.nonzero()
print sparseM.get_shape()

4). 真正的csc_matrix创建稀疏矩阵 csc_matrix类,实际就是数据结构里按列存储稀疏矩阵时,给出每列里非零个数,以及列里那几行是非零值。 下面是官方文档:

csc_matrix((data, indices, indptr), [shape=(M, N)])
    is the standard CSC representation where the row indices for column i are stored in indices[indptr[i]:indptr[i+1]] and their corresponding values are stored in data[indptr[i]:indptr[i+1]]. If the shape parameter is not supplied, the matrix dimensions are inferred from the index arrays.

官方例子:

import numpy as np
from scipy.sparse import csc_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])
print csc_matrix((data, indices, indptr), shape=(3, 3)).toarray()

程序执行结果:

array([[1, 0, 4],
       [0, 0, 5],
       [2, 3, 6]])

以下是解析这个例子:

#第0列
i = 0 
# 0列那些行非0?
indices[indptr[i]:indptr[i+1]]
= indices[indptr[0]:indptr[1]]
= indices[0:2]
= [0, 2]
# 0列非零行对应的值
data[indptr[i]:indptr[i+1]]
= data[indptr[0]:indptr[1]]
= data[0:2]
= [1, 2]

i = 1
indices[indptr[i]:indptr[i+1]]
= indices[indptr[1]:indptr[2]]
= indices[1:2]
= [2]
data[indptr[i]:indptr[i+1]]
= data[indptr[1]:indptr[2]]
= data[2:3]
= [3]
# 第2列
i = 2
# 非0行?
indices[indptr[i]:indptr[i+1]]
= indices[indptr[2]:indptr[3]]
= indices[3:6]
= [0, 1, 2]
# 对应的值
data[indptr[i]:indptr[i+1]]
= data[2:3]
= [4,5,6]

总结一下csc_matrix的各个参数含义,data是稀疏矩阵的值;indices给出各列非0数据所在的行号;indptr则是给出前i列非0元素个数:indptr[0]表示第0列前有0个,indptr[1]第1列前共有0列那么多个实际是第0列非0个数,indptr[2]在则是记录了0、1列一共有多少个非0元素。

5). 思考一下如下的稀疏矩阵怎么用csc_matrix构造出来?

[[  0.   0.   0.   0.]
 [  0.   0.  12.   0.]
 [  0.   0.  0.   22.]]

首先可以写出data = [12, 22],然后给出行坐标indices = [1, 2] ,最后给出前n列的非零数值的个数indptr = [0,0,0,1,2]

import numpy as np
from scipy.sparse import csc_matrix
indptr = np.array([0,0,0,1,2])
indices = np.array([1, 2])
data = np.array([12, 22])
csc = csc_matrix((data, indices, indptr), shape=(3, 4))
print csc, "#csc"
print csc.todense(), "#csc.todense"

程序执行结果

  (1, 2)    12
  (2, 3)    22 #csc
[[ 0  0  0  0]
 [ 0  0 12  0]
 [ 0  0  0 22]] #csc.todense

3.3 csr_matrix类创建稀疏矩阵

csr_matrix类是按行存储矩阵,和csc_matrix按列真好相对应。也有很多函数,就不多解释了。 同样是下面这个矩阵,怎样用csr_matrix实现?

[[  0.   0.   0.   0.]
 [  0.   0.  12.   0.]
 [  0.   0.  0.   22.]]

首先可以写出data = [12, 22],然后给出列坐标indices = [2, 3] ,最后给出前n行的非0值个数indptr = [0,0,1,2]。 程序如下:

import numpy as np
from scipy.sparse import csr_matrix
indptr = np.array([0,0,1,2])
indices = np.array([2,3])
data = np.array([12, 22])
csr = csr_matrix((data, indices, indptr), shape=(3, 4))
print csr, "#csr"
print csr.todense(), "#csr.todense"

3.4 bsr_matrix类创建稀疏矩阵

在理解了csr_matrix、csc_matrix类之后在看bsr_matrix类就不难了,这里的b是block的意思。csr_matrix、csc_matrix类是用data里的值去在indices和indptr确定的位置上填充一个数据,而bsr_matrix类,则是用一个矩阵x去填充这个位置的数据、0值位置用与x矩阵同型0矩阵填充,所以整个稀疏矩阵会扩大。

import numpy as np
from scipy.sparse import bsr_matrix
indptr = np.array([0,0,1,2])
indices = np.array([2,3])
data = np.array([12, 22]).repeat(6).reshape(2, 2, 3)
bsr = bsr_matrix((data, indices, indptr), shape=(6, 12))
print bsr, "#bsr"
print bsr.todense(), "#bsr.todense"

程序执行结果如下:

  (2, 6)    12
  (2, 7)    12
  (2, 8)    12
  (3, 6)    12
  (3, 7)    12
  (3, 8)    12
  (4, 9)    22
  (4, 10)   22
  (4, 11)   22
  (5, 9)    22
  (5, 10)   22
  (5, 11)   22 #bsr
[[ 0  0  0  0  0  0  0  0  0  0  0  0]
 [ 0  0  0  0  0  0  0  0  0  0  0  0]
 [ 0  0  0  0  0  0 12 12 12  0  0  0]
 [ 0  0  0  0  0  0 12 12 12  0  0  0]
 [ 0  0  0  0  0  0  0  0  0 22 22 22]
 [ 0  0  0  0  0  0  0  0  0 22 22 22]] #bsr.todense

3.5 其他稀疏矩类

scipy.sparse里还有dia_matrix 、dok_matrix比较简单,在这里就不再继续展示了。