元胞数组的广播求和 - MATLAB
Broadcasted summing on cell arrays - MATLAB
假设我有一个矩阵 C1,C2
如下:
C1 = nx2 cell
每个单元格是 [5x5 double]
.
C2 = 1x1 cell
包含一个 [5x5 double]
.
如何计算C3
为:
C3{1,1} = C1{1,1}+C2{1,1};
C3{1,2} = C1{1,2}+C2{1,1};
.
.
C3{n,2} = C1{n,2}+C2{1,1};
使用cellfun
或任何其他不循环的方法
对于那些具有常规大小数组的情况,您应该最好使用多维数组而不是慢速元胞数组。不过,坚持使用元胞数组,这是一个矢量化解决方案,在将它们转换为多维数组后,使用 bsxfun
作为矢量化广播求和的基础 -
%// Store size of each cell and size of C1 for later reshaping purposes
[m,n] = size(C1{1,1})
[nr,nc] = size(C1);
%// Perform broadcasted summations with BSXFUN
sums = bsxfun(@plus,cat(3,C1{:}),C2{1,1})
%// Reshape and convert back to cell arrays as desired output
out = reshape(mat2cell(sums,m,n,ones(1,nr*nc)),nr,nc)
cellfun
的直接使用完全符合您的要求:
C3 = cellfun(@(x) x+C2{1,1},C1,'uniformoutput',false);
这将实质上遍历元胞数组 C1
的每个元素,并为每个元素应用匿名函数 @(x) x+C2{1,1}
,即每个元素都将添加到 C2{1,1}
。结果元素以与 C1
.
大小相同的元胞数组形式返回
对于一些较小的测试用例,我将此解决方案与 进行了比较。结果并非微不足道,因为 cellfun
和 cat
(在 Divakar 的解决方案中使用)都非常慢。我投入了第三个版本来检查,其中我在我的匿名函数中定义了一个临时变量:
tmpvar=C2{1,1};
C3a2=cellfun(@(x) x+tmpvar,C1,'uniformoutput',false);
这背后的基本原理是访问单个单元格元素应该有一些开销,我不确定是否将整个单元格拉入匿名函数的工作区。
我定义了一个单独的函数来测试这三种情况,以便让 JIT 完成它的工作(但请注意,我使用的是 R2012b,较新的版本可能会给出完全不同的结果,the new execution engine and everything)。我 运行 所有三种情况都在同一个 运行dom 输入上(具有 C2
大小为 [1,1]
的元胞数组并包含与 C1{k,l}
大小相同的数组),从 10 次尝试中选择最短的运行时间(基于 tic/toc
)。
5x5
10x10
矩阵的元胞数组:
cellfun
: 0.000452 s
cellfun
+tmpvar
: 0.000287 s
bsxfun(cat)
: 0.002970 s
5x5
100x100
矩阵的元胞数组:
cellfun
: 0.000988 s
cellfun
+tmpvar
: 0.000963 s
bsxfun(cat)
: 0.004661 s
10x10
5x5
矩阵的元胞数组:
cellfun
: 0.001580 s
cellfun
+tmpvar
: 0.000945 s
bsxfun(cat)
: 0.011358 s
100x100
5x5
矩阵的元胞数组:
cellfun
: 0.108276 s
cellfun
+tmpvar
: 0.082675 s
bsxfun(cat)
: 1.132417 s
基于这些小测试用例,我很想得出结论
- 在给定的匿名函数中使用临时变量
cellfun
确实可以计算较大的单元格数组是否很大,这是有道理的(从那时起函数被评估的次数更多)。
- 基于
bsxfun(cat)
的解决方案对于小型元胞阵列来说有点慢,而对于较大的元胞阵列来说要慢得多。我怀疑 cat
是罪魁祸首: cat
需要很多时间才能将额外的维度放在一起。我什至可以想象使用带预分配的循环可能会优于 cat
.
- 对新的执行引擎以及更大的 (
n>1000
) 矩阵和元胞数组进行同样的检查会很有趣。
假设我有一个矩阵 C1,C2
如下:
C1 = nx2 cell
每个单元格是 [5x5 double]
.
C2 = 1x1 cell
包含一个 [5x5 double]
.
如何计算C3
为:
C3{1,1} = C1{1,1}+C2{1,1};
C3{1,2} = C1{1,2}+C2{1,1};
.
.
C3{n,2} = C1{n,2}+C2{1,1};
使用cellfun
或任何其他不循环的方法
对于那些具有常规大小数组的情况,您应该最好使用多维数组而不是慢速元胞数组。不过,坚持使用元胞数组,这是一个矢量化解决方案,在将它们转换为多维数组后,使用 bsxfun
作为矢量化广播求和的基础 -
%// Store size of each cell and size of C1 for later reshaping purposes
[m,n] = size(C1{1,1})
[nr,nc] = size(C1);
%// Perform broadcasted summations with BSXFUN
sums = bsxfun(@plus,cat(3,C1{:}),C2{1,1})
%// Reshape and convert back to cell arrays as desired output
out = reshape(mat2cell(sums,m,n,ones(1,nr*nc)),nr,nc)
cellfun
的直接使用完全符合您的要求:
C3 = cellfun(@(x) x+C2{1,1},C1,'uniformoutput',false);
这将实质上遍历元胞数组 C1
的每个元素,并为每个元素应用匿名函数 @(x) x+C2{1,1}
,即每个元素都将添加到 C2{1,1}
。结果元素以与 C1
.
对于一些较小的测试用例,我将此解决方案与 cellfun
和 cat
(在 Divakar 的解决方案中使用)都非常慢。我投入了第三个版本来检查,其中我在我的匿名函数中定义了一个临时变量:
tmpvar=C2{1,1};
C3a2=cellfun(@(x) x+tmpvar,C1,'uniformoutput',false);
这背后的基本原理是访问单个单元格元素应该有一些开销,我不确定是否将整个单元格拉入匿名函数的工作区。
我定义了一个单独的函数来测试这三种情况,以便让 JIT 完成它的工作(但请注意,我使用的是 R2012b,较新的版本可能会给出完全不同的结果,the new execution engine and everything)。我 运行 所有三种情况都在同一个 运行dom 输入上(具有 C2
大小为 [1,1]
的元胞数组并包含与 C1{k,l}
大小相同的数组),从 10 次尝试中选择最短的运行时间(基于 tic/toc
)。
5x5
10x10
矩阵的元胞数组:cellfun
:0.000452 s
cellfun
+tmpvar
:0.000287 s
bsxfun(cat)
:0.002970 s
5x5
100x100
矩阵的元胞数组:cellfun
:0.000988 s
cellfun
+tmpvar
:0.000963 s
bsxfun(cat)
:0.004661 s
10x10
5x5
矩阵的元胞数组:cellfun
:0.001580 s
cellfun
+tmpvar
:0.000945 s
bsxfun(cat)
:0.011358 s
100x100
5x5
矩阵的元胞数组:cellfun
:0.108276 s
cellfun
+tmpvar
:0.082675 s
bsxfun(cat)
:1.132417 s
基于这些小测试用例,我很想得出结论
- 在给定的匿名函数中使用临时变量
cellfun
确实可以计算较大的单元格数组是否很大,这是有道理的(从那时起函数被评估的次数更多)。 - 基于
bsxfun(cat)
的解决方案对于小型元胞阵列来说有点慢,而对于较大的元胞阵列来说要慢得多。我怀疑cat
是罪魁祸首:cat
需要很多时间才能将额外的维度放在一起。我什至可以想象使用带预分配的循环可能会优于cat
. - 对新的执行引擎以及更大的 (
n>1000
) 矩阵和元胞数组进行同样的检查会很有趣。