在 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
在返回 col
和 row
属性之前使用 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 毫秒。
例如我有一个 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
在返回 col
和 row
属性之前使用 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 毫秒。