在 pandas 稀疏矩阵中查找全零列

Find all-zero columns in pandas sparse matrix

例如我有一个 coo_matrix A :

import scipy.sparse as sp
A = sp.coo_matrix([3,0,3,0],
                  [0,0,2,0],
                  [2,5,1,0],
                  [0,0,0,0])

如何得到结果[0,0,0,1],表示前3列包含非零值,只有第4列全为零。

PS : 无法将 A 转换为其他类型。
PS2 : 我尝试使用 np.nonzeros 但似乎我的实现不是很优雅。

方法 #1 我们可以这样做 -

# Get the columns indices of the input sparse matrix
C = sp.find(A)[1]

# Use np.in1d to create a mask of non-zero columns. 
# So, we invert it and convert to int dtype for desired output.
out = (~np.in1d(np.arange(A.shape[1]),C)).astype(int)

或者,为了使代码更短,我们可以使用减法 -[​​=22=]

out = 1-np.in1d(np.arange(A.shape[1]),C)

循序渐进运行 -

1) 输入数组和来自它的稀疏矩阵:

In [137]: arr             # Regular dense array
Out[137]: 
array([[3, 0, 3, 0],
       [0, 0, 2, 0],
       [2, 5, 1, 0],
       [0, 0, 0, 0]])

In [138]: A = sp.coo_matrix(arr) # Convert to sparse matrix as input here on

2) 获取非零列索引:

In [139]: C = sp.find(A)[1]

In [140]: C
Out[140]: array([0, 2, 2, 0, 1, 2], dtype=int32)

3) 使用 np.in1d 获取非零列的掩码:

In [141]: np.in1d(np.arange(A.shape[1]),C)
Out[141]: array([ True,  True,  True, False], dtype=bool)

4) 反转它:

In [142]: ~np.in1d(np.arange(A.shape[1]),C)
Out[142]: array([False, False, False,  True], dtype=bool)

5) 最后转换为 int dtype :

In [143]: (~np.in1d(np.arange(A.shape[1]),C)).astype(int)
Out[143]: array([0, 0, 0, 1])

另一种减法方法:

In [145]: 1-np.in1d(np.arange(A.shape[1]),C)
Out[145]: array([0, 0, 0, 1])

方法 #2 这是另一种方法,使用 matrix-multiplication -

可能更快
out = 1-np.ones(A.shape[0],dtype=bool)*A.astype(bool)

运行时测试

让我们在一个大而稀疏的矩阵上测试所有已发布的方法 -

In [29]: A = sp.coo_matrix((np.random.rand(4000,4000)>0.998).astype(int))

In [30]: %timeit 1-np.in1d(np.arange(A.shape[1]),sp.find(A)[1])
100 loops, best of 3: 4.12 ms per loop # Approach1

In [31]: %timeit 1-np.ones(A.shape[0],dtype=bool)*A.astype(bool)
1000 loops, best of 3: 771 µs per loop # Approach2

In [32]: %timeit 1 - (A.col==np.arange(A.shape[1])[:,None]).any(axis=1)
1 loops, best of 3: 236 ms per loop # @hpaulj's soln

In [33]: %timeit (A!=0).sum(axis=0)==0
1000 loops, best of 3: 1.03 ms per loop  # @jez's soln

In [34]: %timeit (np.sum(np.absolute(A.toarray()), 0) == 0) * 1
10 loops, best of 3: 86.4 ms per loop  # @wwii's soln 

实际的逻辑运算可以这样进行:

b = (A!=0).sum(axis=0)==0
# matrix([[False, False, False,  True]], dtype=bool)

现在,为了确保我能准确回答您的问题,我最好告诉您如何可以将布尔值转换为整数(尽管实际上,对于大多数应用程序我可以想到,如果你坚持使用 bool 的数组,你可以在 numpy 和朋友中做更多的事情):

b = b.astype(int)
#matrix([[0, 0, 0, 1]])

无论哪种方式,然后从 matrix 转换为 list,您可以这样做:

c = list(b.flat)
# [0, 0, 0, 1]

...不过,我不确定这是最好的做法:对于我能想象到的大多数应用程序,我可能只是将 numpy.array 转换为 [=18] =] 代替。

最近

类似,只是它想用 1 填充这些列并对其进行归一化。

我立即建议转置的lil格式。全 0 列将是这种格式的空列表。但坚持使用我建议的 coo 格式

np.nonzero(~(Mo.col==np.arange(Mo.shape[1])[:,None]).any(axis=1))[0]

或对于此 1/0 格式

1 - (Mo.col==np.arange(Mo.shape[1])[:,None]).any(axis=1)

在功能上等同于:

1 - np.in1d(np.arange(Mo.shape[1]),Mo.col)

sparse.find 将矩阵转换为 csr 求和并消除重复,然后返回 coo 得到 data, row,和 col 属性(它 returns)。

Mo.nonzero 在返回 colrow 属性之前使用 A.data != 0 消除 0。

np.ones(A.shape[0],dtype=bool)*A.astype(bool)解需要将A转换为csr格式进行乘法运算。

(A!=0).sum(axis=0) 也转换为 csr 因为列(或行)总和是通过矩阵乘法完成的。

所以不转换的要求是不现实的,至少在稀疏格式的范围内。

===============

对于 Divakar 的测试用例,我的 == 版本相当慢;小的没问题,但是用 1000 列创建的测试数组太大了。

在稀疏到足以包含多个 0 列的矩阵上进行测试:

In [183]: Arr=sparse.random(1000,1000,.001)
In [184]: (1-np.in1d(np.arange(Arr.shape[1]),Arr.col)).any()
Out[184]: True
In [185]: (1-np.in1d(np.arange(Arr.shape[1]),Arr.col)).sum()
Out[185]: 367

In [186]: timeit 1-np.ones(Arr.shape[0],dtype=bool)*Arr.astype(bool)
1000 loops, best of 3: 334 µs per loop
In [187]: timeit 1-np.in1d(np.arange(Arr.shape[1]),Arr.col)
1000 loops, best of 3: 323 µs per loop
In [188]: timeit 1-(Arr.col==np.arange(Arr.shape[1])[:,None]).any(axis=1)
100 loops, best of 3: 3.9 ms per loop
In [189]: timeit (Arr!=0).sum(axis=0)==0
1000 loops, best of 3: 820 µs per loop

转换为数组或密集矩阵,沿第一个轴求和绝对值,测试结果是否为零,转换为 int

>>> import numpy as np
>>> (np.sum(np.absolute(a.toarray()), 0) == 0) * 1
array([0, 0, 0, 1])
>>> (np.sum(np.absolute(a.todense()), 0) == 0) * 1
matrix([[0, 0, 0, 1]])
>>> 
>>> np.asarray((np.sum(np.absolute(a.todense()), 0) == 0), dtype = np.int32)
array([[0, 0, 0, 1]])
>>>

第一个是最快的 - 在我的机器上你的例子是 24 uS。

对于用 np.random.randint(0,3,(1000,1000)) 制作的矩阵,在我的机器上都是 25 毫秒。