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

113

你可以通过例如 v + 1 的方式对向量中的每个元素应用一个函数,或者使用函数 arrayfun。那么在不使用 for 循环的情况下,如何对矩阵的每一行/列执行相同的操作呢?

12个回答

77
许多内置的操作,例如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

20
我会对这种方法在任何特定情况下与简单的for循环进行性能测试,可能比将矩阵转换为单元数组更快。使用tic/toc包裹以进行测试。 - yuk
cellfunnum2cell的效率如何? - Argyll
2
@Argyll:确定哪种方法更有效取决于您想应用的函数类型、矩阵的大小等因素。简而言之,这很可能是问题相关的。事实上,有时候一个好的for循环可以是最快的选择。 - gnovice
1
@yuk,@Argyll:在MATLAB R2017b上,使用for循环似乎稍微更快(我得到的cellfun时间为0.223 +/- 0.014;for时间为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时间:%.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时间:%.3f +/- %.3f\n", mean(et), std(et)) - trybik

25

您可能需要更加冷门的Matlab函数bsxfun。从Matlab文档中可以看到,bsxfun会“对启用了单例扩展的数组A和B应用由函数句柄fun指定的逐元素二进制操作。”

@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]
< p >也就是说,

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


24

我不能评价这个方案的效率,但是这里有一个解决方案:

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
我喜欢将这个函数保留在我的路径上,如下所示:function out = colfunc(func, data); out = arrayfun( @(x) func(data(:,x)) , 1:size(data,2) ); endfunction out = rowfunc(func, data); out = colfunc(func, data.').'; end - undefined

15

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

6

5
自r2016b版本开始,MATLAB会隐式扩展单例维度,因此在许多情况下不再需要使用bsxfun函数,这进一步说明了这个问题的不断演变。从r2016b版本说明中可以看到。

Implicit Expansion: Apply element-wise operations and functions to arrays with automatic expansion of dimensions of length 1

Implicit expansion is a generalization of scalar expansion. With scalar expansion, a scalar expands to be the same size as another array to facilitate element-wise operations. With implicit expansion, the element-wise operators and functions listed here can implicitly expand their inputs to be the same size, as long as the arrays have compatible sizes. Two arrays have compatible sizes if, for every dimension, the dimension sizes of the inputs are either the same or one of them is 1. See Compatible Array Sizes for Basic Operations and Array vs. Matrix Operations for more information.

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

For example, you can calculate the mean of each column in a matrix A, and then subtract the vector of mean values from each column with A - mean(A).

Previously, this functionality was available via the bsxfun function. It is now recommended that you replace most uses of bsxfun with direct calls to the functions and operators that support implicit expansion. Compared to using bsxfun, implicit expansion offers faster speed, better memory usage, and improved readability of code.


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

我喜欢splitapply,它允许将函数应用于A的列,使用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))';

(我找到这个解决方案的来源是这里。)


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)条目将是vec1的第i个元素乘以vec2的第j个元素。
您可以使用bsxfun进行各种内置函数,并且可以声明自己的函数。文档中列出了许多内置函数,但基本上您可以命名任何接受两个数组(向量或矩阵)作为参数的函数并使其正常工作。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接