3

I have a cell array of size m x 1 and each cell is again s x t cell array (size varies). I would like to concatenate vertically. The code is as follows:

function(cell_out) = vert_cat(cell_in)
    [row,col] = cellfun(@size,cell_in,'Uni',0);
    fcn_vert = @(x)([x,repmat({''},size(x,1),max(cell2mat(col))-size(x,2))]);
    cell_out = cellfun(fcn_vert,cell_in,'Uni',0); % Taking up lot of time
    cell_out = vertcat(cell_out{:});
end

Step 3 takes a lot of time. Is it the right way to do or is there any another faster way to achieve this?

Cape Code
  • 3,584
  • 3
  • 24
  • 45
nik
  • 143
  • 4
  • 14
  • Have you tried `profile`ing the code? – Shai Feb 02 '15 at 20:51
  • Just a minor improvement: You don't need `'uni',0` for the first `cellfun` Output of size is uniform. – Daniel Feb 02 '15 at 21:00
  • @Daniel The size varies and is not uniform. Would you please check again? – nik Feb 02 '15 at 21:08
  • 1
    @nik: Could you provide an example input? With the data I generated `[row,col] = cellfun(@size,cell_in);` runs without an error. Maybe I got the description of your data wrong. – Daniel Feb 02 '15 at 21:11
  • @Daniel Let's say the cell size is 4 x 1 and each cell is of size 1 x 3, 1 x 2, 1 x 4, 1 x 1. Without '\'' 'Uni',0 '\'' I don't think it works – nik Feb 02 '15 at 21:21
  • @nik: [1,3] [1,2] [1,4] or [1,1] is all a 2 element vector, the output of size is uniform in this case. – Daniel Feb 02 '15 at 21:39
  • @Daniel It's the size [3 elements] [2 elements] [ 4 elements] [1 element]... – nik Feb 02 '15 at 21:53
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/70069/discussion-between-daniel-and-nik). – Daniel Feb 02 '15 at 21:55

2 Answers2

2

cellfun has been found to be slower than loops (kind of old, but agrees with what I have seen). In addition, repmat has also been a performance hit in the past (though that may be different now). Try this two-loop code that aims to accomplish your task:

function cellOut = vert_cat(c)

    nElem  = length(c);
    colPad = zeros(nElem,1);
    nRow   = zeros(nElem,1);
    for k = 1:nElem
        [nRow(k),colPad(k)] = size(c{k});
    end
    colMax = max(colPad);
    colPad = colMax - colPad;

    cellOut = cell(sum(nRow),colMax);
    bottom  = cumsum(nRow) - nRow + 1;
    top     = bottom + nRow - 1;
    for k = 1:nElem
        cellOut(bottom(k):top(k),:) = [c{k},cell(nRow(k),colPad(k))];
    end

end

My test for this code was

A = rand(20,20);
A = mat2cell(A,ones(20,1),ones(20,1));
C = arrayfun(@(c) A(1:c,1:c),randi([1,15],1,5),'UniformOutput',false);
ccat = vert_cat(c);
Community
  • 1
  • 1
TroyHaskin
  • 8,361
  • 3
  • 22
  • 22
1

I used this pice of code to generate data:

%generating some dummy data
m=1000;
s=100;
t=100;
cell_in=cell(m,1);
for idx=1:m
    cell_in{idx}=cell(randi(s),randi(t));
end

Applying some minor modifications, I was able to speed up the code by a factor of 5

%Minor modifications of the original code
    %use arrays instead of cells for row and col
    [row,col] = cellfun(@size,cell_in);
    %claculate max(col) once
    tcol=max(col);
    %use cell instead of repmat to generate an empty cell
    fcn_vert = @(x)([x,cell(size(x,1),tcol-size(x,2))]);
    cell_out = cellfun(fcn_vert,cell_in,'Uni',0); % Taking up lot of time
    cell_out = vertcat(cell_out{:});

Using simply a for loop is even faster, because the data is only moved once

%new approac. Basic idea: move every data only once
    [row,col] = cellfun(@size,cell_in);
    trow=sum(row);
    tcol=max(col);
    r=1;
    cell_out2 = cell(trow,tcol);
    for idx=1:numel(cell_in)
        cell_out2(r:r+row(idx)-1,1:col(idx))=cell_in{idx};
        r=r+row(idx);
    end
Daniel
  • 36,610
  • 3
  • 36
  • 69