在一个二维的NumPy数组中,统计每一行和每一列中非零元素的个数。

28
我有一个几乎全是非零值的NumPy矩阵,但偶尔会包含零值。我需要能够:
1. 计算每行中的非零值数量,并将该计数放入变量中,以便在后续操作中使用,可能通过迭代行索引并在迭代过程中执行计算来实现。 2. 计算每列中的非零值数量,并将该计数放入变量中,以便在后续操作中使用,可能通过迭代列索引并在迭代过程中执行计算来实现。
例如,我需要做的一件事是对每行进行求和,然后将每行总和除以每行中的非零值数量,为每个行索引报告单独的结果。然后,我需要对每列进行求和,然后将每列总和除以列中的非零值数量,也为每个列索引报告单独的结果。我还需要做其他一些事情,但只要我弄清楚如何完成这里列出的任务,其他的应该很容易。
我正在处理的代码如下。您可以看到我正在创建一个零数组,然后从csv文件中填充它。某些行将包含所有列的值,但其他行仍将在某些最后列中保留一些零,因此创建了上述问题。
以下代码的最后五行来自本论坛上的另一篇帖子。这最后五行代码返回一个零的行/列索引列表。但我不知道如何使用该结果信息来创建上述非零行计数和非零列计数。
ANOVAInputMatrixValuesArray=zeros([len(TestIDs),9],float)
j=0
for j in range(0,len(TestIDs)):
    TestID=str(TestIDs[j])
    ReadOrWrite='Read'
    fileName=inputFileName
    directory=GetCurrentDirectory(arguments that return correct directory)
    inputfile=open(directory,'r')
    reader=csv.reader(inputfile)
    m=0
    for row in reader:
        if m<9:
            if row[0]!='TestID':
                ANOVAInputMatrixValuesArray[(j-1),m]=row[2]
                m+=1
    inputfile.close()

IndicesOfZeros = indices(ANOVAInputMatrixValuesArray.shape) 
locs = IndicesOfZeros[:,ANOVAInputMatrixValuesArray == 0]
pts = hsplit(locs, len(locs[0]))
for pt in pts:
    print(', '.join(str(p[0]) for p in pt))

有谁可以帮助我吗?


这是一个非常啰嗦的方式来问一个很好的问题。 - abcd
5个回答

42
import numpy as np

a = np.array([[1, 0, 1],
              [2, 3, 4],
              [0, 0, 7]])

columns = (a != 0).sum(0)
rows    = (a != 0).sum(1)

变量(a != 0)是与原始a具有相同形状的数组,其中包含所有非零元素的True

.sum(x)函数将沿着轴x的元素求和。 True/False元素的总和是True元素的数量。

变量columnsrows包含原始数组中每个列/行中非零(元素!= 0)值的数量:

columns = np.array([2, 1, 3])
rows    = np.array([2, 3, 1])

编辑:整段代码可能看起来像这样(在原始代码中做了一些简化):

ANOVAInputMatrixValuesArray = zeros([len(TestIDs), 9], float)
for j, TestID in enumerate(TestIDs):
    ReadOrWrite = 'Read'
    fileName = inputFileName
    directory = GetCurrentDirectory(arguments that return correct directory)
    # use directory or filename to get the CSV file?
    with open(directory, 'r') as csvfile:
        ANOVAInputMatrixValuesArray[j,:] = loadtxt(csvfile, comments='TestId', delimiter=';', usecols=(2,))[:9]

nonZeroCols = (ANOVAInputMatrixValuesArray != 0).sum(0)
nonZeroRows = (ANOVAInputMatrixValuesArray != 0).sum(1)

编辑 2:

要获取所有列/行的平均值,请使用以下方法:

colMean = a.sum(0) / (a != 0).sum(0)
rowMean = a.sum(1) / (a != 0).sum(1)

如果在一列/行中没有非零元素,你想要做什么?然后我们可以调整代码来解决这个问题。


2
a.astype(bool)a != 0更快(至少在整数上)。 - joeln

24

在 scipy 稀疏矩阵 m 中,快速计算每一行非零元素的方法是:

np.diff(m.tocsr().indptr)

indptr属性是CSR矩阵的一个属性,指示数据中对应行之间边界的索引。因此,计算每个条目之间的差异将提供每行中非零元素的数量。

类似地,对于每列中非零元素的数量,请使用:

np.diff(m.tocsc().indptr)
如果数据已经以适当的形式存在,则这些代码将分别在O(m.shape[0]))O(m.shape[1])中运行,而不是在Marat和Finn的解决方案中的O(m.getnnz())
如果您需要行和列的非零计数,并且,例如,m已经是CSR,则可以使用:
row_nonzeros = np.diff(m.indptr)
col_nonzeros = np.bincount(m.indices)

它并不比先转换为CSC(O(m.getnnz()))以获取col_nonzeros渐进地更快,但由于实现细节,它更快。


9
scipy.sparse.csr_matrixcsc_matrix现在支持使用此方法进行getnnz(axis=0)getnnz(axis=1) - joeln

2
对于稀疏矩阵,请使用CSR/CSC矩阵支持的getnnz()函数。
例如:
a = scipy.sparse.csr_matrix([[0, 1, 1], [0, 1, 0]])
a.getnnz(axis=0)

array([0, 2, 1])

2
更快的方法是使用全部为1而非实际值的矩阵进行克隆。然后按行或列求和即可:
X_clone = X.tocsc()
X_clone.data = np.ones( X_clone.data.shape )
NumNonZeroElementsByColumn = X_clone.sum(0)
NumNonZeroElementsByRow = X_clone.sum(1)

这对我来说比Finn Årup Nielsen的解决方案快了50倍(1秒对53秒)

编辑: 也许您需要通过将NumNonZeroElementsByColumn转换为一维数组来进行翻译

np.array(NumNonZeroElementsByColumn)[0]

0

在我目前的scipy版本中,(a != 0)不能用于稀疏矩阵(scipy.sparse.lil_matrix)。

对于稀疏矩阵,我做了以下操作:

    (i,j) = X.nonzero()
    column_sums = np.zeros(X.shape[1])
    for n in np.asarray(j).ravel():
        column_sums[n] += 1.

我在想是否有更优雅的方式。

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