稀疏矩阵中忽略重复的条目

11

我尝试按照文档所建议的方法,从一个包含(data, (rows, cols))值的列表中初始化csc_matrixcsr_matrix

sparse = csc_matrix((data, (rows, cols)), shape=(n, n))

问题是,我用于生成datarowscols向量的方法会为某些点引入重复项。默认情况下,scipy将添加重复条目的值。然而,在我的情况下,对于给定的(row,col),这些重复项在data中具有完全相同的值。

我想要达到的目标是让scipy忽略第二个条目(如果已经存在一个),而不是将它们相加。

假设我无法改进生成算法以避免生成重复项,是否有参数或其他创建稀疏矩阵的方式可以忽略重复项?

目前,具有data = [4,4]; cols = [1,1]; rows = [1,1];的两个条目生成一个稀疏矩阵,该矩阵在(1,1)处的值为8,而期望的值为4

>>> c = csc_matrix(([4, 4], ([1,1],[1,1])), shape=(3,3))
>>> c.todense()
matrix([[0, 0, 0],
        [0, 8, 0],
        [0, 0, 0]])

我也知道可以使用二维numpy的unique函数来过滤它们,但是列表非常大,所以这不是一个有效的选项。

其他可能的答案是什么:有没有办法指定如何处理重复项?例如保留minmax而不是默认的sum


我非常确定答案是否定的,没有内置的方法可以更改重复项的行为。但是你不应该太快地放弃使用 np.unique:无论你的列表有多大,scipy 都会将它们转换为数组并在幕后执行类似的操作,因此你应该尝试使用它。 - Jaime
1
np.unique 是一维的,因此处理这些二维坐标需要额外的努力。 - hpaulj
1
正确,但像这个这样的技巧会将其变成二维。 - Imanol Luengo
3个回答

10

在您的示例中创建一个中间dok矩阵是可行的:

In [410]: c=sparse.coo_matrix((data, (cols, rows)),shape=(3,3)).todok().tocsc()

In [411]: c.A
Out[411]: 
array([[0, 0, 0],
       [0, 4, 0],
       [0, 0, 0]], dtype=int32)

一个 coo 矩阵将您的输入数组放入其 datacolrow 属性中,不做任何更改。只有在转换为 csc 后才发生求和。 todok 直接从 coo 属性中加载字典。它创建一个空白的 dok 矩阵,并填充它。
dok.update(izip(izip(self.row,self.col),self.data))

所以,如果有重复的(row,col)值,只有最后一个会保留下来。这使用标准的Python字典哈希来查找唯一键。
以下是使用np.unique的方法。我不得不构造一个特殊的对象数组,因为unique操作在1d上,而我们有一个2d索引。
In [479]: data, cols, rows = [np.array(j) for j in [[1,4,2,4,1],[0,1,1,1,2],[0,1,2,1,1]]]

In [480]: x=np.zeros(cols.shape,dtype=object)

In [481]: x[:]=list(zip(rows,cols))

In [482]: x
Out[482]: array([(0, 0), (1, 1), (2, 1), (1, 1), (1, 2)], dtype=object)

In [483]: i=np.unique(x,return_index=True)[1]

In [484]: i
Out[484]: array([0, 1, 4, 2], dtype=int32)

In [485]: c1=sparse.csc_matrix((data[i],(cols[i],rows[i])),shape=(3,3))

In [486]: c1.A
Out[486]: 
array([[1, 0, 0],
       [0, 4, 2],
       [0, 1, 0]], dtype=int32)

我不知道哪种方法更快。


根据liuengo的链接,获取唯一索引的另一种方法:

rc = np.vstack([rows,cols]).T.copy()
dt = rc.dtype.descr * 2
i = np.unique(rc.view(dt), return_index=True)[1]

为了使用视图(view)更改数据类型,rc 必须拥有自己的数据,因此需要使用 .T.copy()
In [554]: rc.view(dt)
Out[554]: 
array([[(0, 0)],
       [(1, 1)],
       [(2, 1)],
       [(1, 1)],
       [(1, 2)]], 
      dtype=[('f0', '<i4'), ('f1', '<i4')])

看起来是个不错的技巧。我现在无法尝试,转换类型需要很多时间/内存吗? - Imanol Luengo
3
todok()在scipy 0.19版本中不能忽略重复项。 - WeiChing 林煒清
1
是的,coo.todok 现在包括了一个 self.sum_duplicates() 行。解决方案是按照我描述的进行更新,但不需要这个 sum - hpaulj
@hpaulj 你的意思是使用 todok() 函数的代码而不包含 self.sum_duplicates() 这行吗? - SarahData
似乎 i=np.unique(x,return_index=True)[1] 应该改为 i=np.unique(np.unique(x,return_index=True)) 才能得到 [0, 1, 2, 4] 的输出。 - qwerty

4

由于您在重复的(行、列)中的data值相同,您可以按照以下方式获取唯一的行、列和值:

rows, cols, data = zip(*set(zip(rows, cols, data)))

例子:

data = [4, 3, 4]
cols = [1, 2, 1]
rows = [1, 3, 1]

csc_matrix((data, (rows, cols)), shape=(4, 4)).todense()

matrix([[0, 0, 0, 0],
        [0, 8, 0, 0],
        [0, 0, 0, 0],
        [0, 0, 3, 0]])



rows, cols, data = zip(*set(zip(rows, cols, data)))
csc_matrix((data, (rows, cols)), shape=(4, 4)).todense()

matrix([[0, 0, 0, 0],
        [0, 4, 0, 0],
        [0, 0, 0, 0],
        [0, 0, 3, 0]])

2

为了更新hpaulj对于SciPy最新版本的回答,现在针对这个问题最简单的解决方案是,假设有一个COO矩阵c,现在:

dok=sparse.dok_matrix((c.shape),dtype=c.dtype)
dok._update(zip(zip(c.row,c.col),c.data))

new_c = dok.tocsc()

这是由于dok中update()函数中的新包装器,防止对数组进行直接更改,需要使用下划线绕过包装器。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接