對於一個矩陣而言,若數值為零的元素遠遠多於非零元素的個數,且非零元素分布沒有規律時,這樣的矩陣被稱作稀疏矩陣;與之相反,若非零元素數目占據絕大多數時,這樣的矩陣被稱作稠密矩陣。
稀疏矩陣在工程應用中經常被使用,尤其是在通信編碼和機器學習中。若編碼矩陣或特征表達矩陣是稀疏矩陣時,其計算速度會大大提升。對於機器學習而言,稀疏矩陣應用非常廣,比如在數據特征表示、自然語言處理等領域。用稀疏表示和工作在計算上代價很高,需要專門處理稀疏矩陣的表示和操作等,但是這些操作可以大幅提升性能。
Python中的稀疏矩陣
SciPy使用多個數據結構為創建稀疏矩陣提供了工具,以及將稠密矩陣轉化為稀疏矩陣的工具。許多在Numpy數組上運行的線性代數Numpy和SciPy函數可以在SciPy稀疏數組上操作。此外,使用Numpy數據結構的機器學習庫也可以在Scipy稀疏數組上操作,例如,用於機器學習的scikit-learning和用於深度學習的Keras。
Scipy中有可以表示的7種稀疏矩陣類型:
- csc_matrix: Compressed Sparse Column format
- csr_matrix: Compressed Sparse Row format
- bsr_matrix: Block Sparse Row format
- lil_matrix: List of Lists format
- dok_matrix: Dictionary of Keys format
- coo_matrix: COOrdinate format (aka IJV, triplet format)
- dia_matrix: DIAgonal format
下面介紹常用的幾種稀疏矩陣類型:
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]])
優點:
- 轉換成其它存儲格式很快捷簡便,轉換成csr/csc很快
- 允許重復的索引(例如在1行1列處存了值2.0,又在1行1列處存了值3.0,則轉換成其它矩陣時就是2.0+3.0=5.0)
缺點:
- 不支持切片和算術運算操作
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.]])
優點:
- 適合遞增的構建成矩陣
- 轉換成其它存儲方式很高效
- 支持靈活的切片
缺點:
- 當矩陣很大時,考慮用coo
- 算術操作,列切片,矩陣向量內積操作慢
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]])
以官方文檔為例,此時data代表的是存儲的值的數組,indices代表的是每一行中第幾列有對應data中的元素,即從indices中可以推斷出列的信息,indptr則用來推斷出行的信息,默認元素開始為0,第一個元素為2,則證明第一行中有2-0=2個元素,所以將data數組中前另個元素寫入第一行中,而indices前兩個元素為0,2,則代表第0列和第2列。前兩第二個元素為3,證明第二行中有3-2=1個元素,該元素為data[2]=3,且存儲在indices[2] = 2列中。依次類推。
CSR格式常用於讀入數據后進行稀疏矩陣計算。
CSR:
優點:
- 高效的稀疏矩陣算術操作
- 高效的行切片
- 快速地矩陣向量內積操作
缺點:
- 緩慢地列切片操作(可以考慮csc)
- 轉換到稀疏結構代價較高(可以考慮lil,dok)
CSC:
優點:
- 高效的稀疏矩陣算術操作
- 高效的列切片
- 快速地矩陣向量內積操作(不如csr,bsr塊)
缺點:
- 緩慢地行切片操作(可以考慮csr)
- 轉換到稀疏結構代價較高(可以考慮lil,dok)
稀疏矩陣的存取
用save_npz保存單個稀疏矩陣
>>> scipy.sparse.save_npz('sparse_matrix.npz', sparse_matrix)
>>> sparse_matrix = scipy.sparse.load_npz('sparse_matrix.npz')