你可以通过例如 v + 1
的方式对向量中的每个元素应用一个函数,或者使用函数 arrayfun
。那么在不使用 for 循环的情况下,如何对矩阵的每一行/列执行相同的操作呢?
你可以通过例如 v + 1
的方式对向量中的每个元素应用一个函数,或者使用函数 arrayfun
。那么在不使用 for 循环的情况下,如何对矩阵的每一行/列执行相同的操作呢?
sum
和prod
已经能够跨行或列执行,所以您可以重构应用于该函数的函数以利用此功能。如果这不是可行的选项,则一种方法是使用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
您可能需要更加冷门的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大约会快两倍。因此,你需要选择是想要简单还是速度。
我不能评价这个方案的效率,但是这里有一个解决方案:
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)
function out = colfunc(func, data); out = arrayfun( @(x) func(data(:,x)) , 1:size(data,2) ); end
和 function out = rowfunc(func, data); out = colfunc(func, data.').'; end
。 - undefined在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))'
rowfun
(http://www.mathworks.se/help/matlab/ref/rowfun.html),但唯一的“问题”是它只能操作表格(http://www.mathworks.se/help/matlab/ref/table.html)而不是矩阵。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.
以上所述的答案均未能对我起到直接作用,但是,下面这个函数,通过借鉴其他答案的思路得到,可以解决问题:
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
最近版本的Matlab中,您可以利用Table数据结构来提高效率。甚至有一个'rowfun'操作,但我发现只需按照以下步骤更容易:
a = magic(6);
incrementRow = cell2mat(cellfun(@(x) x+1,table2cell(table(a)),'UniformOutput',0))
dataBinner = cell2mat(arrayfun(@(x) Binner(a(x,:),2)',1:size(a,1),'UniformOutput',0)')
我喜欢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))';
(我找到这个解决方案的来源是这里。)
目前被接受的答案似乎是先转换为单元格,然后使用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
进行各种内置函数,并且可以声明自己的函数。文档中列出了许多内置函数,但基本上您可以命名任何接受两个数组(向量或矩阵)作为参数的函数并使其正常工作。
cellfun
和num2cell
的效率如何? - Argyllfor
循环似乎稍微更快(我得到的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