Как отсортировать элементы ячейки?

У меня есть такая ячейка:

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.

Другими словами, количество столбцов в каждом элементе этой ячейки различно, без определенной закономерности.

Теперь я хочу отсортировать эти элементы на основе элемента в первом столбце. Я имею в виду, я хочу, чтобы результат был таким:

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;];

В настоящее время я использую эту функцию:

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 

Это работает, но занимает много времени. На самом деле часть цикла for очень медленная.
Я читал о функции cat, но не знаю, как ее использовать. Я использовал B = cat(1,A{:}(1,1));, но вот эта ошибка: ??? Bad cell reference operation.

Я хотел узнать, есть ли более быстрый способ сделать это?

Спасибо.

Обновить Давайте проведем эксперимент:

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

и результат:
sum1=2,53635923001387
sum2=0,629729057743372
sum3=4,54007401778717
sum4=0,571285037623497

** Что означает per-allocation быстрее, но что такое 4-й метод. Я подумал, что это заслуживает обсуждения в отдельном вопросе. см. Предварительное выделение Matlab по сравнению с отсутствием распределение, второе быстрее, почему?


person Ramin    schedule 24.12.2012    source источник
comment
ваш цикл очень медленный, так как вы не выделили предварительно пространство памяти для массива B. По-вашему, на каждой итерации Matlab должен перераспределять пространство памяти для B, которое на один элемент больше, чем B на предыдущей итерации. Это приводит к значительному замедлению работы Matlab. Предварительное выделение массивов является очень хорошей практикой для кодирования Matlab.   -  person Shai    schedule 24.12.2012


Ответы (2)


Ваш цикл работает медленно, потому что внутри него растет B; вам следует предварительно выделить память для B, и он должен работать значительно быстрее. Для этого вставьте следующую строку перед циклом for:

B = zeros(nrows, 1);

Вы можете дополнительно сократить цикл следующим образом:

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);

РЕДАКТИРОВАТЬ

Я решил сравнить это решение с более коротким решением, в котором используется cellfun (которое я ранее предлагал):

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

Результаты:

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

Цикл выполняется на порядок быстрее, чем cellfun! Эта разница может быть очень заметна для больших массивов, поэтому я рекомендую использовать в этой задаче цикл for.

person Eitan T    schedule 24.12.2012
comment
+1 Я давал тот же ответ, но только через 30 секунд после: S - person petrichor; 24.12.2012
comment
@EitanT это ответ на мой вопрос, но, пожалуйста, прочитайте часть обновления и посмотрите там связанный вопрос. - person Ramin; 24.12.2012
comment
@ Рон, я вижу, что ты уже опубликовал его как новый вопрос, и для тебя уже есть (правильный) ответ. В любом случае, когда вы вычисляете sum4, вы уже используете предварительно выделенный B из расчета sum3, поэтому он такой же быстрый. - person Eitan T; 24.12.2012
comment
@EitanT большое спасибо, я подумал, что это что-то странное :-) - person Ramin; 24.12.2012

Вы можете использовать cellfun

[~, I] = sort( cellfun( @(x) x(1), A ) );
Asorted = A(I);
person Shai    schedule 24.12.2012
comment
Я обнаружил, что это намного медленнее, чем цикл. - person Eitan T; 24.12.2012
comment
@EitanT - я считаю, что текущая реализация cellfun, arrayfun и некоторых других fun далека от идеала. Однако намерение Mathworks состоит в том, чтобы обеспечить лучшую (возможно, параллельную) реализацию этих функций. Итак, я думаю, что это хорошая практика - использовать эти fun в надежде, что в будущем это окупится. Но вы правы, если вы беспокоитесь о времени выполнения больше, чем о правильности своего кода — вперед и for-зацикливайте это решение! - person Shai; 24.12.2012
comment
Что ж, ОП заявил, что его основной проблемой была производительность, потому что N очень велико, поэтому я бы не стал подстрекать его использовать cellfun. Я сам предложил это решение, но затем отказался от него и заменил его на цикл for. - person Eitan T; 24.12.2012