Loren on the Art of MATLAB

March 1st, 2006

More on expansion: arrayfun

Since there seems to be so much interest in the topic of going beyond scalar expansion, it seems worth talking about yet some other ways to achieve the same goals. I am very appreciative of the thoughtful comments we've received so far on this topic and I know we will be weighing this input carefully as we design how to move forward.

Suppose we want to raise all the values in x to all the powers in powers, where both x and powers are vectors, like this:

powers = 1:3;
x = (0:.1:.4)';

We know from the last blog article, we can achieve this by creating larger intermediate arrays like this, using Tony's trick (used for most of the work in repmat:

X = x(:,ones(size(powers,2),1))
P = powers(ones(size(x,1),1),:)
X =

         0         0         0
    0.1000    0.1000    0.1000
    0.2000    0.2000    0.2000
    0.3000    0.3000    0.3000
    0.4000    0.4000    0.4000

P =

     1     2     3
     1     2     3
     1     2     3
     1     2     3
     1     2     3

and then we are ready to perform the desired calculation:

q = X.^P
q =

         0         0         0
    0.1000    0.0100    0.0010
    0.2000    0.0400    0.0080
    0.3000    0.0900    0.0270
    0.4000    0.1600    0.0640

but at the cost of 2 larger arrays, X and P, that we might not even need going forward.

In the absence of new operators in MATLAB 7, how might we solve this today without creating these temporary arrays and without using a for loop? A new feature in R14sp3, arrayfun is a good candidate. Here are two ways to achieve the same results using arrayfun, the first iterating calculating results for each value of x and the second, for each value of powers. I am taking advantage of anonymous functions, new in MATLAB 7, to define the function I want to calculate, first as a function of powers, and second, as a function of the x values.

pq = cell2mat(arrayfun(@(p)x.^p,powers,'uniformoutput',false));
xq = cell2mat(arrayfun(@(x)x.^powers,x,'uniformoutput',false));

Before proceeding, let's just make sure we're getting the right answer!

isequal(xq,pq,q)
ans =

     1

Now that we've taken care of checking for correctness, let's decipher what the statements mean. Taking the final arguments to arrayfun first, I chose nonuniform output since for each value of my function, I calculate a vector of output values, not a single value. This places outputs for each input value (in this case, the values in powers), into cells in a cell array.

The first argument is an anonyous function, a function defined "inline" that takes its non-argument values from the workspace. So the first function, @(p)x.^p, takes x from the workspace. The function itself takes the values in x and raises them each to some value p, the function argument. Then with the second input to arrayfun, we set the argument to be iterated over to powers from our workspace.

Finally, since our output is now in a cell array, in this case with each cell containing equal numbers of elements, we convert the cell array to a numeric matrix using cell2mat.

Which of the 2 methods using arrayfun is preferable? Well, as is so often, that depends. Since there's an intermediate cell array, we might want to minimize its size. How large are typical sizes for the vectors x and powers? If one is typically substantially larger than the other, we might want to profile the code, either from the command line or the desktop (see this link for information about using the MATLAB Profiler user interface). The profiler can help us make these tradeoffs.

Once again I ask for you to add your thoughts here. Thanks.

9 Responses to “More on expansion: arrayfun”

  1. Petr Pošík replied on :

    Hi Loren. Thanks for introducing the arrayfun to me! I’m using SP2 so that I can’t use them, but anyway…

    Yes, this is a reasonable way of accomplishing the expansion. Similar constructs can be found in PERL (function map), etc.

    But one reason for which I love MATLAB so much is the minimalism with which these things can be written. And I think the arrayfun is not the MATLAB way of doing it. Compare:

    > 5 + [6 7 8 9]

    with

    > arrayfun(@(p)5+p,[6 7 8 9],’uniformoutput’,true)

    Of course, this is a stupid example, but I really do not know if I fell in love with MATLAB if I had to write the second type of commands. The difference when compared with FOR loops is not that big, and in my opinion, the FOR loops are for many programmers more intuitive because they are used to use them.

  2. A Brook replied on :

    This is one point where Mathematica beats you. Their “Outer” is much cleaner and easier to use than this “arrayfun”.

    Why can’t we have Outer in Matlab? Then we can say
    Outer(@(a,b)a.^b,x,powers) — so much easier, nicer, and keeping with Matlab Spirit.

    Maybe you can somehow implement Outer with arrayfun?

  3. StephenLL replied on :

    The approach recommended by Urs in Loren’s previous blog entry, would yield something like: genopfun(x,y.’,'^’) (if x and y were vectors) which is really nice and compact. Simililar to R’s outer function. What makes the genop solution so nice is that rather then just vectors it would work along singleton dimensions as well.

    Stephen

  4. Dani Hak replied on :

    Hello Loren,

    arrayfuncion works on each array element,
    is there a function which operates on each colon?

    Thanks,
    Dani.

  5. Loren replied on :

    Dani-

    No, there is not function per se that works on each column. However, many MATLAB functions do operate on columns such as sum, mean, fft. You could write an anonymous function that takes a matrix and works on each column (using something like A(:,i) ). If you have a detailed question, I recommend you contact technical support to help with the specific issues.

    –Loren

  6. StephenLL replied on :

    Dani,

    The following maybe what you are looking for.
    http://www.mathworks.com/matlabcentral/fileexchange/loadFile.do?objectId=16467&objectType=FILE

    I wrote it so I did not have to explicitly write loops or always write code that could take dim as an input.

    The benefit is not speed but clear compact writing of code.

    StephenLL

  7. Mohammed replied on :

    I would like to use arrayfun in my code, but I cannot figure out how to apply it to my equation.

    I have a matrix C 670×670, I would like to perform this calculation on every element in C, the equation is:

    m = some constant
    Aij = (m * Cij) / (Cii * Cjj) Where i != j

    Is it possible to do that without using for loops?
    Thanks,

  8. Loren replied on :

    Mohammed-

    I don’t think arrayfun is the best tool for the job here. This can be easily vectorized, though I don’t know what you want for the diagonals when i == j; I’ll assume 0 for now. I haven’t tried this, but something like this should work.

    d = diag(C);
    A = m*diag(d)\C/diag(d);
    [mm,mm] = size(A);  % assuming A is square
    A(1:(mm+1):end) = 0; % fill diagonals
    

    –Loren

  9. pierce replied on :

    -Dani
    I believe you are looking for something like this.
    It will subtract each row in martix B from matrix A

    arrayfun(@(i)(bsxfun(@minus,A,B(i,:))),1:size(B,1),'UniformOutput',false)
    

Leave a Reply

Wrap code fragments inside <pre> tags, like this:

<pre class="code">
a = magic(3);
sum(a)
</pre>

If you have a "<" character in your code, either follow it with a space or replace it with "&lt;" (including the semicolon).


Loren Shure works on design of the MATLAB language at The MathWorks. She writes here about once a week on MATLAB programming and related topics.

  • Jun: I totally can not believe it, Loren. You are really helpful. Thank you so much, MATLAB master!
  • Loren: Wow folks- Always lots of interest when there’s a quickie to try out! I will only make 2 general...
  • Loren: Jun- ismember is your friend here: >> [aa,ind] = ismember(Array2,Arra y1) aa = 1 1 1 1 1 1 1 ind = 1 2 1 4 4 3...
  • Dan: I like the first way better than the second way. Combining the arrays into one and running any is nice, although...
  • James Myatt: How about I = (a == 0 | b == 0); a(I) = []; b(I) = [];
  • Tunc: Hello Loren, love your blog because of such inspiring and challenging comments to such ’small’...
  • Pekka Kumpulainen: Here is my tradeoff. I usually want to keep the original variables as they are most probably...
  • Iain: Followup: Of course, to allow NaNs (counting them as non-zero): mask = (a~=0) & (b~=0); The mask says “a...
  • Matt Fig: I would usually go with something like this: y = a&b; x = a(y); y = b(y); But I was surprised to find...
  • kk: c=all([a;b]) a(c) a(b)

These postings are the author's and don't necessarily represent the opinions of The MathWorks.