【发布时间】:2021-01-18 05:37:18
【问题描述】:
我有一个单元格数组,其中每个单元格都是不同大小的矩阵。我想将所有矩阵的每个元素连接成一个列向量。 所以
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};
我可以通过循环来做到这一点,但好奇是否有更快的方法。我查看了 cell2mat 并重塑,但无法让他们这样做(尺寸不匹配错误)。网络搜索似乎没有帮助。
这是我使用 for 循环的解决方案:
unrolled_X=[];
for i=1:length(X)
unrolled_X = [unrolled_X; X{i}(:)];
end
编辑 2:感谢您的回答。我学到了一些关于性能的新东西。我对@HansHirse、@lucien-xhh 和@wolfie 的3 个解决方案进行了基准测试。有点意外的结果。请注意,我实际上正在运行 Octave(版本 5.2.0。)。
所以没有 cell2fun 的解决方案是最快的。其他 2 个解决方案都使用 cellfun,但惊人地接近最快,而另一个是最快的两倍。代码和结果如下。
代码
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
结果:
octave:161> run_benchmarks
unroll_with_cellfun: 1.240324
unroll_with_cellfun2: 0.606957 <-- Close to fastest.
unroll_with_vertcat: 0.597657 <-- FASTEST
惊讶地发现 cellfun2 与最快的解决方案几乎相同,而且 cellfun 需要 2 倍的时间,即使它与 cellfun2 几乎相同。
【问题讨论】:
-
我怀疑这两个 cellfun 答案可能会给出不同的结果,其中一个是交错的值。这将是放缓的原因。您是否仔细检查了结果?如果元胞数组中包含不同大小的数组,则交错值的解决方案将不起作用。
-
请注意,你在测试中的 X 是一个列数组,而在 Hans 和 Lucien 的答案中它是一个行数组。
-
另外,你在做测试之前“热身”了吗?对函数的第一次调用往往比后续调用慢很多。
-
我刚刚在 MATLAB R2017a 中运行了您的基准测试,“cellfun2”几乎比“cellfun”慢 2 倍,“vertcat”快 2 倍以上。在 MATLAB 中,循环得到了优化,因此对
cellfun的调用具有很大的开销。我猜添加的.'操作会增加时间? - 但我不明白为什么“cellfun”会比 Octave 中的“cellfun2”慢。这没有任何意义。 -
好的,我破解了 Octave(Linux 上的 v.5.2.0),并在那里运行了你的基准测试。我看到“cellfun”需要 2.28 秒,“cellfun2”需要 2.07,“vertcat”需要 2.04。 “cellfun”确实比较慢,可能是由单元阵列上的转置引起的?但这不是 2 倍,更像是 1.1 倍。另外,你必须有一台速度非常快的电脑......