Convert c-order index into f-order index in Python

Viewed 315

I am trying to find a solution to the following problem. I have an index in C-order and I need to convert it into F-order.

To explain simply my problem, here is an example:


Let's say we have a matrix x as:

x = np.arange(1,5).reshape(2,2)
print(x)

array([[1, 2],
       [3, 4]])

Then the flattened matrix in C order is:

flat_c = x.ravel()
print(flat_c)

array([1, 2, 3, 4])

Now, the value 3 is at the 2nd position of the flat_c vector i.e. flat_c[2] is 3.

If I would flatten the matrix x using the F-order, I would have:

flat_f = x.ravel(order='f')

array([1, 3, 2, 4])

Now, the value 3 is at the 1st position of the flat_f vector i.e. flat_f[1] is 3.

I am trying to find a way to get the F-order index knowing the dimension of the matrix and the corresponding index in C-order.

I tried using np.unravel_index but this function returns the matrix positions...

3 Answers

We can use a combination of np.ravel_multi_index and np.unravel_index for a ndarray supported solution. Hence, given array shape s of input array a and c-order index c_idx, it would be -

s = a.shape
f_idx = np.ravel_multi_index(np.unravel_index(c_idx,s)[::-1],s[::-1])

So, the idea is pretty simple. Use np.unravel_index to get c-based indices in n-dim, then get flattened-linear index in fortran order by using np.ravel_multi_index on flipped shape and those flipped n-dim indices to simulate fortran behavior.

Sample runs on 2D -

In [321]: a
Out[321]: 
array([[ 0,  1,  2,  3,  4],
       [ 5,  6,  7,  8,  9],
       [10, 11, 12, 13, 14]])

In [322]: s = a.shape

In [323]: c_idx = 6

In [324]: np.ravel_multi_index(np.unravel_index(c_idx,s)[::-1],s[::-1])
Out[324]: 4

In [325]: c_idx = 12

In [326]: np.ravel_multi_index(np.unravel_index(c_idx,s)[::-1],s[::-1])
Out[326]: 8

Sample run on 3D array -

In [336]: a
Out[336]: 
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]]])

In [337]: s = a.shape

In [338]: c_idx = 21

In [339]: np.ravel_multi_index(np.unravel_index(c_idx,s)[::-1],s[::-1])
Out[339]: 9

In [340]: a.ravel('F')[9]
Out[340]: 21

Suppose your matrix is of shape (nrow,ncol). Then the 1D index when unraveled in C style for the (irow,icol) entry is given by

idxc = ncol*irow + icol

In the above equation, you know idxc. Then,

icol = idxc % ncol

Now you can find irow

irow = (idxc - icol) / ncol

Now you know both irow and icol. You can use them to get the F index. I think the F index will be given by

idxf = nrow*icol + irow

Please double-check my math, I might have got something wrong...

For the 3D case, if your array has dimensions [n1][n2][n3], then the unraveled C-index for [i1][i2][i3] is

idxc = n2*n3*i1 + n3*i2+i3

Using modulo operations similar to the 2D case, we can recover i1,i2,i3 and then convert to unraveled F index, i.e.

n3*i2 + i3 = idxc % (n2*n3)
i3 = (n3*i2+i3) % n3
i2 = ((n3*i2+i3) - i3) /n3
i1 = (idxc-(n3+i2+i3)) /(n2*n3)

F index would be:

idxf = i1 + n1*i2 +n1*n2*i3

Please check my math.

In simple cases you may also get away with transposing and ravelling the array:

import numpy as np

x = np.arange(2 * 2).reshape(2, 2)

print(x)
# [[0 1]
#  [2 3]]
print(x.ravel())
# [0 1 2 3]
print(x.transpose().ravel())
# [0 2 1 3]

x = np.arange(2 * 3 * 4).reshape(2, 3, 4)

print(x)
# [[[ 0  1  2  3]
#   [ 4  5  6  7]
#   [ 8  9 10 11]]

#  [[12 13 14 15]
#   [16 17 18 19]
#   [20 21 22 23]]]
print(x.ravel())
# [ 0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23]
print(x.transpose().ravel())
# [ 0 12  4 16  8 20  1 13  5 17  9 21  2 14  6 18 10 22  3 15  7 19 11 23]
Related