0
votes

I am working with numpy arrays as rank > 2 tensors in Python and am trying to reshape such a tensor into a matrix, i.e. a rank-2 array. The standard ndarray.reshape() function doesn't really work for this because I need to group the indices of my tensor in a particular way. What I mean is this: say I start with a rank 3 tensor, T_ijk. I am trying to find a function that will output the rank 2 tensor T_(j)(ik), for instance, i.e. for this exampe the desired input/output would be

[Input:]      T=np.array([[[1 2]
                           [3 4]]
                          [[5 6]
                           [7 8]]])

[Output:]     array([[1, 2, 5, 6],
                     [3, 4, 7, 8]])

Also, a friend suggested to me that tensorflow might have functions like this, but I've never used it. Does anyone have any insight here?

2
Please provide sample input and output as per stackoverflow.com/help/minimal-reproducible-example - Akshay Sehgal
You'll need to transpose (swap) some axes as well as reshaoe to get the order you want. - hpaulj
Yes I see how transposing a couple times in addition to reshaping would work on a simple example like this, but if I were working with a much more complicated case, e.g. if I wanted to transform T_ijklmno to T_(ilo)(jmnk) having to figure out which axes to switch and how to reshape will probably get out of hand... that's why I'm looking for an in-built solution - user13134331

2 Answers

0
votes

Try this -

k = 1
m = 2
i = 5
j = 5
l = 2

#dummy T_ijklm
T = np.array(range(100)).reshape(k,m,i,j,l)
T_new = T.reshape(k*m,i*j*l)

print('Original T:',T.shape)
print('New T:',T_new.shape)

#(km)(ijl) = 2*50

Original T: (1, 2, 5, 5, 2)
New T: (2, 50)

New tensor is now a rank 2

array([[ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15,
        16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
        32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
        48, 49],
       [50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65,
        66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81,
        82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97,
        98, 99]])
0
votes
In [216]: arr = np.arange(1,9).reshape(2,2,2)                                                        
In [217]: arr                                                                                        
Out[217]: 
array([[[1, 2],
        [3, 4]],

       [[5, 6],
        [7, 8]]])

reshape keeps elements in the original [1,2,3,4,5...] order

In [218]: arr.reshape(2,4)                                                                           
Out[218]: 
array([[1, 2, 3, 4],
       [5, 6, 7, 8]])

Figuring out the correct transpose order can be tricky. Sometimes I just try several things. Here I note that you want to preserve the order on the last dimension, so all we have to do is swap the first 2 axes:

In [219]: arr.transpose(1,0,2)                                                                       
Out[219]: 
array([[[1, 2],
        [5, 6]],

       [[3, 4],
        [7, 8]]])

now the reshape does what we want:

In [220]: arr.transpose(1,0,2).reshape(2,4)                                                          
Out[220]: 
array([[1, 2, 5, 6],
       [3, 4, 7, 8]])

This sequence, as best I know, the best "built-in" approach.

You comment:

if I wanted to transform T_ijklmno to T_(ilo)(jmnk) having to figure out which axes to switch and how to reshape will probably get out of hand... that's why I'm looking for an in-built solution

The T_.... notation reminds me that we could use einsum to do the transpose:

In [221]: np.einsum('ijk->jik',arr)                                                                  
Out[221]: 
array([[[1, 2],
        [5, 6]],

       [[3, 4],
        [7, 8]]])

So T_ijklmno to T_(ilo)(jmnk) could become

np.einsum('ijklmno->ilojmnk',T).reshape(I*L*O, J*M*N*K)
T.transpose(0,3,6,1,4,5,2).reshape(...)

(I wrote these by just eyeballing your T expression)

There are so many ways you could transpose and reshape an array with 7 dimensions, that there's little point in coming up with anything more general than the existing methods - transpose, swapaxes, einsum. Simply identifying the dimensions as you do with 'ijk...' is the toughest part of the problem.