如何将元胞数组展开为列向量?
How to unroll a cell array into a column vector?
我有一个元胞数组,其中每个元胞都是一个不同大小的矩阵。我想将所有矩阵的每个元素连接成一个列向量。
所以
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:感谢您的回答。我学到了一些关于 perf 的新东西。我对@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 几乎与最快的解决方案相同,而且即使 cellfun2 几乎与 cellfun2 相同,它也需要 2 倍的时间。
一个解决方案:尝试 X = {[X1(:); X2(:)]}
,然后使用 cell2mat
两个解决方案:
clear
X1 = rand(2,3);
X2 = rand(3,4);
X3 = rand(4,5);
X = {X1, X2, X3};
XX = cellfun(@(x) x(:)', X, 'UniformOutput', false);
cell2mat(XX)
你可以使用cellfun
to flatten all matrices using an anonymous function. Then, feed the modified cell array as "column vector" to cell2mat
,即预先转置修改后的单元格。
这是一个例子,其中所有内容都变成一行(使用 MATLAB Online 测试):
X1 = rand(2, 3);
X2 = rand(3, 4);
X3 = rand(1, 5);
X = {X1, X2, X3}
unrolled_X = cell2mat(cellfun(@(x) x(:), X, 'UniformOutput', false).')
一些示例输出(使用 Octave 6.1.0 生成):
X =
{
[1,1] =
0.2781 0.3303 0.7424
0.3314 0.4878 0.6254
[1,2] =
0.567344 0.848374 0.035421 0.171656
0.359233 0.482265 0.327617 0.188834
0.088272 0.771683 0.763845 0.181979
[1,3] =
0.9843 0.7817 0.9399 0.5453 0.3310
}
unrolled_X =
0.278085
0.331438
0.330314
0.487774
0.742395
0.625360
0.567344
0.359233
0.088272
0.848374
0.482265
0.771683
0.035421
0.327617
0.763845
0.171656
0.188834
0.181979
0.984326
0.781678
0.939857
0.545296
0.331043
事实上,将 cellfun
与匿名函数一起使用有点像伪装的循环,但它应该比你的循环更有效,因为你正在追加到一个数组。
预分配你的循环将提高性能和更好的实践
unrolled_X = cell(length(X),1);
for ii = 1:length(X)
unrolled_X{ii} = X{ii}(:);
end
unrolled_X = vertcat( unrolled_X{:} );
任何像 cellfun
这样的 shorthand 基本上都是变相的循环,而 cell2mat
在后台使用循环进行连接,但有额外的检查,因此实际上可能会导致轻微的缓慢 -下来。
如果你使用 Octave,你可以使用 cellindexmat
:
unrolled_X = vertcat(cellindexmat(X, ':'){:});
我有一个元胞数组,其中每个元胞都是一个不同大小的矩阵。我想将所有矩阵的每个元素连接成一个列向量。 所以
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:感谢您的回答。我学到了一些关于 perf 的新东西。我对@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 几乎与最快的解决方案相同,而且即使 cellfun2 几乎与 cellfun2 相同,它也需要 2 倍的时间。
一个解决方案:尝试 X = {[X1(:); X2(:)]}
,然后使用 cell2mat
两个解决方案:
clear
X1 = rand(2,3);
X2 = rand(3,4);
X3 = rand(4,5);
X = {X1, X2, X3};
XX = cellfun(@(x) x(:)', X, 'UniformOutput', false);
cell2mat(XX)
你可以使用cellfun
to flatten all matrices using an anonymous function. Then, feed the modified cell array as "column vector" to cell2mat
,即预先转置修改后的单元格。
这是一个例子,其中所有内容都变成一行(使用 MATLAB Online 测试):
X1 = rand(2, 3);
X2 = rand(3, 4);
X3 = rand(1, 5);
X = {X1, X2, X3}
unrolled_X = cell2mat(cellfun(@(x) x(:), X, 'UniformOutput', false).')
一些示例输出(使用 Octave 6.1.0 生成):
X =
{
[1,1] =
0.2781 0.3303 0.7424
0.3314 0.4878 0.6254
[1,2] =
0.567344 0.848374 0.035421 0.171656
0.359233 0.482265 0.327617 0.188834
0.088272 0.771683 0.763845 0.181979
[1,3] =
0.9843 0.7817 0.9399 0.5453 0.3310
}
unrolled_X =
0.278085
0.331438
0.330314
0.487774
0.742395
0.625360
0.567344
0.359233
0.088272
0.848374
0.482265
0.771683
0.035421
0.327617
0.763845
0.171656
0.188834
0.181979
0.984326
0.781678
0.939857
0.545296
0.331043
事实上,将 cellfun
与匿名函数一起使用有点像伪装的循环,但它应该比你的循环更有效,因为你正在追加到一个数组。
预分配你的循环将提高性能和更好的实践
unrolled_X = cell(length(X),1);
for ii = 1:length(X)
unrolled_X{ii} = X{ii}(:);
end
unrolled_X = vertcat( unrolled_X{:} );
任何像 cellfun
这样的 shorthand 基本上都是变相的循环,而 cell2mat
在后台使用循环进行连接,但有额外的检查,因此实际上可能会导致轻微的缓慢 -下来。
如果你使用 Octave,你可以使用 cellindexmat
:
unrolled_X = vertcat(cellindexmat(X, ':'){:});