如何将函数应用于 MATLAB 中矩阵的每一行/列?
Posted
技术标签:
【中文标题】如何将函数应用于 MATLAB 中矩阵的每一行/列?【英文标题】:How can I apply a function to every row/column of a matrix in MATLAB? 【发布时间】:2011-01-19 10:02:58 【问题描述】:您可以通过说例如v + 1
将函数应用于向量中的每个项目,或者您可以使用函数arrayfun
。如何在不使用 for 循环的情况下对矩阵的每一行/列执行此操作?
【问题讨论】:
【参考方案1】: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
【讨论】:
我会针对任何特定情况针对简单的 for 循环测试这种方法的性能,这可能比将矩阵转换为单元数组更快。用 tic/toc 包裹起来进行测试。cellfun
和 num2cell
的效率如何?
@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(x1,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 中做任何事情的方法!以上是关于如何将函数应用于 MATLAB 中矩阵的每一行/列?的主要内容,如果未能解决你的问题,请参考以下文章
如何将给定矩阵的每一行中的所有元素与给定向量的相应元素相乘并在 MATLAB 中求和?