检查Scipy稀疏矩阵的密度

4
有没有一种好的方法来测量或检查scipy.sparse矩阵的密度?
例如:
import scipy.sparse
import numpy as np

row  = np.array([0,3,1,0])
col  = np.array([0,3,1,2])
data = np.array([4,5,7,9])

mat = scipy.sparse.coo_matrix((data,(row,col)), shape=(4,4))
print mat.todense()

[[4 0 9 0]
 [0 7 0 0]
 [0 0 0 0]
 [0 0 0 5]]

也许可以返回一些总体密度的统计数据,比如每行的平均占用率(例如,第一行占用了2/4的值,第二行占用了1/4,第三行占用了0/4,第四行占用了1/4,因此平均占用率/密度将是1/4),标准差,方差等。也许有更好的密度指标可以应用,不依赖于矩阵的大小(假设它足够大)。
4个回答

11

一种方法是使用getnnz()方法来确定给定行、列或整个矩阵中非零元素的数量。

让我们以一个示例稀疏矩阵sp_mat为例。

sp_mat.todense()

matrix([[0, 1, 1, 1, 1],
        [1, 0, 1, 0, 0]])

整个矩阵中非零元素的数量:

sp_mat.getnnz()
# 6

给定行中非零元素的数量:

sp_mat[0,:].getnnz()
# 4

所有行的非零元素计数:

sp_mat.getnnz(axis=1)
# array([4, 2], dtype=int32)

列中非零元素的数量:

sp_mat[:,1].getnnz()
# 1

所有列的非零元素计数:

sp_mat.getnnz(axis=0)
#  array([1, 1, 2, 1, 1])

这可以与矩阵的形状进行比较,以计算密度:

sp_mat.shape
# (2, 5)

感谢 @slaw 提出的 axis 参数建议。 - David Maust
你的例子简洁而有用,但似乎第0行的非零元素数量应该是4而不是3? - David Chen
谢谢 @DavidChen,你说得对。我已经更新了例子。 - David Maust

8

为了得到一个简单的密度分数,即矩阵中非零元素的比例,我使用以下代码:

mat
density = mat.getnnz() / np.prod(mat.shape)

0

我不知道是否存在这样的密度函数,但你可以搜索 sparse 文档。

很容易得到整个数组中非零元素的数量,并通过迭代获得每一行的非零元素数量。

mat.nnz
Out[55]: 4

[i.nnz for i in mat.tolil()]
Out[57]: [2, 1, 0, 1]

我使用了tolil,因为coo不允许行迭代(或索引)。csr也可以。

您还可以直接使用lil格式的属性,因为它们是列表的列表。这比在lil格式的行上进行迭代要快得多。该操作在每次迭代时创建一个新的稀疏矩阵,这是一个相对较慢的操作。

mal=mat.tolil()

mal.data
Out[65]: array([[4, 9], [7], [], [5]], dtype=object)

mal.rows
Out[67]: array([[0, 2], [1], [], [3]], dtype=object)

[len(i) for i in mal.rows]
Out[68]: [2, 1, 0, 1]

将其转换为数组,并计算您想要的所有统计信息:
In [76]: s=np.array([len(i) for i in mal.rows])

In [77]: np.mean(s/4.)
Out[77]: 0.25

In [78]: np.std(s/4.)
Out[78]: 0.17677669529663689

把这个行数应用到密集数组可能会更快

In [93]: timeit [np.count_nonzero(i) for i in mat.A]
10000 loops, best of 3: 44.3 µs per loop

In [94]: timeit [i.nnz for i in mat.tolil()]
100 loops, best of 3: 2.67 ms per loop

我刚意识到,至少在密集版本中,你可以通过求和布尔值来获取非零计数,而无需迭代:

In [6]: (mat.A!=0).sum(axis=1)
Out[6]: array([2, 1, 0, 1])

(虽然对于这个小样本数组来说,这比另一个密集版本要慢)。

稀疏版本也可以工作,但速度较慢(但比迭代稀疏版本要快)。主要是布尔测试;行求和是通过矩阵乘法完成的。

In [9]: (mat!=0).sum(axis=1)
Out[9]: 
matrix([[2],
        [1],
        [0],
        [1]])

这是一种更快的稀疏求和方法:

In [13]: mat1=mat.tocsr(); mat1.data[:]=1;mat1.sum(axis=1)
Out[13]: 
matrix([[2],
        [1],
        [0],
        [1]])

tocsr 函数会进行一次复制;我们将其 data 更改为全 1,并对其求和。

因此,如果速度很重要,您需要使用实际大小的矩阵进行自己的时间测试。


0
你可以将mat中的所有元素计数,如下所示: all=sum(mat.count()) 接下来,你可以计算所有零元素的数量,如下所示: zeros=all-count_nonzero(mat) 通过这些值,你可以估算密度,如下所示: density=zeros/all

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