I have a cell array, where each cell is a matrix of different size. I want to concatenate every elements of all the matrices into one column vector. So
X1=rand(2,3); % Total 6 elements.
X2=rand(3,4); % Total 12 elements.
X = {X1, X2}; % Total 18 elements in a 2-cell array.
% How to unroll everything from X into one giant column vector of size 18x1 ?
% Edit: The above example only shows two matrices, X1 and X2, but there could be n such matrices in the cell array.
X = {X1, X2, ... , Xn};
I can do this with a loop, but curious if there is a faster way. I looked into cell2mat and reshape, but can't get them to do this (dimension mismatch errors). Searches on web don't seem to help.
This is my solution with a for loop:
unrolled_X=[];
for i=1:length(X)
unrolled_X = [unrolled_X; X{i}(:)];
end
Edit 2: Thanks for the answers. I learned something new about perf. I benchmarked the 3 solutions by @HansHirse, @lucien-xhh, and @wolfie. A bit surprising results. Note I am actually running Octave (version 5.2.0.).
So the solution without cell2fun was fastest. Other 2 solutions both use cellfun, but was surprisingly close to the fastest, while the other was double of the fastest. Code and results follow.
Code
function run_benchmarks()
X={};
for i=1:5
X{i}=rand(1000,1000);
end
fprintf("unroll_with_cellfun: %f\n", benchmark(@()unroll_with_cellfun(X), 100));
fprintf("unroll_with_cellfun2: %f\n", benchmark(@()unroll_with_cellfun2(X), 100));
fprintf("unroll_with_vertcat: %f\n", benchmark(@()unroll_with_vertcat(X), 100));
end
function unrolled_X = unroll_with_cellfun(X)
unrolled_X = cell2mat(cellfun(@(x) x(:), X, 'UniformOutput', false).');
end
function unrolled_X = unroll_with_cellfun2(X)
unrolled_X = cell2mat(cellfun(@(x) x(:).', X, 'UniformOutput', false)).';
end
function unrolled_X = unroll_with_vertcat(X)
unrolled_X = cell(length(X),1);
for ii = 1:length(X)
unrolled_X{ii} = X{ii}(:);
end
unrolled_X = vertcat( unrolled_X{:} );
end
function elapsed_time_in_seconds = benchmark(f, N)
% benchmark runs the function 'f' N times and returns the elapsed time in seconds.
timeid = tic;
for i=1:N
output = f();
end
elapsed_time_in_seconds = toc(timeid);
end
Results:
octave:161> run_benchmarks
unroll_with_cellfun: 1.240324
unroll_with_cellfun2: 0.606957 <-- Close to fastest.
unroll_with_vertcat: 0.597657 <-- FASTEST
Surprised to see cellfun2 is almost same as the fastest solution, and that cellfun takes 2x time even it's almost same as cellfun2.
Preallocating your loop will be more performant and better practise
unrolled_X = cell(length(X),1);
for ii = 1:length(X)
unrolled_X{ii} = X{ii}(:);
end
unrolled_X = vertcat( unrolled_X{:} );
Any shorthand like cellfun
is basically this loop in disguise, and cell2mat
uses a loop for concatenation under the hood but has additional checks so may actually cause a minor slow-down.