【问题标题】:Mablab/Octave - use cellfun to index one matrix with anotherMablab/Octave - 使用 cellfun 将一个矩阵与另一个矩阵索引
【发布时间】:2012-12-29 03:38:09
【问题描述】:

我有一个包含随机数矩阵的单元格,比如a = {[300*20],....,[300*20]};。我有另一个相同格式的单元格,称为b,其中包含nan 术语在a 中的位置逻辑。

我想使用cellfun 循环遍历单元格,基本上让nan 等于0a(b)=0

谢谢, j

【问题讨论】:

    标签: matlab indexing cell octave matrix-indexing


    【解决方案1】:

    您可以定义一个将任何 NaN 替换为零的函数。

    function a = nan2zero(a)
      a(isnan(a)) = 0;
    

    然后您可以使用cellfun 将此函数应用于您的元胞数组。

    a0 = cellfun(@nan2zero, a, 'UniformOutput', 0)
    

    这样,您甚至不需要任何矩阵b

    【讨论】:

    • 假设b 中的矩阵已经存在,将它们用作函数的第二个输入会更有效。诚然,速度差异会很小,除非ab 真的 很大:-) 但是是的,+1
    • 甚至不确定。测试 NaN 应该比通过 b 快得多。
    • 传入b 应该更快,只要在函数内部没有分配给b。 Matlab 将处理 b "By Ref" 只要你不改变它。
    • 其实我想得越多,我就越不相信自己的论点。我可能会进行一些测试 :-) 如果我能接受,我会在这里发布结果。
    • 按引用参数听起来不错。我也在考虑测试。不过,我认为不存在让cellfun 处理两个单元阵列的方法,并且将ab 中的每个矩阵组合起来会更糟。让我知道你得到了什么。如果 MATLAB 将 a(isnan(a)) = 0 作为一个整体进行优化,我敢打赌,b 看起来会很伤心。
    【解决方案2】:

    首先,您可能应该给@s.bandara打勾,因为这是第一个正确答案,它使用了cellfun(根据您的要求)。不要给这个答案。这个答案的目的是提供一些额外的分析。

    我想我会研究解决这个问题的一些可能方法的效率。

    第一种方法是@s.bandara 提倡的方法。

    第二种方法与@s.bandara 提倡的方法类似,但它使用bnan 转换为0,而不是使用isnan。理论上,这种方法可能更快,因为函数内部没有分配任何东西给b,所以应该“按引用”处理。

    第三种方法使用循环来绕过使用cellfun,因为cellfun is often slower than an explicit loop

    快速测速的结果是:

    Elapsed time is 3.882972 seconds. %# First approach (a, isnan, and cellfun, eg @s.bandara)
    Elapsed time is 3.391190 seconds. %# Second approach (a, b, and cellfun)
    Elapsed time is 3.041992 seconds. %# Third approach (loop-based solution)
    

    换句话说,通过传递b 而不是使用isnan 可以节省(少量)费用。使用循环而不是cellfun 可以进一步节省(少量)。但我不会因此而失眠。请记住,任何模拟的结果都特定于指定的输入。

    注意,这些结果在多次运行中是一致的,我使用 tictoc 来执行此操作,尽管每种方法都有许多循环。如果我想真正彻底,我应该使用 FEX 的timeit。如果有人有兴趣,这三种方法的代码如下:

    %# Build some example matrices
    T = 1000; N = 100; Q = 50; M = 100;
    a = cell(1, Q); b = cell(1, Q);
    for q = 1:Q
        a{q} = randn(T, N);
        b{q} = logical(randi(2, T, N) - 1);
        a{q}(b{q}) = nan;
    end
    
    %# Solution using a, isnan, and cellfun (@s.bandara solution)
    tic
    for m = 1:M
        Soln2 = cellfun(@f1, a, 'UniformOutput', 0);
    end
    toc
    
    %# Solution using a, b, and cellfun
    tic
    for m = 1:M
        Soln1 = cellfun(@f2, a, b, 'UniformOutput', 0);
    end
    toc
    
    
    %# Solution using a loop to avoid cellfun
    tic
    for m = 1:M
        Soln3 = cell(1, Q);
        for q = 1:Q
            Soln3{q} = a{q};
            Soln3{q}(b{q}) = 0;
        end
    end
    toc
    
    %# Solution proposed by @EitanT
    [K, N] = size(a{1});
    tic
    for m = 1:M
        a0 = [a{:}];       %// Concatenate matrices along the 2nd dimension
        a0(isnan(a0)) = 0; %// Replace NaNs with zeroes    
        Soln4 = mat2cell(a0, K, N * ones(size(a)));
    end
    toc
    

    地点:

    function x1 = f1(x1)
    x1(isnan(x1)) = 0;
    

    和:

    function x1 = f2(x1, x2)
    x1(x2) = 0;
    

    更新:@EitanT 提出了第四种方法。这种方法将矩阵元胞数组连接成一个大矩阵,对大矩阵执行运算,然后可选择将其转换回元胞数组。我已将此过程的代码添加到上面的测试例程中。对于我的测试代码中指定的输入,即T = 1000N = 100Q = 50M = 100,定时运行如下:

    Elapsed time is 3.916690 seconds. %# @s.bandara
    Elapsed time is 3.362319 seconds. %# a, b, and cellfun
    Elapsed time is 2.906029 seconds. %# loop-based solution
    Elapsed time is 4.986837 seconds. %# @EitanT
    

    我对此感到有些惊讶,因为我认为@EitanT 的方法会产生最好的结果。在纸面上,这似乎非常明智。请注意,我们当然可以乱用输入参数来找到有利于不同解决方案的特定设置。例如,如果矩阵很小,但数量很大,那么@EitanT 的方法效果很好,例如T = 10N = 5Q = 500M = 100 产生:

    Elapsed time is 0.362377 seconds. %# @s.bandara
    Elapsed time is 0.299595 seconds. %# a, b, and cellfun
    Elapsed time is 0.352112 seconds. %# loop-based solution
    Elapsed time is 0.030150 seconds. %# @EitanT
    

    这里@EitanT 的方法占主导地位。

    对于 OP 指出的问题规模,我发现基于循环的解决方案通常具有最佳性能。但是,对于某些Q,例如Q = 5,@EitanT 的解决方案却成功地领先。

    【讨论】:

    • 看起来你的速度更快,伙计。 +1。
    • @EitanT 完成!我已经为我的答案添加了更新。但我不明白结果。如果你有机会,我很想知道你的机器上是否有类似的数字,或者你是否能发现我的代码有问题。结果对我来说根本没有任何意义:-) ps +1 为您的答案。在纸面上,这似乎非常明智。在纸上……
    • @ColinTBowers 是的,我运行了您的代码,得到的结果与您的相似。这就是我感到困惑的原因。
    • @EitanT 找到了!新鲜的眼睛总是有帮助的。我使用M 来控制每种方法的迭代次数。但是您的代码(我在没有仔细检查的情况下粘贴了该代码)使用M 来测量矩阵维度之一的长度。我已经修复它,重新运行模拟,并调整了我的答案。干杯!
    • 太棒了!不过我仍然很好奇,为什么在某些情况下它比循环慢。
    【解决方案3】:

    嗯。

    鉴于元胞数组内容的性质,可能存在一个更更快的解决方案:您可以将元胞数据转换为单个矩阵并使用向量索引替换所有NaN 值立即进入其中,无需cellfun 或循环:

    a0 = [a{:}];       %// Concatenate matrices along the 2nd dimension
    a0(isnan(a0)) = 0; %// Replace NaNs with zeroes
    

    如果你想将它转换回元胞数组,那很好:

    [M, N] = size(a{1});
    mat2cell(a0, M, N * ones(size(a)))
    

    附言
    如果可能,使用 3-D 矩阵而不是元胞数组。在 MATLAB 中,向量化运算通常要快得多。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-06-16
      • 2016-11-17
      • 1970-01-01
      • 2020-01-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-01-20
      相关资源
      最近更新 更多