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

时间:2016-09-26 20:29:06

标签: python numpy scipy sparse-matrix

例如我有一个coo_matrix A:

import pandas as pd
from patsy import dmatrices
import statsmodels.api as sm 

y, X = dmatrices( 'label ~ age + gender', data=df, return_type='dataframe')
mod = sm.Logit(y, X)
res = mod.fit()
print res.summary()

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

PS:无法将A转换为其他类型 PS2:我尝试使用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]) ,但似乎我的实现不是很优雅。

4 个答案:

答案 0 :(得分:1)

方法#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)

或者,为了缩短代码,我们可以使用减法 -

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 

答案 1 :(得分:1)

实际的逻辑操作可以这样执行:

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

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

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

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

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

......尽管如此,我不确定这是最好的事情:对于我能想象的大多数应用程序,我可能只会转换为一维numpy.array c = b.A.flatten()代替。

答案 2 :(得分:1)

最近

scipy.sparse.coo_matrix how to fast find all zeros column, fill with 1 and normalize

类似,除了它想用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以获取datarow和{ {1}}属性(它返回)。

在返回colMo.nonzero属性之前,

A.data != 0使用col消除0。

row解决方案需要将np.ones(A.shape[0],dtype=bool)*A.astype(bool)格式转换为A格式才能进行乘法。

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

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

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

对于Divakar的测试用例,我的csr版本非常慢;小的可以,但是用1000列创建了太大的测试数组。

在足够稀疏且具有多个0列的矩阵上进行测试:

==

答案 3 :(得分:0)

转换为数组或密集矩阵,沿第一轴求和绝对值,测试结果为零,转换为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美元。

对于使用np.random.randint(0,3,(1000,1000))制作的矩阵,我的机器上的所有矩阵都是25 mS。