Susensio
Susensio

Reputation: 860

numpy 3d tensor by 2d array

I have a sparse matrix. I know that each column has two nonzero values, so I want to compress (remove zeros) using a tensor that is defined as a list of permutation matrices.

I have

src = np.array([[2, 9, 0, 2, 4],
                [0, 1, 8, 8, 0],
                [1, 0, 3, 0, 0],
                [0, 0, 0, 0, 7]])

and I want

trg = np.array([[2, 9, 8, 2, 4],
                [1, 1, 3, 8, 7]])

which is the same matrix, but without zeros.

I have hardcoded the tensor that selects the nonzero values

p = np.array([
    [[1,0,0,0],[0,0,1,0]],
    [[1,0,0,0],[0,1,0,0]],
    [[0,1,0,0],[0,0,1,0]],
    [[1,0,0,0],[0,1,0,0]],
    [[1,0,0,0],[0,0,0,1]]
])

and I can iterate over both p and src to kinda get trg

>>> for i in range(len(p)):
>>>    print(p[i] @ src[:,i])

[2 1]
[9 1]
[8 3]
[2 8]
[4 7]

How can I do this the numpy way (i.e. without loops)? I have tried tensordot and transposing my matrices with no luck.

Upvotes: 1

Views: 1114

Answers (4)

cheersmate
cheersmate

Reputation: 2656

A solution using np.where:

src[np.where(src.T)[::-1]].reshape(2, -1, order='F')

This is what happens:

  • np.where gives the indices of nonzero elements, using the transpose so they are sorted correctly without further measures,
  • invert order with [::-1] because due to the transpose, row and column indices are swapped,
  • apply advanced indexing to obtain the elements,
  • and finally, reshape.

Output:

array([[2, 9, 8, 2, 4],
       [1, 1, 3, 8, 7]])

Upvotes: 2

javidcf
javidcf

Reputation: 59701

Here's one way:

import numpy as np

src = np.array([[2, 9, 0, 2, 4],
                [0, 1, 8, 8, 0],
                [1, 0, 3, 0, 0],
                [0, 0, 0, 0, 7]])
# Masked indices of non-zero positions
idx = np.arange(len(src))[:, np.newaxis] * (src != 0)
# Sort to and pick valid indices at the end
idx = np.sort(idx, axis=0)[-2:]
# Get values
trg = src[idx, np.arange(src.shape[1])]
print(trg)

Output:

[[2 9 8 2 4]
 [1 1 3 8 7]]

Upvotes: 1

Divakar
Divakar

Reputation: 221534

Owing to the row-major ordering, we can use the transposed version to index the array with its non-zeros mask and then reshape -

out = src.T[src.T!=0].reshape(src.shape[1],-1).T

Sample run -

In [19]: src
Out[19]: 
array([[2, 9, 0, 2, 4],
       [0, 1, 8, 8, 0],
       [1, 0, 3, 0, 0],
       [0, 0, 0, 0, 7]])

In [20]: src.T[src.T!=0].reshape(src.shape[1],-1).T
Out[20]: 
array([[2, 9, 8, 2, 4],
       [1, 1, 3, 8, 7]])

Upvotes: 2

Learning is a mess
Learning is a mess

Reputation: 8277

You can use a mask:

mask = src != 0
src[mask] #array without the zeroes but 1d
n_cols = src.shape[1]
tgt = src[mask].reshape(-1,n_cols)

This method requires reshaping the 1d array back to 2d, I decided to keep the same number of columns but for some case your array might be not "reshapable" to 2d.

Upvotes: 1

Related Questions