1

I would like to define an anonymous function that takes a vector, but I am unable to achieve that unless every element in the vector is specified:

Data = 
[ X11 X12;
  X21 X22];

Iteration #1: V -> [X11 X12] (desired row of values to be passed into 'some_func' at each iteration). Iteration #2: V -> [X21 X22]

This works:

good_func_handle = @(a1,a2)  some_func([a1,a2],bla,bla)
output = arrayfun(good_func_handle,Data(:,1),Data(:,2)) -> to avoid having to write a for loop

This doesn't work:

bad_func_handle = @(vec)  some_func(vec,bla,bla)
output = arrayfun(bad_func_handle,Data)

When this is called only the first element X11 is passed in to some_func but not X11 and X12.

Is there a way to set up the function to take a vector variable as input instead of having to specify all elements in the vector?

Cris Luengo
  • 55,762
  • 10
  • 62
  • 120
broccolee
  • 11
  • 2
  • The single variable version should pass the entire variable (all elements) to some_func. If some_func is only working with the first element of v, then the problem is with some_func. If you replace some_func with a built in function like sin or cos, you should get sin or cos of the whole vector as an output. – Nick J Jun 16 '22 at 12:10
  • This question is phrased in a very confusing way. Are you trying to "explode" a vector into its parts? Or are you trying to create a vector from individual variables? – Tasos Papastylianou Jun 16 '22 at 14:24
  • @NickJ Thank you for your answer. There is no problem with 'some_func'. I can pass in an array, and it returns appropriately. I am just trying to avoid having to write for loops. I'd vectorize the function, but it cannot be vectorized unfortunately. – broccolee Jun 16 '22 at 16:04
  • @TasosPapastylianou Pardon me, I have rephrased the question, hopefully it's a little clearer. I would like to specify the input to the function handler as a single variable representing the vector. Because if I have a vector of 1000 elements, then it becomes cumbersome. – broccolee Jun 16 '22 at 16:08
  • Why not just use a loop? Seems the most logical solution to me. I mean, all `arrayfun` is doing is hiding the loops anyway. – beaker Jun 16 '22 at 16:19
  • @beaker Thank you for your answer. My solution is ultimately for loop as well, I'm just curious if this can be done or not. – broccolee Jun 16 '22 at 16:34
  • It is “function handle”, not “handler”. And what you are struggling with is an anonymous function, not the handle itself. A function handle is just a reference to a function (either named or anonymous). I’ll rewrite your question to use the right terminology. – Cris Luengo Jun 16 '22 at 16:36
  • In order to truly vectorize the operation, you would have to have some way of achieving the functionality of `some_func` that would automatically be applied to each row of the input. But to answer your question, you *could* convert the matrix to a cell array with each cell being a row from the matrix, then use `cellfun` instead of `arrayfun`. It doesn't quite seem worth it to me. – beaker Jun 16 '22 at 16:51
  • 1
    @beaker I agree. That would be more complex code (more difficult to read) than a plain old loop, and possibly slower too (though I don't have a good feel for efficiency of Octave code, in MATLAB removing a function call always speeds up code). – Cris Luengo Jun 16 '22 at 18:40
  • it sounds like your issue is misunderstanding the behavior of `arrayfun`. by definition it operates on each individual element of an array. there are ways to twist `arrayfun` to act on blocks of an input array, e.g., by passing it an index instead of the array itself and making creative use of indexing in an anonymuos function but there's virtually no benefit over a for loop at that point. – Nick J Jun 16 '22 at 20:41

1 Answers1

2

The problem is in arrayfun, not in the function being called. arrayfun loops over each element of the input array, not each row. In your first solution, you created input arrays such that looping over each element was the right thing to do.

Since arrayfun is just a loop, there is no advantage in time for using it, other than programmer time. It is just supposed to simplify writing a loop over an array. [Qualifications: In MATLAB, arrayfun adds overhead because a function needs to be called for each iteration; this can be significant as shown below. In Octave, arrayfun seems to be a bit more efficient than a plain loop, see smcmi's comments below.]


In response to the comment below by @smcmi, I re-created their experiment as follows:

a = zeros(1, 1e6);

b = method1(a);
c = method2(a);
assert(isequal(b,c))

timeit(@()method1(a))
timeit(@()method2(a))

function out = method1(in)
    out = zeros(size(in));
    for ii=1:numel(in)
        out(ii) = in(ii) * 2;
    end
end

function out = method2(in)
    out = arrayfun(@(x) x * 2, in);
end

Running this using MATLAB Online (which is pretty slow usually) I see this output:

ans =
    0.0036

ans =
    2.5299

That is, the plain loop takes 0.0036 s, whereas arrayfun takes 2.5 s. This is three orders of magnitude difference.

The largest cost in arrayfun is calling a function for every array element. In MATLAB, calling a function still carries a bit of overhead, whereas the loop is completely optimized by the JIT and runs at speeds similar to those seen in any compiled language.

In Octave the timings are quite different, as it has no JIT at all. See comments below this answer for Octave performance.

Cris Luengo
  • 55,762
  • 10
  • 62
  • 120
  • Sorry, posted too early. I'm not sure this is true, I timed various procedures, each taking an array element and multiplies it by 2. For a 1 x 1e7 array, --a for loop on a non-preallocated array took 194 seconds --a for loop with a zeros-preallocated array took 39 s (arrayfun doesn't require preallocation, which is also nice) --arrayfun(@times2,array) took 32 s, where @times2 is input *= 2 --arrayfun(@(x) 2*x) took 28 seconds --arrayfun(@times,2,array) took 26 seconds So you're saving almost 20% right away, and up to 35% if you can put your function in the arrayfun call. – smcmi Jun 23 '22 at 19:39
  • I used "tic" and "toc" surrounding each procedure and compared. – smcmi Jun 23 '22 at 19:40
  • 1
    @smcmi Oh, right, in Octave there's no JIT at all, timings are very different there. Do note that `@times` is a built-in function, your `@times2` is the most meaningful thing to compare to. – Cris Luengo Jun 23 '22 at 19:43
  • I'm trying to post my code, but it's not displaying properly and just looks convoluted. Essentially, it's each procedure I described above surrounded by `tic` and `toc` within a single function, and t(1), t(2), etc. set equal to `toc` each time, then comparing the `t` values. – smcmi Jun 23 '22 at 19:47
  • Cris, adapting your code to Octave, I obtained 3.4 s for `method1` and 2.7 s for `method2`, again roughly a 20% reduction, just like before. So there's a benefit in Octave, but a (serious!) drawback in Matlab. – smcmi Jun 23 '22 at 20:21