快速查找 Python 中 CSC 矩阵中每一行的非零条目索引

Fast way to find indexes of nonzero entries for every row in a CSC matrix in Python

这是当前的实现:

def nonzero_indexes_by_row(input):
    return [
        np.nonzero(row)[1] 
        for row in csr_matrix(input.T)
    ]

矩阵非常大(1.5M,500K),因为我正在访问行,所以我必须先将 CSC 转换为 CSR。结果将是一个二维列表,每个列表包含一个非零索引列表,对应于原始矩阵中的行。

当前过程需要 20 分钟。有没有更快的方法?

您的代码的一个有趣的替代方法是将您的数组转换为 COOrdinate 格式,然后读取其 rowcol 属性:

def nonzero_indices_by_coo(input):
    cx = input.T.tocoo()
    res = [ [] for i in range(cx.shape[0]) ]
    for i, j in zip(cx.row, cx.col):
        res[i].append(j)
    return res

它 returns 一个普通 pythonic 列表的列表,而不是 Numpy 数组, 但这应该不是什么重要的区别。

我注意到您的代码在内部使用源数组的 t运行sposition (T 运算符)所以我在我的代码中做了同样的事情。

为了比较执行速度,我创建了以下稀疏数组(2000 by 300):

r = 2000; c = 300
x = scipy.sparse.lil_matrix( (r,c) )
for _ in range(r):
    x[np.random.randint(0,r-1), np.random.randint(0,c-1)] = np.random.randint(1,100)

我的代码 运行 比你的快 12 倍。

更快的解决方案(其他格式)

或者生成一个 2-D (Numpy) 数组可能会更好, 有 2 行:

  • 第一行 - 连续非零元素的行索引,
  • 第二行 - 列索引。

要生成这样的结果,您可以使用以下代码:

def nonzero_indices_2d(input):
    cx = input.T.tocoo()
    return np.array([cx.row, cx.col])

运行比我的第一个解决方案快 4 倍。

当然,你的代码的其他部分应该重新编写,以消耗 以另一种格式给出的索引。

稀疏数组也有其 own nonzero 方法:

arr.nonzero()

生成 2 行 Numpy 索引数组。这个函数 运行s 还没有 快了几个百分点。

所以,假设 2-D 结果格式是可以接受的(而不是 列表的列表),也许你不需要任何自己的函数来获取这些 指数。

另一个需要考虑的细节:是否(在所有版本中)应该有 使用 t运行 位置。 您的选择,但没有 t运行sposition 每个版本 代码会 运行 快一点。

当然可以。您非常接近理想的解决方案,但您正在分配一些不必要的数组。这是一个更快的方法:

from scipy import sparse
import numpy as np

def my_impl(csc):
    csr = csc.tocsr()
    return np.split(csr.indices, csr.indptr[1:-1])

def your_impl(input):
    return [
        np.nonzero(row)[1] 
        for row in sparse.csr_matrix(input)
    ]

## Results

# demo data
csc = sparse.random(15000, 5000, format="csc")

your_result = your_impl(csc)
my_result = my_impl(csc)

## Tests for correctness

# Same result
assert all(np.array_equal(x, y) for x, y in zip(your_result, my_result))
# Right number of rows
assert len(my_result) == csc.shape[0]

## Speed

%timeit my_impl(csc)
# 31 ms ± 1.26 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

%timeit your_impl(csc)
# 1.49 s ± 19.9 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

附带问题,为什么要转置矩阵?那么你不会得到列的非零条目吗?如果那是你想要的,你甚至不需要转换为 csr,只需 运行:

np.split(csc.indices, csc.indptr[1:-1])