5
votes

Let's said you have a 1D matrix

a = rand(1,5);
[sa i] = sort(a);

then sa and a(i) are the same. However, if the size of the matrix increases

a = rand(3,4);
[sa i] = sort(a);

then sa and a(i) are not the same. And the same happens when I'm trying to sort a 3D matrix by its third dimension.

How can I access the values of a through the index i? Or in other words how can I compute the sa=a(X), what X should be?

Edit:

Thanks for the solutions. However, they don't work when you change the dimension to sort by. Nevertheless, I pick up the idea and use it to build a general form.

What the algorithm is doing is to build the indexes of the matrix. MATLAB index the cells column wise. Therefore, the index is given by

idx = r + (c-1)*ROWS + (p-1)*ROWS*COLS

where, idx is the index, r is the row position, c is the column position, and p is the page position.

Therefore, if we sort in the first dimension (normal sort(a)) the result index is the position in the columns; if we sort in the second dimension, the result index is the position in the rows; and if we sort in the third dimension, the result index is the page position. This being said, it only last to produce the rows and cols for the given case:

r = repmat((1:rows)',[1 cols pages]);
c = repmat(1:cols,[rows 1 pages]);

Sorting in the first dimension is explained in the solutions given. Then, lets sort in the second dimension (row wise) of a two dimensional array

a = rand(4,5);
[rows cols pages] = size(a);
R = repmat((1:rows)',[1 cols pages]);
[sa idx] = sort(a,2);
nIdx = R + (idx-1)*rows;
isequal(sa,a(nIdx))

Now, if we use the same idea for sorting in the third dimension (page wise), we need to do

a = rand(4,5,3);
[rows cols pages] = size(a);
R = repmat((1:rows)',[1 cols pages]);
C = repmat(1:cols,[rows 1 pages]);
[sa idx] = sort(a,3);
nIdx = R + (C-1)*rows + (idx-1)*rows*cols;
isequal(sa,a(nIdx))

And the same logic can be used to extend it to N dimensions. Thanks for your help, you en light the way. :)

4
The best answer for 2D and 3D is right here.Trefex

4 Answers

4
votes

[sa, i]=sort(a) returns the ordered indices for each column. You just need to get the correct linear indices for the matrix. So, for a 2D matrix,

A=rand(3,4);
[rows,cols]=size(A);
[B,index]=sort(A,1);
correctedIndex=index+repmat(0:cols-1,rows,1)*rows;

Now test it:

A =

    0.9572    0.1419    0.7922    0.0357
    0.4854    0.4218    0.9595    0.8491
    0.8003    0.9157    0.6557    0.9340

B =

    0.4854    0.1419    0.6557    0.0357
    0.8003    0.4218    0.7922    0.8491
    0.9572    0.9157    0.9595    0.9340

A(correctedIndex)

ans =

    0.4854    0.1419    0.6557    0.0357
    0.8003    0.4218    0.7922    0.8491
    0.9572    0.9157    0.9595    0.9340
2
votes

You can create a general vectorized solution that will work for any N-D matrix or sort dimension by using the functions IND2SUB and SUB2IND. Here, I've packaged this solution up into a new function sort_linear_index, which will behave just like the function SORT except that it will return linear indices so that B = A(IX) will always work no matter what size A is.

function [sortedA,sortIndex] = sort_linear_index(A,sortDim,sortOrder)
%#SORT_LINEAR_INDEX   Just like SORT, but returns linear indices

  sizeA = size(A);  %# Get the matrix size
  if nargin < 2
    sortDim = find(sizeA > 1,1);  %# Define sortDim, if necessary
  end
  if nargin < 3
    sortOrder = 'ascend';  %# Define sortOrder, if necessary
  end
  [sortedA,sortIndex] = sort(A,sortDim,sortOrder);  %# Sort the matrix
  [subIndex{1:numel(sizeA)}] = ...  %# Create a set of matrix subscripts
     ind2sub(sizeA,reshape(1:prod(sizeA),sizeA));
  subIndex{sortDim} = sortIndex;  %# Overwrite part of the subscripts with
                                  %#   the sort indices
  sortIndex = sub2ind(sizeA,subIndex{:});  %# Find the linear indices

end

And now we can test the function out:

>> A = rand(1,10);
>> [B,IX] = sort_linear_index(A);  %# Sort a row vector
>> isequal(B,A(IX))
ans =
     1
>> A = rand(3,4,3);
>> [B,IX] = sort_linear_index(A,1);  %# Sort a 3-by-4-by-3 matrix along
>> isequal(B,A(IX))                  %#   the first dimension
ans =
     1
>> [B,IX] = sort_linear_index(A,3);  %# Sort a 3-by-4-by-3 matrix along
>> isequal(B,A(IX))                  %#   the third dimension
ans =
     1
>> [B,IX] = sort_linear_index(A,2,'descend');  %# Sort a 3-by-4-by-3 matrix along
>> isequal(B,A(IX))                            %#   the second dimension
ans =                                          %#   in descending order
     1
1
votes
a = rand(3,5);
[sa i] = sort(a);
ii=bsxfun(@plus,i,0:size(a,1):numel(a)-size(a,1));
isequal(a(ii),sa)