1. 程式人生 > >python稀疏矩陣的儲存與表示

python稀疏矩陣的儲存與表示

from scipy import sparse
help(sparse)

其中顯示了可以表示的7種稀疏矩陣型別:

There are seven available sparse matrix types:

        1. csc_matrix: Compressed Sparse Column format
        2. csr_matrix: Compressed Sparse Row format
        3. bsr_matrix: Block Sparse Row format
        4. lil_matrix: List of Lists format
5. dok_matrix: Dictionary of Keys format 6. coo_matrix: COOrdinate format (aka IJV, triplet format) 7. dia_matrix: DIAgonal format

對應的類:

bsr_matrix - Block Sparse Row matrix
coo_matrix - A sparse matrix in COOrdinate format
csc_matrix - Compressed Sparse Column matrix
csr_matrix - Compressed Sparse Row matrix
dia_matrix - Sparse matrix with DIAgonal storage dok_matrix - Dictionary Of Keys based sparse matrix lil_matrix - Row-based linked list sparse matrix spmatrix - Sparse matrix base clas

為了有效地構建矩陣,使用dok_matrix或者lil_matrix,lil_matrix類支援基本的切片和索引操作,語法與NumPy的arrays相似。COO格式也能有效率地構建矩陣。儘管與NumPy有許多相似性,但是強烈不建議使用NumPy的函式直接對稀疏矩陣格式進行操作,因為可能導致不正確的結果。如果將NumPy的函式用在這些矩陣上,首先檢查SciPy在對應的稀疏矩陣類上有沒有已經實現的操作,或者使用toarray()

方法將稀疏矩陣物件轉為NumPy的array。
實現乘法與轉置操作,實現轉換矩陣維CSC或CSR格式,lil_matrix格式是基於行的,所以轉為為CSR比CSC更有效率。所有的轉換在CSR,CSC和COO格式之間都是有效的,線性時間操作。
下面介紹7種稀疏矩陣:

1.coo_matrix

coo_matrix是最簡單的儲存方式。採用三個陣列row、col和data儲存非零元素的行下標,列下標與值。這三個陣列的長度相同一般來說,coo_matrix主要用來建立矩陣,因為coo_matrix無法對矩陣的元素進行增刪改等操作,一旦建立之後,除了將之轉換成其它格式的矩陣,幾乎無法對其做任何操作和矩陣運算。

>>> row = [0, 1, 2, 2]
>>> col = [0, 1, 2, 3]
>>> data = [1, 2, 3, 4]
# 生成coo格式的矩陣
>>> coo_mat = sparse.coo_matrix((data, (row, col)), shape=(4, 4))
>>> coo_mat
<4x4 sparse matrix of type '<class 'numpy.int32'>'
    with 4 stored elements in COOrdinate format>
>>> coo_mat.toarray()
array([[1, 0, 0, 0],
       [0, 2, 0, 0],
       [0, 0, 3, 4],
       [0, 0, 0, 0]])
>>> type(coo_mat)
<class 'scipy.sparse.coo.coo_matrix'>
>>> type(coo_mat.toarray())
<class 'numpy.ndarray'>

優點:
- 轉換成其它儲存格式很快捷簡便,轉換成csr/csc很快
- 允許重複的索引(例如在1行1列處存了值2.0,又在1行1列處存了值3.0,則轉換成其它矩陣時就是2.0+3.0=5.0)

缺點:
- 不支援切片和算術運算操作

2.dok_matrix與lil_matrix

dok_matrix和lil_matrix適用的場景是逐漸新增矩陣的元素。dok_matrix的策略是採用字典來記錄矩陣中不為0的元素。所以字典的key存的是記錄元素的位置資訊的元祖,value是記錄元素的具體值。

>>> S = sparse.dok_matrix((5, 5), dtype=np.float32)
>>> for i in range(5):
    for j in range(5):
        S[i,j] = i+j    # 更新元素


>>> S.toarray()
[[0. 1. 2. 3. 4.]
 [1. 2. 3. 4. 5.]
 [2. 3. 4. 5. 6.]
 [3. 4. 5. 6. 7.]
 [4. 5. 6. 7. 8.]]

優點:
- 對於遞增的構建稀疏矩陣很高效,比如定義該矩陣後,想進行每行每列更新值,可用該矩陣。當訪問某個單元,只需要O(1)
缺點:
不允許重複索引(coo中適用),但可以很高效的轉換成coo後進行重複索引

lil_matrix則是使用兩個列表儲存非0元素。data儲存每行中的非零元素,rows儲存非零元素所在的列。這種格式也很適合逐個新增元素,並且能快速獲取行相關的資料。

>>> l = sparse.lil_matrix((4, 4))
>>> l[1, 1] = 1
>>> l[1, 3] =2
>>> l[2, 3] = 3
>>> l.toarray()
array([[0., 0., 0., 0.],
       [0., 1., 0., 2.],
       [0., 0., 0., 3.],
       [0., 0., 0., 0.]])
>>> l.data
array([list([]), list([1.0, 2.0]), list([3.0]), list([])], dtype=object)
>>> l.rows
array([list([]), list([1, 3]), list([3]), list([])], dtype=object)

優點:

  • 適合遞增的構建成矩陣
  • 轉換成其它儲存方式很高效
  • 支援靈活的切片
    缺點:
  • 當矩陣很大時,考慮用coo
  • 算術操作,列切片,矩陣向量內積操作慢

3.dia_matrix

如果稀疏矩陣僅包含非0元素的對角線,則對角儲存格式(DIA)可以減少非0元素定位的資訊量。這種儲存格式對有限元素或者有限差分離散化的矩陣尤其有效。dia_matrix通過兩個陣列確定: data和offsets。其中data對角線元素的值;offsets:第i個offsets是當前第i個對角線和主對角線的距離。data[k:]儲存了offsets[k]對應的對角線的全部元素。
這裡寫圖片描述
上述的程式碼實現:

>>> data = np.array([[1, 2, 3, 4], [5, 6, 0, 0], [0, 7, 8, 9]])
>>> offsets = np.array([0, -2, 1])
>>> sparse.dia_matrix((data, offsets), shape=(4, 4)).toarray()
array([[1, 7, 0, 0],
       [0, 2, 8, 0],
       [5, 0, 3, 9],
       [0, 6, 0, 4]])

offsets[0]=0表示第0個對角線與主對角線的距離為0,表示第0個對角線就是主對角線,data[0]就是第0個對角線的值。offsets[1]=-2表示第1個對角線與主對角線距離為-2,此時該對角線在主對角線的左下方,對角線上數值的數量為4-2=2,對應的值為data[1, :2+1],此時data[1, 3:]為無效的值,在構造對角稀疏矩陣時不起作用。offsets[2]=1表示第2個對角線與主對角線距離為1,此時該對角線在主對角線的右上方,對角線上數值的數量為4-1=3,對應的值為data[2, 1:],此時data[2, :1]為無效的值,在構造對角稀疏矩陣時不起作用。

4.csr_matrix與csc_matrix

csr_matrix是按行對矩陣進行壓縮的,csc_matrix是按列對矩陣進行壓縮的。通過row_offsets,column_indices,data來確定矩陣。column_indices,data與coo格式的列索引與數值的含義完全相同,row_offsets表示元素的行偏移量。
用如下例子說明:

>>> 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]])

indices和data分別表示列索引和資料,第i行的列索引儲存在indices[indptr[i]:indptr[i+1]]中,對應的值為data[indptr[i]:indptr[i+1]]。即第0行的列索引為indices[0:2]=[0,2],值為data[0:2]=[1,2];第1行的列索引為indices[2:3]=[2],值為data[2:3]=[3]…
CSR格式常用於讀入資料後進行稀疏矩陣計算。

CSR:

優點:
- 高效的稀疏矩陣算術操作
- 高效的行切片
- 快速地矩陣向量內積操作
缺點:
- 緩慢地列切片操作(可以考慮csc)
- 轉換到稀疏結構代價較高(可以考慮lil,dok)

CSC:

優點:
- 高效的稀疏矩陣算術操作
- 高效的列切片
- 快速地矩陣向量內積操作(不如csr,bsr塊)
缺點:
- 緩慢地行切片操作(可以考慮csr)
- 轉換到稀疏結構代價較高(可以考慮lil,dok)

5.bsr_matrix

基於行的塊壓縮,通過row_offsets,column_indices,data來確定矩陣。與csr相比,只是data中的元資料由0維的數變為了一個矩陣(塊),其餘完全相同。

>>> 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]).repeat(4).reshape(6,2,2)
>>> bsr_matrix((data,indices,indptr), shape=(6,6)).todense()
matrix([[1, 1, 0, 0, 2, 2],
        [1, 1, 0, 0, 2, 2],
        [0, 0, 0, 0, 3, 3],
        [0, 0, 0, 0, 3, 3],
        [4, 4, 5, 5, 6, 6],
        [4, 4, 5, 5, 6, 6]])

優點:

  • 很類似於csr,更適合於矩陣的某些子矩陣很多值,在某些情況下比csr和csc計算更高效。

稀疏矩陣的存取

用save_npz儲存單個稀疏矩陣

>>> scipy.sparse.save_npz('sparse_matrix.npz', sparse_matrix)
>>> sparse_matrix = scipy.sparse.load_npz('sparse_matrix.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

對於儲存到npz檔案中的CSR格式的稀疏矩陣,內容為:

data.npy
format.npy
indices.npy
indptr.npy
shape.npy