问题
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?
回答1:
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.
回答2:
You can use cellfun
[~, I] = sort( cellfun( @(x) x(1), A ) );
Asorted = A(I);
来源:https://stackoverflow.com/questions/14021238/how-can-i-sort-the-elements-of-a-cell