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.
6:33 am |
Posted in Efficiency, Memory, New Feature, Vectorization |
Permalink |
You can follow any responses to this entry through the RSS 2.0 feed.
You can skip to the end and leave a response. Pinging is currently not allowed.
Leave a Reply
|
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.
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?
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
Hello Loren,
arrayfuncion works on each array element,
is there a function which operates on each colon?
Thanks,
Dani.
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
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