4

I am trying to turn a second order tensor into a binary third order tensor. Given a second order tensor as a m x n numpy array: A, I need to take each element value: x, in A and replace it with a vector: v, with dimensions equal to the maximum value of A, but with a value of 1 incremented at the index of v corresponding to the value x (i.e. v[x] = 1). I have been following this question: Increment given indices in a matrix, which addresses producing an array with increments at indices given by 2 dimensional coordinates. I have been reading the answers and trying to use np.ravel_multi_index() and np.bincount() to do the same but with 3 dimensional coordinates, however I keep on getting a ValueError: "invalid entry in coordinates array". This is what I have been using:

def expand_to_tensor_3(array):
    (x, y) = array.shape
    (a, b) = np.indices((x, y))
    a = a.reshape(x*y)
    b = b.reshape(x*y)
    tensor_3 = np.bincount(np.ravel_multi_index((a, b, array.reshape(x*y)), (x, y, np.amax(array))))
    return tensor_3

If you know what is wrong here or know an even better method to accomplish my goal, both would be really helpful, thanks.

1
  • Please show example input and desired output. Commented Oct 9, 2014 at 5:23

2 Answers 2

5

You can use (A[:,:,np.newaxis] == np.arange(A.max()+1)).astype(int).

Here's a demonstration:

In [52]: A
Out[52]: 
array([[2, 0, 0, 2],
       [3, 1, 2, 3],
       [3, 2, 1, 0]])

In [53]: B = (A[:,:,np.newaxis] == np.arange(A.max()+1)).astype(int)

In [54]: B
Out[54]: 
array([[[0, 0, 1, 0],
        [1, 0, 0, 0],
        [1, 0, 0, 0],
        [0, 0, 1, 0]],

       [[0, 0, 0, 1],
        [0, 1, 0, 0],
        [0, 0, 1, 0],
        [0, 0, 0, 1]],

       [[0, 0, 0, 1],
        [0, 0, 1, 0],
        [0, 1, 0, 0],
        [1, 0, 0, 0]]])

Check a few individual elements of A:

In [55]: A[0,0]
Out[55]: 2

In [56]: B[0,0,:]
Out[56]: array([0, 0, 1, 0])

In [57]: A[1,3]
Out[57]: 3

In [58]: B[1,3,:]
Out[58]: array([0, 0, 0, 1])

The expression A[:,:,np.newaxis] == np.arange(A.max()+1) uses broadcasting to compare each element of A to np.arange(A.max()+1). For a single value, this looks like:

In [63]: 3 == np.arange(A.max()+1)
Out[63]: array([False, False, False,  True], dtype=bool)

In [64]: (3 == np.arange(A.max()+1)).astype(int)
Out[64]: array([0, 0, 0, 1])

A[:,:,np.newaxis] is a three-dimensional view of A with shape (3,4,1). The extra dimension is added so that the comparison to np.arange(A.max()+1) will broadcast to each element, giving a result with shape (3, 4, A.max()+1).

With a trivial change, this will work for an n-dimensional array. Indexing a numpy array with the ellipsis ... means "all the other dimensions". So

(A[..., np.newaxis] == np.arange(A.max()+1)).astype(int)

converts an n-dimensional array to an (n+1)-dimensional array, where the last dimension is the binary indicator of the integer in A. Here's an example with a one-dimensional array:

In [6]: a = np.array([3, 4, 0, 1])

In [7]: (a[...,np.newaxis] == np.arange(a.max()+1)).astype(int)
Out[7]: 
array([[0, 0, 0, 1, 0],
       [0, 0, 0, 0, 1],
       [1, 0, 0, 0, 0],
       [0, 1, 0, 0, 0]])
Sign up to request clarification or add additional context in comments.

1 Comment

Thank you very much for your answer and explanation. Sometimes I get lost in some answers but you explained it very clearly, thank you again.
0

You can make it work this way:

tensor_3 = np.bincount(np.ravel_multi_index((a, b, array.reshape(x*y)),
                                            (x, y, np.amax(array) + 1)))

The difference is that I add 1 to the amax() result, because ravel_multi_index() expects that the indexes are all strictly less than the dimensions, not less-or-equal.

I'm not 100% sure if this is what you wanted; another way to make the code run is to specify mode='clip' or mode='wrap' in ravel_multi_index(), which does something a bit different and I'm guessing is less correct. But you can try it.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.