【问题标题】:How can I apply a function to every row/column of a matrix in MATLAB?如何将函数应用于 MATLAB 中矩阵的每一行/列?
【发布时间】:2011-01-19 10:02:58
【问题描述】:

您可以通过说例如v + 1 将函数应用于向量中的每个项目,或者您可以使用函数arrayfun。如何在不使用 for 循环的情况下对矩阵的每一行/列执行此操作?

【问题讨论】:

    标签: matlab function matrix vectorization


    【解决方案1】:

    sumprod 等许多内置操作已经能够跨行或跨列操作,因此您可以重构您正在应用的函数以利用这一点。

    如果这不是一个可行的选择,一种方法是使用mat2cellnum2cell 将行或列收集到单元格中,然后使用cellfun 对生成的单元格数组进行操作。

    例如,假设您想对矩阵M 的列求和。你可以简单地使用sum

    M = magic(10);           %# A 10-by-10 matrix
    columnSums = sum(M, 1);  %# A 1-by-10 vector of sums for each column
    

    下面是使用更复杂的num2cell/cellfun 选项的方法:

    M = magic(10);                  %# A 10-by-10 matrix
    C = num2cell(M, 1);             %# Collect the columns into cells
    columnSums = cellfun(@sum, C);  %# A 1-by-10 vector of sums for each cell
    

    【讨论】:

    • 我会针对任何特定情况针对简单的 for 循环测试这种方法的性能,这可能比将矩阵转换为单元数组更快。用 tic/toc 包裹起来进行测试。
    • cellfunnum2cell 的效率如何?
    • @Argyll:确定哪种方法更有效将取决于您想要应用的函数类型、矩阵的大小等。简而言之,它可能取决于问题。事实上,有时一个好的旧 for 循环可能是最快的选择。
    • @yuk, @Argyll :在 MATLAB R2017b 上 for 似乎稍快一些(我得到 cellfun 计时:0.223 +/- 0.014;计时:0.157 +/- 0.005);供参考,用于测试的晦涩单行:n = 1e5; m = rand(n, 10); func = @sum; rep = 32; for k=rep:-1:1, tic; x = cellfun(func, num2cell(m,2)); et(k) = toc; end; fprintf("cellfun timing: %.3f +/- %.3f\n", mean(et), std(et)); for k=rep:-1:1, tic; x = nan(1,n); for i=1:n, x(i) = func(m(i,:)); end; et(k) = toc; end; fprintf(" for timing: %.3f +/- %.3f\n", mean(et), std(et))
    【解决方案2】:

    您可能想要更模糊的 Matlab 函数 bsxfun。在 Matlab 文档中,bsxfun “将函数句柄 fun 指定的逐元素二元运算应用于数组 A 和 B,并启用单例扩展。”

    @gnovice 上面提到 sum 和其他基本函数已经在第一个非单一维度上运行(即,如果多于一行,则为行,如果只有一行,则为列,或者如果较低维度都有大小,则为更高维度==1)。但是,bsxfun 适用于任何函数,包括(尤其是)用户定义的函数。

    例如,假设您有一个矩阵 A 和一个行向量 B。例如,假设:

    A = [1 2 3;
         4 5 6;
         7 8 9]
    B = [0 1 2]
    

    您需要一个函数 power_by_col,它在向量 C 中返回 A 中所有元素的 B 对应列的幂。

    从上面的例子中,C 是一个 3x3 矩阵:

    C = [1^0 2^1 3^2;
         4^0 5^1 6^2;
         7^0 8^1 9^2]
    

    即,

    C = [1 2 9;
         1 5 36;
         1 8 81]
    

    您可以使用 repmat 以蛮力方式做到这一点:

    C = A.^repmat(B, size(A, 1), 1)
    

    或者您可以使用 bsxfun 以经典的方式执行此操作,它在内部负责 repmat 步骤:

    C = bsxfun(@(x,y) x.^y, A, B)
    

    因此 bsxfun 为您节省了一些步骤(您无需显式计算 A 的尺寸)。然而,在我的一些非正式测试中,如果要应用的函数(如上面的幂函数)很简单,repmat 的速度大约会快两倍。因此,您需要选择是想要简单还是快速。

    【讨论】:

      【解决方案3】:

      我无法评论这有多有效,但这里有一个解决方案:

      applyToGivenRow = @(func, matrix) @(row) func(matrix(row, :))
      applyToRows = @(func, matrix) arrayfun(applyToGivenRow(func, matrix), 1:size(matrix,1))'
      
      % Example
      myMx = [1 2 3; 4 5 6; 7 8 9];
      myFunc = @sum;
      
      applyToRows(myFunc, myMx)
      

      【讨论】:

      • 给出了更通用的答案here
      【解决方案4】:

      Alex's answer 的基础上,这里有一个更通用的函数:

      applyToGivenRow = @(func, matrix) @(row) func(matrix(row, :));
      newApplyToRows = @(func, matrix) arrayfun(applyToGivenRow(func, matrix), 1:size(matrix,1), 'UniformOutput', false)';
      takeAll = @(x) reshape([x{:}], size(x{1},2), size(x,1))';
      genericApplyToRows = @(func, matrix) takeAll(newApplyToRows(func, matrix));
      

      这是两个函数的比较:

      >> % Example
      myMx = [1 2 3; 4 5 6; 7 8 9];
      myFunc = @(x) [mean(x), std(x), sum(x), length(x)];
      >> genericApplyToRows(myFunc, myMx)
      
      ans =
      
           2     1     6     3
           5     1    15     3
           8     1    24     3
      
      >> applyToRows(myFunc, myMx)
      ??? Error using ==> arrayfun
      Non-scalar in Uniform output, at index 1, output 1.
      Set 'UniformOutput' to false.
      
      Error in ==> @(func,matrix)arrayfun(applyToGivenRow(func,matrix),1:size(matrix,1))'
      

      【讨论】:

        【解决方案5】:

        为了完整性/兴趣,我想补充一点,matlab 确实有一个功能,允许您对每行而不是每元素的数据进行操作。它被称为rowfun (http://www.mathworks.se/help/matlab/ref/rowfun.html),但唯一的“问题”是它在tables (http://www.mathworks.se/help/matlab/ref/table.html) 而非矩阵 上运行。

        【讨论】:

          【解决方案6】:

          除了这个问题的答案不断发展之外,从 r2016b 开始,MATLAB 将隐式扩展单例维度,在许多情况下不再需要 bsxfun

          来自r2016b release notes

          隐式扩展:将元素操作和函数应用于数组,自动扩展长度为 1 的维度

          隐式扩展是标量扩展的推广。和 标量扩展,一个标量扩展为与另一个相同的大小 数组以促进元素操作。通过隐式扩展, 此处列出的逐元素运算符和函数可以隐式地 将它们的输入扩展为相同的大小,只要数组有 兼容的尺寸。两个数组具有兼容的大小,如果,对于每个 维度,输入的维度大小要么相同,要么 其中之一是 1。请参阅 Compatible Array Sizes for Basic Operations 和 数组与矩阵运算了解更多信息。

          Element-wise arithmetic operators — +, -, .*, .^, ./, .\
          
          Relational operators — <, <=, >, >=, ==, ~=
          
          Logical operators — &, |, xor
          
          Bit-wise functions — bitand, bitor, bitxor
          
          Elementary math functions — max, min, mod, rem, hypot, atan2, atan2d
          

          例如,您可以计算矩阵 A 中每一列的均值, 然后用 A 从每列中减去平均值向量 - 平均值(A)。

          以前,此功能可通过 bsxfun 函数使用。 现在建议您将 bsxfun 的大多数用法替换为直接 调用支持隐式扩展的函数和运算符。 与使用 bsxfun 相比,隐式扩展提供更快的速度, 更好的内存使用,并提高代码的可读性。

          【讨论】:

            【解决方案7】:

            以上答案对我来说都不是“开箱即用”的,但是,通过复制其他答案的想法获得的以下功能有效:

            apply_func_2_cols = @(f,M) cell2mat(cellfun(f,num2cell(M,1), 'UniformOutput',0));
            

            它接受一个函数f 并将其应用于矩阵M 的每一列。

            例如:

            f = @(v) [0 1;1 0]*v + [0 0.1]';
            apply_func_2_cols(f,[0 0 1 1;0 1 0 1])
            
             ans =
            
               0.00000   1.00000   0.00000   1.00000
               0.10000   0.10000   1.10000   1.10000
            

            【讨论】:

              【解决方案8】:

              使用最新版本的 Matlab,您可以使用 Table 数据结构来发挥自己的优势。甚至还有一个“rowfun”操作,但我发现这样做更容易:

              a = magic(6);
              incrementRow = cell2mat(cellfun(@(x) x+1,table2cell(table(a)),'UniformOutput',0))
              

              或者这是我拥有的一个不需要表格的旧版本,适用于较旧的 Matlab 版本。

              dataBinner = cell2mat(arrayfun(@(x) Binner(a(x,:),2)',1:size(a,1),'UniformOutput',0)')
              

              【讨论】:

                【解决方案9】:

                公认的答案似乎是先转换为单元格,然后使用cellfun 对所有单元格进行操作。我不知道具体的应用,但总的来说我认为使用bsxfun 对矩阵进行操作会更有效。基本上bsxfun 在两个数组中逐个元素地应用操作。因此,如果您想将 n x 1 向量中的每个项目乘以 m x 1 向量中的每个项目以获得 n x m 数组,您可以使用:

                vec1 = [ stuff ];    % n x 1 vector
                vec2 = [ stuff ];    % m x 1 vector
                result = bsxfun('times', vec1.', vec2);
                

                这将为您提供名为 result 的矩阵,其中 (i, j) 条目将是 vec1 的第 i 个元素乘以 vec2 的第 j 个元素。

                您可以将bsxfun 用于各种内置函数,也可以声明自己的函数。该文档列出了许多内置函数,但基本上您可以命名任何接受两个数组(向量或矩阵)作为参数的函数并使其工作。

                【讨论】:

                  【解决方案10】:

                  我喜欢splitapply,它允许使用splitapply(fun,A,1:size(A,2)) 将函数应用于A 的列。

                  例如

                  A = magic(5);
                  B = splitapply(@(x) x+1, A, 1:size(A,2));
                  C = splitapply(@std,  A, 1:size(A,2));
                  

                  要将函数应用于行,您可以使用 splitapply(fun, A', 1:size(A,1))';

                  (我的这个解决方案的来源是here。)

                  【讨论】:

                    【解决方案11】:

                    在寻找如何计算矩阵的行和时偶然发现了这个问题/答案。

                    我想补充一点,Matlab 的 SUM 函数实际上支持对给定维度求和,即具有二维的标准矩阵。

                    所以要计算列总和:

                    colsum = sum(M) % or sum(M, 1)
                    

                    对于行总和,只需执行

                    rowsum = sum(M, 2)
                    

                    我敢打赌,这比编写 for 循环和转换为单元格要快 :)

                    所有这些都可以在 SUM 的 matlab 帮助中找到。

                    【讨论】:

                    • 在该问题的原始答案的第一句话中提到了沿给定维度应用 SUM 的能力。然后,答案继续解决了选择维度的能力尚未内置到函数中的情况。不过,您是对的,使用内置的维度选择选项(当它们可用时)几乎总是比 for 循环或转换为单元格更快。
                    • 没错,但是,上面的答案让我回到了matlab文档,因为我不需要所有的花哨,所以我只是想分享和保存其他人,需要简单的解决方案, 从搜索开始。
                    【解决方案12】:

                    如果你知道你的行的长度,你可以做这样的事情:

                    a=rand(9,3);
                    b=rand(9,3); 
                    arrayfun(@(x1,x2,y1,y2,z1,z2) line([x1,x2],[y1,y2],[z1,z2]) , a(:,1),b(:,1),a(:,2),b(:,2),a(:,3),b(:,3) )
                    

                    【讨论】:

                    • 致任何看到此答案的人:这不是这样做的方法!这不是在 MATLAB 中做任何事情的方法!
                    猜你喜欢
                    • 2013-02-23
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    相关资源
                    最近更新 更多