【问题标题】:How do I apply a function with multiple parameters using `cellfun` (MATLAB)?如何使用 `cellfun` (MATLAB) 应用具有多个参数的函数?
【发布时间】:2015-04-28 21:37:28
【问题描述】:

使用cellfun,如何将函数应用于mat2cell 函数创建的所有单元格?我的函数是在另一个文件中定义的,这里用myFunc引用。这个函数有两个参数,应该是一个单元格和一个整数。

例如function H = myFunc(img,Q)

我的代码如下:

% Split into grid and process each cell
width = size(img,2) % get image width
height = size(img,1) % get image height
depth = size(img,3) % get depth
C = mat2cell(img,[height/2 height/2],[width/2 width/2],[depth/2 depth/2]); % divides image into sections
F = cellfun(@myFunc,C);
save(fout,'F');

问题当然在于F = cellfun(@myFunc,C);。如何传递单元格和选择的整数,例如每个单元格 4 到 myFunc

非常感谢。

【问题讨论】:

    标签: matlab image-processing matrix cell-array divide


    【解决方案1】:

    最近遇到了这个问题,注意到虽然 Octave 在 cellfun 中有隐式的单元格扩展参数,但 Matlab 没有。调用匿名函数比直接调用函数的开销更大(尽管在这方面 Matlab 不如 Octave 差),所以我发现将参数作为单元数组传递会更快一些,这里用一个简单的例子展示:

    abc = {magic(2), magic(3), magic(4)}
    abc =
      3×1 cell array
        {2×2 double}
        {3×3 double}
        {4×4 double}
        
    def = cellfun (@(x) sum(x,2), abc, "UniformOutput", false) %anonymous function method
    def =
      3×1 cell array
        {2×1 double}
        {3×1 double}
        {4×1 double}
    def{:}
    ans =
         4
         6
    ans =
        15
        15
        15
    ans =
        34
        34
        34
        34 
    

    将参数扩展为元胞数组并将其作为另一个输入传递会产生相同的正确输出:

    def = cellfun (@sum, abc, num2cell(2*ones(size(abc))), "UniformOutput", false) % cell expansion method
    def =
      3×1 cell array
        {2×1 double}
        {3×1 double}
        {4×1 double}
    def{:}
    ans =
         4
         6
    ans =
        15
        15
        15
    ans =
        34
        34
        34
        34 
        
    

    快速 tic/toc 检查表明这在 Matlab 2021a 中要快一些:

    tic; 
    for idx = 1:100000
      cellfun(@(x) sum(x,2), abc,"UniformOutput",false); 
    end
    toc
    Elapsed time is 4.017116 seconds.
    
    tic, 
    for idx = 1:100000
      cellfun(@sum, abc, num2cell(2*ones(size(abc))),"UniformOutput",false);
    end
    toc
    Elapsed time is 1.217712 seconds
    

    我没有尝试过使用非简单参数,但 repmat 可以为字符串输入做同样的事情,但 repmat 似乎增加了相当多的开销:

    tic
    for idx = 1:100000
        cellfun(@sum, abc, repmat({2}, size(abc)),"UniformOutput",false); 
    end
    toc
    Elapsed time is 4.367002 seconds.
    

    所以也许有更好的方法来扩展这些。请注意,这是一个非常简单的带有小数组的测试用例,并且随着事情的扩大或添加多个参数,这种时间节省可能不会成立。此外,您正在乘以执行此操作的每个参数的内存需求,因为每个参数都扩展为输入数组的大小。

    快速测试表明,使用自定义 myfunc 与使用 sum 等 matlab 函数可以节省相同的时间。

    【讨论】:

    • 在我的机器上(并且有一个稍大的abc 9 个小数组),第二个变体比第一个变体快约 3 倍,但一个简单的循环又快了约 4 倍。所以如果你真的关心速度,不要使用cellfuncellfun 用于更短的代码,而不是速度。我还发现简单的循环更容易阅读,所以我倾向于避免使用cellfun。我的解决方案:res = cell(size(abc)); for ii = 1:numel(abc), res{ii} = sum(abc{ii},2); end.
    • 感谢时间补充。老实说,我大部分时间都使用 Octave,但循环仍然很慢。 num2cell 版本只比匿名函数快 10-15%。 Octave 让您只需将 {2} 放入第二个单元格数组,它就会隐式扩展,这大约是匿名函数的 2 倍。你的循环比那里的匿名函数慢大约 10%。也许有一天他们会让 JIT 编译器工作。 :)
    • 有趣。在 MATLAB 中,cellfun 被实现为 M 文件(或者更确切地说,这是我最后一次检查,但那是几年前的事了),所以 cellfun 总是增加函数调用的开销(所有参数检查),以及通过句柄重复调用函数的开销,因此它不能比拼写循环更快。我猜在 Octave 中它不是作为 M 文件实现的。
    • ">> which cellfun" "'cellfun' 是来自文件 libinterp/corefcn/cellfun.cc" 的内置函数所以看来你是正确的
    【解决方案2】:

    通过使用匿名函数:

    F = cellfun(@(Q) myFunc(Q,4),C);
    

    【讨论】:

      【解决方案3】:

      好吧,只需将一个新的匿名函数定义为@(x) myFunc(x,4) 并以这种方式使用它:

      F = cellfun(@(x) myFunc(x,4), C)
      

      【讨论】:

      • 谢谢! :)
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-04-25
      • 2011-10-13
      • 1970-01-01
      • 2018-09-25
      • 2012-10-28
      • 1970-01-01
      相关资源
      最近更新 更多