110

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

4

12 回答 12

77

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

mat2cell如果这不是一个可行的选择,一种方法是使用or将行或列收集到单元格中num2cell,然后使用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
于 2010-02-21T20:02:49.263 回答
24

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

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

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

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]

IE,

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 的速度大约会快两倍。因此,您需要选择是想要简单还是速度。

于 2013-07-07T23:46:50.080 回答
23

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

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)
于 2012-10-30T09:15:40.003 回答
14

基于Alex 的回答,这里有一个更通用的函数:

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))'
于 2013-04-12T12:16:20.757 回答
6

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

于 2014-01-05T21:20:00.727 回答
4

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

r2016b 发行说明

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

隐式扩展是标量扩展的推广。通过标量扩展,标量扩展为与另一个数组相同的大小,以促进元素操作。通过隐式扩展,此处列出的逐元素运算符和函数可以隐式地将其输入扩展为相同的大小,只要数组具有兼容的大小即可。如果对于每个维度,输入的维度大小相同或其中一个为 1,则两个数组具有兼容的大小。有关更多信息,请参阅基本运算的兼容数组大小和数组与矩阵运算。

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 - mean(A) 从每一列中减去平均值向量。

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

于 2016-09-26T16:09:41.620 回答
2

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

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
于 2017-03-01T00:43:15.633 回答
1

公认的答案似乎是先转换为单元格,然后用于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) 条目将是 的第 i 个元素vec1乘以 的第 j 个元素vec2

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

于 2013-07-08T18:35:28.097 回答
1

使用最新版本的 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)')
于 2015-05-06T06:49:10.087 回答
0

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

例如

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。)

于 2021-10-27T20:27:36.300 回答
-1

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

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

因此,要计算列总和,请执行以下操作:

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

对于行总和,只需执行

rowsum = sum(M, 2)

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

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

于 2012-09-11T08:27:54.850 回答
-2

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

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) )
于 2013-08-29T05:09:10.490 回答