Swapping the dimensions of a numpy array

PythonArraysOptimizationNumpyDimensions

Python Problem Overview


I would like to do the following:

for i in dimension1:
  for j in dimension2:
    for k in dimension3:
      for l in dimension4:
        B[k,l,i,j] = A[i,j,k,l]

without the use of loops. In the end both A and B contain the same information but indexed differently.

I must point out that the dimension 1,2,3 and 4 can be the same or different. So a numpy.reshape() seems difficult.

Python Solutions


Solution 1 - Python

The canonical way of doing this in numpy would be to use np.transpose's optional permutation argument. In your case, to go from ijkl to klij, the permutation is (2, 3, 0, 1), e.g.:

In [16]: a = np.empty((2, 3, 4, 5))

In [17]: b = np.transpose(a, (2, 3, 0, 1))

In [18]: b.shape
Out[18]: (4, 5, 2, 3)

Solution 2 - Python

Please note: Jaime's answer is better. NumPy provides np.transpose precisely for this purpose.


Or use np.einsum; this is perhaps a perversion of its intended purpose, but the syntax is quite nice:

In [195]: A = np.random.random((2,4,3,5))

In [196]: B = np.einsum('klij->ijkl', A)

In [197]: A.shape
Out[197]: (2, 4, 3, 5)

In [198]: B.shape
Out[198]: (3, 5, 2, 4)

In [199]: import itertools as IT    
In [200]: all(B[k,l,i,j] == A[i,j,k,l] for i,j,k,l in IT.product(*map(range, A.shape)))
Out[200]: True

Solution 3 - Python

You could rollaxis twice:

>>> A = np.random.random((2,4,3,5))
>>> B = np.rollaxis(np.rollaxis(A, 2), 3, 1)
>>> A.shape
(2, 4, 3, 5)
>>> B.shape
(3, 5, 2, 4)
>>> from itertools import product
>>> all(B[k,l,i,j] == A[i,j,k,l] for i,j,k,l in product(*map(range, A.shape)))
True

or maybe swapaxes twice is easier to follow:

>>> A = np.random.random((2,4,3,5))
>>> C = A.swapaxes(0, 2).swapaxes(1,3)
>>> C.shape
(3, 5, 2, 4)
>>> all(C[k,l,i,j] == A[i,j,k,l] for i,j,k,l in product(*map(range, A.shape)))
True

Solution 4 - Python

I would look at numpy.ndarray.shape and itertools.product:

import numpy, itertools
A = numpy.ones((10,10,10,10))
B = numpy.zeros((10,10,10,10))

for i, j, k, l in itertools.product(*map(xrange, A.shape)):
    B[k,l,i,j] = A[i,j,k,l]

By "without the use of loops" I'm assuming you mean "without the use of nested loops", of course. Unless there's some numpy built-in that does this, I think this is your best bet.

Solution 5 - Python

One can also leverage numpy.moveaxis() for moving the required axes to desired locations. Here is an illustration, stealing the example from Jaime's answer:

In [160]: a = np.empty((2, 3, 4, 5))

# move the axes that are originally at positions [0, 1] to [2, 3]
In [161]: np.moveaxis(a, [0, 1], [2, 3]).shape 
Out[161]: (4, 5, 2, 3)

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionsponceView Question on Stackoverflow
Solution 1 - PythonJaimeView Answer on Stackoverflow
Solution 2 - PythonunutbuView Answer on Stackoverflow
Solution 3 - PythonDSMView Answer on Stackoverflow
Solution 4 - PythonmetapertureView Answer on Stackoverflow
Solution 5 - Pythonkmario23View Answer on Stackoverflow