Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I sort the elements of a cell?

I have a cell like this:

A{1,1}=[ 1 ;2; 3;];
A{2,1}=[ 4 ;2;];
A{3,1}=[ 3 ;2; 5; 4; 6;];
...
A{N,1}=[ 10 ;2;5; 7;];   %N is very large.

In other words, the number of columns in each element of this cell is different, with no definite pattern.

Now, I want to sort these elements based on the element at the first column. I mean, I want the result to be like this:

Asorted{1,1}=[ 1 ;2; 3;];
Asorted{2,1}=[ 3 ;2; 5; 4; 6;];
Asorted{3,1}=[ 4 ;2;];
...
Asorted{N,1}=[ 10 ;2;5; 7;];

Currently I use this function:

function Asorted = sortcell(A)
B=[];
nrows = size(A,1);

for i=1:nrows            % this for-loop is slow
    st=A{i,1};
    B(i,1) = st(1,1);
end
[sorted,indices] = sort(B);
Asorted = A(indices,:);
end 

It works, but it takes much time. In fact the for loop part is very slow.
I read about cat function, but I don't know how to use it. I used B = cat(1,A{:}(1,1));, but there is this error: ??? Bad cell reference operation.

I wanted to know if there is a more faster way of doing this?

Thanks.

Update Lets do an experiment:

A={};
for i=1:1e3
    A{i,1} = ones(4,1) * randn;
end

N=1000;sum1=0;sum2=0;sum3=0;sum4=0;
for t=1:N

    % # Solution with for loop and no prealocation
    tic
    B = [];
    for i = 1:size(A, 1)
        B(i, 1) = A{i,1}(1,1);
    end
    [Y, I] = sort(B);
    Asorted = A(I,:);
    a=toc;sum1=sum1+a;

    % # Solution with for loop and Prealocation
    tic
    B = zeros(size(A,1), 1);
    for i = 1:size(A, 1)
        B(i, 1) = A{i,1}(1,1);
    end
    [Y, I] = sort(B);
    Asorted = A(I,:);
    a=toc;sum2=sum2+a;

    % # Solution with cellfun
    tic
    [Y, I] = sort( cellfun( @(x) x(1), A ) );
    Asorted = A(I);
    a=toc;sum3=sum3+a;
    tic

    % # Solution with for loop and ???
    for i = 1:size(A, 1)
        B(i, 1) = A{i}(1);
    end
    [Y, I] = sort(B);
    Asorted = A(I);
    a=toc;sum4=sum4+a;
end

and the result is
sum1=2.53635923001387
sum2=0.629729057743372
sum3=4.54007401778717
sum4=0.571285037623497

** Which means per-allocation is faster, but what is the 4-th method. I thought it deserves to be discussed in a separate question. see Matlab Pre-allocation vs. no allocation, the second one is faster, why?

like image 609
Ramin Avatar asked Dec 24 '12 12:12

Ramin


2 Answers

Your loop is slow because B is growing inside it; you should preallocate memory for B and it should run significantly faster. To do this, insert the following line before the for loop:

B = zeros(nrows, 1);

You can further shorten your loop like so:

B = zeros(size(A,1), 1);
for i = 1:size(A, 1)
    B(i, 1) = A{i}(1);
end
[Y, I] = sort(B);
Asorted = A(I);

EDIT

I decided to compare this solution with a shorter solution that employs cellfun (which I formerly proposed):

A = {[1; 2; 3], [4; 2], [3; 2; 5; 4; 6], [10; 2; 5; 7]};

% # Solution with a for loop
tic
for jj = 1:1e3
    B = zeros(size(A,1), 1);
    for i = 1:size(A, 1)
        B(i, 1) = A{i}(1);
    end
    [Y, I] = sort(B);
    Asorted = A(I);
end
toc

% # Solution with cellfun
tic
for jj = 1:1e3
    [Y, I] = sort( cellfun( @(x) x(1), A ) );
    Asorted = A(I);
end
toc

The results are:

Elapsed time is 0.028761 seconds.
Elapsed time is 0.253888 seconds.

The loop runs by an order of magnitude faster than cellfun! This difference can be extremely noticeable for large arrays, so I recommend using a for loop in this problem.

like image 160
Eitan T Avatar answered Sep 29 '22 09:09

Eitan T


You can use cellfun

[~, I] = sort( cellfun( @(x) x(1), A ) );
Asorted = A(I);
like image 41
Shai Avatar answered Sep 29 '22 09:09

Shai