Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MATLAB ind2sub equivalent in Python

Matlab has two functions for converting matrix subscripts to linear indices and vice versa. (ind2sub and sub2ind)

I found the equivalent for R but is there an equivalent way in Python?

like image 445
DataTx Avatar asked Mar 11 '15 19:03

DataTx


4 Answers

A Google search lead me to this link: https://github.com/jjakeman/pyheat/blob/master/utilities/math_utils.py

From what I can tell, there is no direct implementation of those functions in MATLAB.


Just turns out I can't read the documentation properly. If you want the functionality of sub2ind, you'll want the ravel_multi_index function. The function declaration says that you require two inputs. The first input is a 2D numpy array where each row are the locations for a particular dimension. For example, if you wanted to apply ind2sub on a 2D matrix, you would specify a 2D numpy array where the first row consists of all of the row locations you want, and the second row consists of all of the column locations you want. The second input is tuple that determines the size of each dimension, so for a 2D array, it'd be the number of rows and columns.

To do ind2sub, you'd want the unravel_index function. The first input is an array of linear indices that you want converted into locations of each dimension in your array. The second input is a tuple of dimensions like previously.

I'm going to leave the post at the bottom for posterity, in case you want to try and implement these yourself.


However, you can certainly implement those yourself. I'm assuming that because you tagged your post with numpy that you'll want a numpy-esque solution. Remember, in numpy you access elements in row major, not column major, and so given two arrays such that one is for row and the other for column indices (0-indexed), sub2ind for 2D matrices is very simply:

def sub2ind(array_shape, rows, cols):
    return rows*array_shape[1] + cols

array_shape is an array of two elements where the first element is the number of rows in the matrix and the second element is the number of columns. If you recall, you can access an element in a row-major matrix by:

ind = r*cols + c

(r,c) are the row and column index you want, provided it's 0-indexed. To go the opposite way, you would use integer division and the modulus:

def ind2sub(array_shape, ind):
    rows = (ind.astype('int') / array_shape[1])
    cols = (ind.astype('int') % array_shape[1]) # or numpy.mod(ind.astype('int'), array_shape[1])
    return (rows, cols)

Here, the output is a two-element tuple where the first element is the row locations and the second element is the column locations. To summarize ind2sub, to access the row you want, you take the linear index and do an integer division with the columns. To get the column you want, you find the modulus or the remainder. Going to 3 dimensions and onwards is a bit more complicated. I'll leave you to take a look at the link I referred above for more details.

Obviously, I didn't place any error checking in the above functions, so you'd obviously use array_shape to your advantage in that case. A better way of doing what you want would be something like:

def sub2ind(array_shape, rows, cols):
    ind = rows*array_shape[1] + cols
    ind[ind < 0] = -1
    ind[ind >= array_shape[0]*array_shape[1]] = -1
    return ind

def ind2sub(array_shape, ind):
    ind[ind < 0] = -1
    ind[ind >= array_shape[0]*array_shape[1]] = -1
    rows = (ind.astype('int') / array_shape[1])
    cols = ind % array_shape[1]
    return (rows, cols)

I did some basic error checking to ensure that no rows or columns for sub2ind or linear indices for ind2sub are out of bounds. I set those locations to -1 so you know you messed up somewhere.

Good luck!

like image 180
rayryeng Avatar answered Oct 27 '22 09:10

rayryeng


I think you want ravel_multi_index and unravel_index

like image 20
TheBlackCat Avatar answered Oct 27 '22 09:10

TheBlackCat


Building on @rayryeng and @theblackcat's answers, you should also note that you will have to use Fortran style indexing, and recall that Python is 0 indexed whilst MATLAB is 1 indexed.

The Fortran style requirement tricked me up for a bit.

In Python:

np.unravel_index(7, [1, 2, 3, 4], 'F')
(0, 1, 0, 1)

and in MATLAB/Octave

[a, b, c, d] = ind2sub([1, 2, 3, 4], 8)
a =  1
b =  2
c =  1
d =  2
like image 6
Gilly Avatar answered Oct 27 '22 08:10

Gilly


For 2D matrices, you can use Python's built-in divmod function (returns the quotient and remainder of division). To get subscripts (r,c) of a nrows x ncols matrix, use:

(r, c) = divmod(ind, ncols) for row-major ordering

(c, r) = divmod(ind, nrows) for column-major ordering


For example, a 3x6 matrix in row-major (C style) ordering:

  0     1     2     3     4     5
(0,0) (0,1) (0,2) (0,3) (0,4) (0,5)

  6     7     8     9    10    11
(1,0) (1,1) (1,2) (1,3) (1,4) (1,5)

 12    13    14    15    16    17
(2,0) (2,1) (2,2) (2,3) (2,4) (2,5)

(0, 0) = divmod(0, 6)
(0, 3) = divmod(3, 6)
(1, 0) = divmod(6, 6)
(1, 5) = divmod(11, 6)
(2, 2) = divmod(14, 6)

A 3x6 matrix in column-major (Fortran style) ordering:

  0     3     6     9    12    15
(0,0) (0,1) (0,2) (0,3) (0,4) (0,5)

  1     4     7    10    13    16
(1,0) (1,1) (1,2) (1,3) (1,4) (1,5)

  2     5     8    11    14    17
(2,0) (2,1) (2,2) (2,3) (2,4) (2,5)

(0, 0) = divmod(0, 3)        #r=0, c=0
(1, 0) = divmod(3, 3)        #r=0, c=1
(2, 0) = divmod(6, 3)        #r=0, c=2
(3, 2) = divmod(11, 3)       #r=2, c=3
(4, 2) = divmod(14, 3)       #r=2, c=4
like image 1
Nirmal Avatar answered Oct 27 '22 09:10

Nirmal