MATLAB中如何构造一个矩阵,该矩阵中每一列个数不等
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MATLAB中如何构造一个矩阵,该矩阵中每一列个数不等相关的知识,希望对你有一定的参考价值。
我想解决这样的问题:在一个无向连通图中,共120个点,每个点和x个点有通路,每个点对应的x值不同。要构造120列的矩阵,第i列存放:与第i个点相连接的点的标号。用120个变量太复杂了。
有没有其他方法
如何有效地将稀疏矩阵列添加到另一个稀疏矩阵中的每一列?
【中文标题】如何有效地将稀疏矩阵列添加到另一个稀疏矩阵中的每一列?【英文标题】:How would one go about adding a sparse matrix column to every column in another sparse matrix efficiently? 【发布时间】:2021-11-18 16:45:46 【问题描述】:感谢您花时间查看我的问题。
我正在尝试创建一个程序,该程序可以将值数组添加到稀疏矩阵中的每个“列”,如下所示:
A = [[1,1,0],
[0,0,1],
[1,0,1]]
B = [[1],
[0],
[1]]
A + B = [[2,2,1],
[0,0,1],
[2,1,2]]
以稀疏矩阵的坐标格式表示,如下所示:
A = [[0,0,1],
[2,0,1],
[0,1,1],
[1,2,1],
[2,2,1]]
B = [[0,0,1],
[2,0,1]]
A + B = [[0,0,2],
[2,0,2],
[0,1,2],
[2,1,1],
[0,2,1],
[1,2,1],
[2,2,2]]
我正在处理由于其大小而必须以稀疏方式表示的大型矩阵。我需要能够向矩阵中的每一列添加一列值,但需要使用专门处理稀疏三元组的算法。
我花了一整天的时间来解决这个问题,实际上是连续 10 个小时,我真的很震惊,我无法在任何地方找到一个好的答案。使用乘法执行此操作既简单又高效,但是 scipy 或 numpy 中似乎没有任何时间和空间高效的解决方案来执行此操作,或者如果有,当我发现时它会杀了我。我试图实施一个解决方案,但最终效率极低。
基本上,我的解决方案在技术上确实可行,但时间效率极差,它遵循以下步骤:
-
检查 A 和 B 行之间的共享值,将相关的三元组值相加
添加来自 A 的唯一值
在矩阵的列中为 i 添加 B_row_i、x_i、B_value_i,检查我们没有添加 A 三元组中的逐字值。
至少,我认为这就是它的作用......我现在完全筋疲力尽了,我开始在编码时逐步退出。如果有人能提出快速解决方案,将不胜感激!
from scipy.sparse import coo_matrix
from tqdm import tqdm
class SparseCoordinates:
def __init__(self,coo_a,coo_b):
self.shape = coo_a.shape
self.coo_a_rows = coo_a.row
self.coo_a_cols = coo_a.col
self.coo_a_data = coo_a.data
self.coo_b_rows = coo_b.row
self.coo_b_cols = coo_b.col
self.coo_b_data = coo_b.data
self.coo_c_rows = []
self.coo_c_cols = []
self.coo_c_data = []
def __check(self,a,b,c,lr,lc,lv):
for i in range(len(lr)):
if lr[i] == a and lc[i] == b and lv[i] == c:
return True
return False
def __check_shared_rows(self):
for i in tqdm(range(len(self.coo_a_rows))):
for j in range(len(self.coo_b_rows)):
if self.coo_a_rows[i] == self.coo_b_rows[j]:
self.coo_c_rows.append(self.coo_a_rows[i])
self.coo_c_cols.append(self.coo_a_cols[i])
self.coo_c_data.append(self.coo_a_data[i] + self.coo_b_data[j])
def __add_unique_from_a(self):
a_unique = set(self.coo_a_rows) - set(self.coo_b_rows)
for i in tqdm(range(len(self.coo_a_rows))):
if self.coo_a_rows[i] in a_unique:
self.coo_c_rows.append(self.coo_a_rows[i])
self.coo_c_cols.append(self.coo_a_cols[i])
self.coo_c_data.append(self.coo_a_data[i])
def __add_all_remaining_from_b(self):
for i in tqdm(range(len(self.coo_b_rows))):
for j in range(self.shape[1]):
if not self.__check(self.coo_b_rows[i],j,self.coo_b_data[i],
self.coo_a_rows,self.coo_a_cols,self.coo_a_data):
self.coo_c_rows.append(self.coo_b_rows[i])
self.coo_c_cols.append(j)
self.coo_c_data.append(self.coo_b_data[i])
def add(self):
self.__check_shared_rows()
self.__add_unique_from_a()
self.__add_all_remaining_from_b()
return coo_matrix((self.coo_c_data,(self.coo_c_rows,self.coo_c_cols)),shape=self.shape)
【问题讨论】:
您能否构建一个稀疏矩阵,其中每列都是您要添加的列,然后执行以下操作:[***.com/questions/4565685/… ? 我可以做这样的事情。最初我觉得为了加法的目的用相同的值填充稀疏矩阵可能效率低下,但考虑到您的响应和其他响应,这可能是最好的方法。您认为这是最有效的方法,还是有另一种方法可以通过 2D 矩阵的每个 1D 切片添加 1D 数组? 【参考方案1】:对于numpy
数组,A+B
完成这项工作是因为broadcasting
。 broadcasting
在核心迭代级别实现,利用了strides
。 scipy.sparse
没有实现 broadcasting
。如果 B
扩展为 (3,3) 矩阵以匹配 A
添加确实有效
In [76]: A = np.array([[1,1,0],
...: [0,0,1],
...: [1,0,1]])
...:
...: B = np.array([[1],
...: [0],
...: [1]])
In [77]: B.shape
Out[77]: (3, 1)
In [78]: A+B
Out[78]:
array([[2, 2, 1],
[0, 0, 1],
[2, 1, 2]])
稀疏:
In [79]: from scipy import sparse
In [81]: M=sparse.csr_matrix(A);N=sparse.csr_matrix(B)
矩阵乘法非常适合稀疏矩阵:
In [82]: M@N
Out[82]:
<3x1 sparse matrix of type '<class 'numpy.int64'>'
with 3 stored elements in Compressed Sparse Row format>
In [84]: N.T@M
Out[84]:
<1x3 sparse matrix of type '<class 'numpy.int64'>'
with 3 stored elements in Compressed Sparse Column format>
矩阵乘法用于行或列索引,以及求和。
定义一个助手:
In [86]: O=sparse.csr_matrix([1,1,1])
In [87]: O
Out[87]:
<1x3 sparse matrix of type '<class 'numpy.int64'>'
with 3 stored elements in Compressed Sparse Row format>
In [88]: N@O
Out[88]:
<3x3 sparse matrix of type '<class 'numpy.int64'>'
with 6 stored elements in Compressed Sparse Row format>
在总和中使用它:
In [89]: M+N@O
Out[89]:
<3x3 sparse matrix of type '<class 'numpy.int64'>'
with 7 stored elements in Compressed Sparse Row format>
In [90]: _.A
Out[90]:
array([[2, 2, 1],
[0, 0, 1],
[2, 1, 2]])
这个矩阵比M
稀疏 - 更少的 0。这降低了使用稀疏矩阵的好处。
coo_matrix
格式用于创建矩阵和构建新矩阵,如sparse.bmat
、sparse.hstack
和sparse.vstack
,但csr_matrix
用于大多数数学。有时可以通过intptr
数组迭代“行”来进行自定义数学运算。有各种各样的答案可以做到这一点。使用coo
属性的数学通常不是一个好主意。
但是,由于 coo
重复项在转换为 csr
时会相加,因此我可能会设置 M
和 N
的 coo 格式的合理总和。
In [91]: Mo=M.tocoo(); No=N.tocoo()
In [95]: Oo=O.tocoo()
In [98]: rows = np.concatenate((Mo.row, Oo.row))
In [99]: cols = np.concatenate((Mo.col, Oo.col))
In [100]: data = np.concatenate((Mo.data, Oo.data))
In [101]: sparse.coo_matrix((data,(rows,cols)),M.shape)
Out[101]:
<3x3 sparse matrix of type '<class 'numpy.int64'>'
with 8 stored elements in COOrdinate format>
In [102]: _.A
Out[102]:
array([[2, 2, 1],
[0, 0, 1],
[1, 0, 1]])
这里我加入了Mo
和Oo
的属性。我可以对No
的属性做同样的事情,但由于我已经拥有Oo
,我认为这是值得的工作。我将把它留给你。
In [103]: Oo.row,Oo.col,Oo.data
Out[103]:
(array([0, 0, 0], dtype=int32),
array([0, 1, 2], dtype=int32),
array([1, 1, 1]))
In [104]: No.row,No.col,No.data
Out[104]: (array([0, 2], dtype=int32), array([0, 0], dtype=int32), array([1, 1]))
我不知道这种coo
方法是否值得努力。
【讨论】:
感谢您的回复!最初我对创建另一个等维稀疏矩阵犹豫不决,但我认为鉴于您对此事的看法,这可能是最好的方法。【参考方案2】:由于您已经在使用 scipy 稀疏数组和 numpy,因此您可以非常有效地执行此操作。在您熟悉稀疏数组的索引之前,高效稀疏操作的实际实现可能看起来有点复杂,但 numpy 有一些很好的工具。
您只需几行代码即可完成此操作,无论是 COO、CSR 还是 CSC 格式。作为快速概述,压缩的稀疏矩阵格式具有实际存储数据的 3 个 1 维 numpy 数组。第一个,m.indptr
每行(对于 CSR)或每列(对于 CSC)都有一个条目加一个。 m.indptr[i]
给出了行 i 开始的其他两个数组的索引,所以s = m.indptr[i]:m.indptr[i+1]
给出了行中所有这些索引的切片。下一个数组m.indices
给出了未压缩轴中每个非零值的索引——CSR 的列,CSC 的行。 m.data
给出了这些位置的实际值。您可以经常利用这种格式来大大简化代码并节省时间。例如,如果您想将 i 列中的每个非零值加 1:
m.data[m.indptr[i]:m.indptr[i+1]] += 1
就是这样!现在,我们可能需要为每一行或每一列添加不同的值。在 for 循环中执行该步骤对于大问题是不切实际的。一个有用的通用技术是构造一个与最初用零填充的数据数组大小相同的数组,然后使用切片和 numpy 函数(如 take、diff 和 repeat)的某种组合使用 indptr 和索引数组准备更改,然后最终更新数据数组一次。
当您向矩阵的每一列添加向量时,对值的修改仅取决于行索引 - 因此,如果我们能够找到一种方法来一次有效地将正确的值添加到每一行,我们好的。如果我们有一个 CSR 矩阵,我们就可以准确地知道每行中有多少非零元素,并且它们将在数据数组中一起排序。使用np.diff(m.indptr)
非常方便地给出每行中非零元素的数量,然后m.data += np.repeat(vec, np.diff(m.indptr))
给出一个与数据数组大小相同的数组,对第一行中的每个元素重复一次 vec[0],vec[1 ] 对第二行中的每个元素重复一次,依此类推,然后将结果添加到数据数组中。这仅需要 O(nnz) 时间和内存,这是最优的,并且可以充分利用 numpy 向量化和并行性,无需额外的努力。
如果由于某些其他约束,您需要根据未压缩轴中的索引更改值,则效率稍低的解决方案遵循完全相同的想法,但您不使用重复和 indptr 数组,而是使用np.take(vec, m.indices)
。这将创建一个数组,其中元素 i 取自 m.indices[i] 给出的 vec 的索引 - 换句话说,它采用与每个非零元素的行/列索引相对应的 vec 元素。这涉及以较不连续的方式访问内存以从 vec 中提取正确的值,但在实践中,由于 vec 的大小通常比数据数组更小,并且数据更新是连续的,因此速度非常快。
编辑添加:scikit-learn 中的sparsefuncs 模块有一些非常有指导意义的代码,以及类似的sparsefuncs_fast Cython 模块。您可能会注意到,我提供的方法与行和列缩放函数中使用的代码非常相似——这就是我学习的地方。
【讨论】:
以上是关于MATLAB中如何构造一个矩阵,该矩阵中每一列个数不等的主要内容,如果未能解决你的问题,请参考以下文章