Search code examples
matlabmatrixmatrix-indexing

Shortest command to extract a submatrix using a vector containing the indexes of the 2 corners [matlab]


Let's say we have the following matrix

A=magic(4)

16     2     3    13
 5    11    10     8
 9     7     6    12
 4    14    15     1

and we want to extract 3 submatrices, identified by the indexes for top left and bottom right corners. The indexes for a submatrix are contained in a row of the matrix i; columns 1 and 2 of i are the row indexes of the corners, columns 3 and 4 of i are the column indexes of the corners.

i.e.

i =

 1     1     1     3
 2     4     1     2
 3     4     3     4

>> A(i(1,1):i(1,2),i(1,3):i(1,4))

ans =

16     2     3

>> A(i(2,1):i(2,2),i(2,3):i(2,4))

ans =

 5    11
 9     7
 4    14

>> A(i(3,1):i(3,2),i(3,3):i(3,4))

ans =

     6    12
    15     1

The command A(i(,):i(,),i(,):i(,)) which I used to extract the submatrices is not very convenient, so I wonder is there a better way to do the job ?


Solution

  • If you don't want to type it all out then why not write a wrapper function?

    A = magic(4);
    S = @(r) A(i(r,1):i(r,2),i(r,3):i(r,4));
    S(1)
    S(2)
    S(3)
    

    If A may change after the definition of S then you would need to make it a parameter to the function.

    S = @(A,r) A(i(r,1):i(r,2),i(r,3):i(r,4));
    A = magic(4)
    S(A,1)
    S(A,2)
    S(A,3)
    

    Similarly if i may change then you would need to make it a parameter as well.

    Edit Unfortunately, contrary to my comment, if you want to perform assignment then A(I(r)) won't work exactly the same as what you've posted since this always returns an array instead of a matrix. One possible workaround is to use cell arrays in place of comma-separated-lists, but this isn't as elegant as the read only option. For example

    S = @(r) {i(r,1):i(r,2) , i(r,3):i(r,4)};
    s = S(1); A(s{:})
    s = S(2); A(s{:})
    s = S(3); A(s{:})
    

    Following the same principle you could pre-define a cell array from i to make access one line.

    s = arrayfun(@(r) {i(r,1):i(r,2),i(r,3):i(r,4)}, 1:size(i,1), 'UniformOutput', false);
    A(s{1}{:})
    A(s{2}{:})
    A(s{3}{:})