如何在MATLAB中将函数应用于矩阵的每一行/每一列?


106

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

Answers:


73

许多内置操作,例如sumprod已经可以跨行或跨列进行操作,因此您可以重构要利用的功能。

如果这不是可行的选择,那么一种方法是使用mat2cell或将行或列收集到单元格中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

17
我将针对简单的for循环针对任何特殊情况测试这种方法的性能,这可能比将矩阵转换为单元数组要快。使用tic / tac包装纸进行测试。
yuk

5
@yuk:我想你的意思是“ tic / toc”。;)
gnovice

4
@gnovice,也许yuk做了一些魔术,并分配了tak = toc。用true = false一种有效的语言来说,我敢肯定有一种方法可以做到((
–chessofnerd 2014年

1
@Argyll:确定哪种方法更有效将取决于您要应用哪种功能,矩阵的大小等。总之,这可能取决于问题。实际上,有时好的旧for循环可能是最快的选择。
gnovice 2014年

2
@gnovice,建议对进行修改sum(M, 1)。初学者可能认为sum可以将这种方法用于任意大小的矩阵,然后在一天的矩阵结束时陷入困境1-by-n
Stewie Griffin

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]

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


21

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

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)

这里给出一个更通用的答案。
Wok

11

基于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))'


4

从r2016b开始,此问题的答案不断演变,从本质上讲,MATLAB将隐式扩展单例尺寸,从而bsxfun在许多情况下无需这样做。

r2016b发行说明中

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

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

Element-wise arithmetic operators+, -, .*, .^, ./, .\

Relational operators<, <=, >, >=, ==, ~=

Logical operators&, |, xor

Bit-wise functionsbitand, bitor, bitxor

Elementary math functionsmax, min, mod, rem, hypot, atan2, atan2d

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

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


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

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

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各种内置函数,也可以声明自己的函数。该文档列出了许多内置函数,但是基本上您可以命名任何接受两个数组(向量或矩阵)作为参数的函数,并使它起作用。


-1

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

我只想补充一下,Matlab的SUM函数实际上支持对给定维度(即具有两个维度的标准矩阵)求和。

因此,要计算列总和:

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

对于行总和,只需执行

rowsum = sum(M, 2)

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

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


7
该问题的原始答案的第一句话提到了沿给定维度应用SUM的能力。然后,答案就继续解决该功能尚未内置选择维度的情况。没错,但是,使用内置的维度选择选项(如果可用)几乎总是比for循环或转换为单元更快。
cjh 2012年

没错,但是,上面的答案使我回到了matlab文档中,因为我不需要花哨的精力,所以我只想共享和保存需要简单解决方案的其他人,使其免于搜索。
2012年

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

2
对于任何看到此答案的人:这不是这样做的方法!这不是在MATLAB中执行任何操作的方式!
Stewie Griffin
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.