istern
istern

Reputation: 393

Get top-n items of every row in a scipy sparse matrix

After reading this similar question, I still can't fully understand how to go about implementing the solution im looking for. I have a sparse matrix, i.e.:

 import numpy as np
 from scipy import sparse
 arr = np.array([[0,5,3,0,2],[6,0,4,9,0],[0,0,0,6,8]])
 arr_csc = sparse.csc_matrix(arr)

I would like to efficiently get the top n items of each row, without converting the sparse matrix to dense. The end result should look like this (assuming n=2):

 top_n_arr = np.array([[0,5,3,0,0],[6,0,0,9,0],[0,0,0,6,8]])
 top_n_arr_csc = sparse.csc_matrix(top_n_arr)

Upvotes: 2

Views: 2949

Answers (1)

hpaulj
hpaulj

Reputation: 231325

What is wrong with the linked answer? Does it not work in your case? or you just don't understand it? Or it isn't efficient enough?

I was going to suggest working out a means of finding the top values for a row of an lil format matrix, and apply that row by row. But I would just be repeating my earlier answer.


OK, my previous answer was a start, but lacked some details on iterating through the lol format. Here's a start; it probably could be cleaned up.

Make the array, and a lil version:

In [42]: arr = np.array([[0,5,3,0,2],[6,0,4,9,0],[0,0,0,6,8]])    
In [43]: arr_sp=sparse.csc_matrix(arr)
In [44]: arr_ll=arr_sp.tolil()

The row function from the previous answer:

def max_n(row_data, row_indices, n):
        i = row_data.argsort()[-n:]
        # i = row_data.argpartition(-n)[-n:]
        top_values = row_data[i]
        top_indices = row_indices[i]  # do the sparse indices matter?
        return top_values, top_indices, i

Iterate over the rows of arr_ll, apply this function and replace the elements:

In [46]: for i in range(arr_ll.shape[0]):
    d,r=max_n(np.array(arr_ll.data[i]),np.array(arr_ll.rows[i]),2)[:2]
    arr_ll.data[i]=d.tolist()
    arr_ll.rows[i]=r.tolist()
   ....:     

In [47]: arr_ll.data
Out[47]: array([[3, 5], [6, 9], [6, 8]], dtype=object)

In [48]: arr_ll.rows
Out[48]: array([[2, 1], [0, 3], [3, 4]], dtype=object)

In [49]: arr_ll.tocsc().A
Out[49]: 
array([[0, 5, 3, 0, 0],
       [6, 0, 0, 9, 0],
       [0, 0, 0, 6, 8]])

In the lil format, the data is stored in 2 object type arrays, as sublists, one with the data numbers, the other with the column indices.

Viewing the data attributes of sparse matrix is handy when doing new things. Changing those attributes has some risk, since it mess up the whole array. But it looks like the lil format can be tweaked like this safely.

The csr format is better for accessing rows than csc. It's data is stored in 3 arrays, data, indices and indptr. The lil format effectively splits 2 of those arrays into sublists based on information in the indptr. csr is great for math (multiplication, addition etc), but not so good when changing the sparsity (turning nonzero values into zeros).

Upvotes: 5

Related Questions