从scipy中的稀疏矩阵的每一行中有效地选择随机非零列

时间:2015-05-21 23:59:16

标签: python numpy scipy

我正在尝试为大型稀疏SciPy矩阵的每一行有效地选择随机非零列索引。我似乎无法弄清楚这样做的矢量化方式,所以我采用了非常慢的Python循环:

random_columns = np.zeros((sparse_matrix.shape[0]))
for i,row in enumerate(sparse_matrix):
    random_columns[i] = (np.random.choice(row.nonzero()[1]))

我的矩阵是一个近似(4000000,800)的csr_matrix,几乎每行只有一个非零值,因此Python循环会破坏性能。必须有更好的方法!

编辑我可以直接访问csr_matrix的基础数据,使其快2倍:

random_columns[i] = row.indices[np.random.choice(len(row.data))]

1 个答案:

答案 0 :(得分:1)

您是否查看了此基础数据表示以及其他稀疏格式?

例如,对于小矩阵

In [257]: M = sparse.rand(10,10,.1,format='csr')

In [258]: M
Out[258]: 
<10x10 sparse matrix of type '<class 'numpy.float64'>'
    with 10 stored elements in Compressed Sparse Row format>

In [259]: M.data
Out[259]: 
array([ 0.86390256,  0.85244302,  0.88549326,  0.78737361,  0.99918561,
        0.89862529,  0.86842524,  0.25714778,  0.4174032 ,  0.33137501])

In [260]: M.indices
Out[260]: array([1, 5, 8, 8, 9, 0, 3, 9, 4, 5], dtype=int32)

In [261]: M.indptr
Out[261]: array([ 0,  1,  1,  3,  4,  4,  5,  5,  7,  8, 10], dtype=int32)

对于csr,索引有点模糊。或者更确切地说,每个非零值的列索引都存在于M.indices中,但需要进行一些计算才能确定哪些属于哪一行。

对于其他格式,连接更明显。

对于lil,我们有2个列表列表

In [262]: Ml=M.tolil()

In [263]: Ml.data
Out[263]: 
array([[0.863902562935336], [], [0.8524430195076207, 0.8854932609233054],
       [0.7873736126927198], [], [0.9991856090158101], [],
       [0.8986252926235274, 0.8684252408594123], [0.2571477751356357],
       [0.4174032029993796, 0.3313750148434619]], dtype=object)

In [264]: Ml.rows
Out[264]: array([[1], [], [5, 8], [8], [], [9], [], [0, 3], [9], [4, 5]], dtype=object)

In [265]: [np.random.choice(x) for x in Ml.rows if x]
# some rows might not have any nonzero
Out[265]: [1, 5, 8, 9, 3, 9, 5]

In [268]: [np.random.choice(x.nonzero()[1]) for x in M if len(x.nonzero()[1])]
Out[268]: [1, 5, 8, 9, 0, 9, 4]

您也可以将nonzero用于整个矩阵

 In [274]: M.nonzero()
 Out[274]: 
 (array([0, 2, 2, 3, 5, 7, 7, 8, 9, 9], dtype=int32),
 array([1, 5, 8, 8, 9, 0, 3, 9, 4, 5], dtype=int32))

这些与M.tocoo()并查看rowcol属性的数组相同。从理论上讲,您可以使用groupby来获取列的子列表,并从中进行选择。但你又有了列表或生成器和迭代。

我不知道这些陈述中的任何一个是否更快。

对问题进行矢量化可能存在一些限制。非零数(choices的输入)将按行不同。有些行有非,有些行有1或更多。无论何时遇到不同长度的数组或列表,都很难对操作进行矢量化。如果您不能将值排列到常规的2d数组中,则无法通过数组操作对它们进行整体操作。

lil格式值得一看:

In [276]: timeit [np.random.choice(x.nonzero()[1]) for x in M if len(x.nonzero()[1])]
100 loops, best of 3: 4.24 ms per loop

In [289]: timeit [np.random.choice(row.indices) for row in M if len(row.indices)]
1000 loops, best of 3: 1.52 ms per loop
# 3x speedup using row.indices

In [277]: %%timeit
   .....: Ml=M.tolil()
   .....: [np.random.choice(x) for x in Ml.rows if x]
   .....: 
10000 loops, best of 3: 181 µs per loop