How can I apply a function to every row/column of a matrix in MATLAB?
Asked Answered
V

13

115

You can apply a function to every item in a vector by saying, for example, v + 1, or you can use the function arrayfun. How can I do it for every row/column of a matrix without using a for loop?

Volscian answered 21/2, 2010 at 19:58 Comment(0)
B
79

Many built-in operations like sum and prod are already able to operate across rows or columns, so you may be able to refactor the function you are applying to take advantage of this.

If that's not a viable option, one way to do it is to collect the rows or columns into cells using mat2cell or num2cell, then use cellfun to operate on the resulting cell array.

As an example, let's say you want to sum the columns of a matrix M. You can do this simply using sum:

M = magic(10);           %# A 10-by-10 matrix
columnSums = sum(M, 1);  %# A 1-by-10 vector of sums for each column

And here is how you would do this using the more complicated num2cell/cellfun option:

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
Butanol answered 21/2, 2010 at 20:2 Comment(4)
I would test performance of this approach for any particular case against simple for-loop, which might be faster then converting a matrix to cell array. Wrap with tic/toc to test.Sexcentenary
How (in)efficient is cellfun and num2cell?Lacrimatory
@Argyll: Determining which approach is more efficient will depend on what sort of function you were wanting to apply, the size of the matrix, etc. In short, it is likely problem-dependent. In fact, sometimes a good old for loop can be the fastest choice.Butanol
@yuk, @Lacrimatory : on MATLAB R2017b for seems slightly faster (I get cellfun timing: 0.223 +/- 0.014; and for timing: 0.157 +/- 0.005); for ref., the obscure one-liner used to test: 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))Spoilfive
V
25

You may want the more obscure Matlab function bsxfun. From the Matlab documentation, bsxfun "applies the element-by-element binary operation specified by the function handle fun to arrays A and B, with singleton expansion enabled."

@gnovice stated above that sum and other basic functions already operate on the first non-singleton dimension (i.e., rows if there's more than one row, columns if there's only one row, or higher dimensions if the lower dimensions all have size==1). However, bsxfun works for any function, including (and especially) user-defined functions.

For example, let's say you have a matrix A and a row vector B. E.g., let's say:

A = [1 2 3;
     4 5 6;
     7 8 9]
B = [0 1 2]

You want a function power_by_col which returns in a vector C all the elements in A to the power of the corresponding column of B.

From the above example, C is a 3x3 matrix:

C = [1^0 2^1 3^2;
     4^0 5^1 6^2;
     7^0 8^1 9^2]

i.e.,

C = [1 2 9;
     1 5 36;
     1 8 81]

You could do this the brute force way using repmat:

C = A.^repmat(B, size(A, 1), 1)

Or you could do this the classy way using bsxfun, which internally takes care of the repmat step:

C = bsxfun(@(x,y) x.^y, A, B)

So bsxfun saves you some steps (you don't need to explicitly calculate the dimensions of A). However, in some informal tests of mine, it turns out that repmat is roughly twice as fast if the function to be applied (like my power function, above) is simple. So you'll need to choose whether you want simplicity or speed.

Varien answered 7/7, 2013 at 23:46 Comment(0)
H
24

I can't comment on how efficient this is, but here's a solution:

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)
Horsemanship answered 30/10, 2012 at 9:15 Comment(2)
A more generic answer is given here.Pinnatipartite
I like keeping this on my path as function out = colfunc(func, data); out = arrayfun( @(x) func(data(:,x)) , 1:size(data,2) ); end and function out = rowfunc(func, data); out = colfunc(func, data.').'; end.Ensiform
P
15

Building on Alex's answer, here is a more generic function:

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

Here is a comparison between the two functions:

>> % 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))'
Pinnatipartite answered 12/4, 2013 at 12:16 Comment(0)
C
6

For completeness/interest I'd like to add that matlab does have a function that allows you to operate on data per-row rather than per-element. It is called rowfun (http://www.mathworks.se/help/matlab/ref/rowfun.html), but the only "problem" is that it operates on tables (http://www.mathworks.se/help/matlab/ref/table.html) rather than matrices.

Contaminate answered 5/1, 2014 at 21:20 Comment(0)
L
5

Adding to the evolving nature of the answer to this question, starting with r2016b, MATLAB will implicitly expand singleton dimensions, removing the need for bsxfun in many cases.

From the r2016b release notes:

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.

Lianna answered 26/9, 2016 at 16:9 Comment(0)
L
2

None of the above answers worked "out of the box" for me, however, the following function, obtained by copying the ideas of the other answers works:

apply_func_2_cols = @(f,M) cell2mat(cellfun(f,num2cell(M,1), 'UniformOutput',0));

It takes a function f and applies it to every column of the matrix M.

So for example:

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
Llanes answered 1/3, 2017 at 0:43 Comment(0)
R
1

The accepted answer seems to be to convert to cells first and then use cellfun to operate over all of the cells. I do not know the specific application, but in general I would think using bsxfun to operate over the matrix would be more efficient. Basically bsxfun applies an operation element-by-element across two arrays. So if you wanted to multiply each item in an n x 1 vector by each item in an m x 1 vector to get an n x m array, you could use:

vec1 = [ stuff ];    % n x 1 vector
vec2 = [ stuff ];    % m x 1 vector
result = bsxfun('times', vec1.', vec2);

This will give you matrix called result wherein the (i, j) entry will be the ith element of vec1 multiplied by the jth element of vec2.

You can use bsxfun for all sorts of built-in functions, and you can declare your own. The documentation has a list of many built-in functions, but basically you can name any function that accepts two arrays (vector or matrix) as arguments and get it to work.

Ryter answered 8/7, 2013 at 18:35 Comment(0)
K
1

With recent versions of Matlab, you can use the Table data structure to your advantage. There's even a 'rowfun' operation but I found it easier just to do this:

a = magic(6);
incrementRow = cell2mat(cellfun(@(x) x+1,table2cell(table(a)),'UniformOutput',0))

or here's an older one I had that doesn't require tables, for older Matlab versions.

dataBinner = cell2mat(arrayfun(@(x) Binner(a(x,:),2)',1:size(a,1),'UniformOutput',0)')
Kauslick answered 6/5, 2015 at 6:49 Comment(0)
K
1

I like splitapply, which allows a function to be applied to the columns of A using splitapply(fun,A,1:size(A,2)).

For example

A = magic(5);
B = splitapply(@(x) x+1, A, 1:size(A,2));
C = splitapply(@std,  A, 1:size(A,2));

To apply the function to the rows, you could use splitapply(fun, A', 1:size(A,1))';

(My source for this solution is here.)

Kimono answered 27/10, 2021 at 20:27 Comment(0)
D
0

You can still use arrayfun too.

% customed function
myfun=@(x) sum(x)

M=[1 2 3; 4 5 6]

% by column
arrayfun(@(c) myfun(M(:,c)),1:size(M,2))

% by row
arrayfun(@(r) myfun(M(r,:)),1:size(M,1))

But, splitapply is shorter when by column.

% by column
splitapply(myfun,M,1:size(M,2))
Dietetic answered 23/4 at 16:56 Comment(0)
P
-2

Stumbled upon this question/answer while seeking how to compute the row sums of a matrix.

I would just like to add that Matlab's SUM function actually has support for summing for a given dimension, i.e a standard matrix with two dimensions.

So to calculate the column sums do:

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

and for the row sums, simply do

rowsum = sum(M, 2)

My bet is that this is faster than both programming a for loop and converting to cells :)

All this can be found in the matlab help for SUM.

Pneumoencephalogram answered 11/9, 2012 at 8:27 Comment(2)
the ability to apply SUM along a given dimension was mentioned in the first sentence of the original answer to this question. The answer then went on to address the case when the ability to choose a dimension is not already built in to the function. You are right, though, that using the built-in dimension-selection options -- when they are available -- is almost always faster than a for loop or converting to cells.Hypostasis
True that, however, the answer above sent me back to the matlab documentation, as I didn't need all that fancyness, so I just wanted to share and save others, in need for the simple solution, from searching on.Pneumoencephalogram
Y
-3

if you know the length of your rows you can make something like this:

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) )
Yangyangtze answered 29/8, 2013 at 5:9 Comment(1)
To anyone who sees this answer: This is not the way to do it! This is not the way to do anything in MATLAB!Picaresque

© 2022 - 2024 — McMap. All rights reserved.