统计2D NumPy数组中每行和每列的非零元素

28 投票
5 回答
34788 浏览
提问于 2025-04-16 04:36

我有一个 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))

有人能帮我解决这个问题吗?

5 个回答

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]
24

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

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

CSR矩阵的 indptr 属性表示数据中每一行的边界索引。因此,计算每个条目的差值就能得到每一行非零元素的数量。

同样地,如果想要计算每一列的非零元素数量,可以使用:

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 更快,但由于实现细节,它的运行速度更快。

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)

如果某一列/行没有非零元素,你想怎么处理呢?我们可以调整代码来解决这个问题。

撰写回答