Return only second output in anonymous function

62 views (last 30 days)
I need to get an index via max() and use this to get the corresponding element like in the following code:
data = complex(1:5,1:5);
[~,i] = max(abs(data));
res = real(data(i)):
Now, I want to do this for multiple datasets at once. Therefore I have a cell array of vectors. Below, you find a working version of what I am doing.
Is there an easier/faster/better way to do the following:
% test data
data = {complex(1:5,1:5) complex(6:10,6:10)};
% working example
fct = @(d) max(abs(d));
[~,idxs] = cellfun(fct, data, 'UniformOutput', false);
fct2 = @(idx, d) d(idx);
vals = cellfun(fct2, idxs, data, 'UniformOutput', false);
res = real([vals{:}]);

Accepted Answer

Star Strider
Star Strider on 9 Jan 2016
If you only want the indices, one possibility is to use the find function, returning only the index of the first maximum value it finds, matching the index returned by the second output of max. This produces the same result as your posted code:
% test data
data = {complex(1:5,1:5) complex(6:10,6:10)};
% working example
fct = @(d) find(abs(d) == max(abs(d)), 1, 'first');
idxs = cellfun(fct, data, 'Uni',0);
fct2 = @(idx, d) d(idx);
vals = cellfun(fct2, idxs, data, 'UniformOutput', false);
res = real([vals{:}]);
I’m also giving you a vote, because yours is an example of the way a Question is best posed. It’s one of the best I’ve seen.
  2 Comments
Dark Dragon
Dark Dragon on 9 Jan 2016
Although your solution doesn't work for an arbitrary function returning two arguments, it works for my specific problem!
With your help, I could get rid of the splitted cellfun calls. This speeded up the calls about a factor of 2!
% test data
data = {complex(1:5,1:5) complex(6:10,6:10)};
% working example
fct = @(d) real(d(find(abs(d) == max(abs(d)), 1, 'first')));
vals = cellfun(fct, data, 'Uni',0);
res = [vals{:}]
Also thanks for your hint to shorten
('UniformOutput', false)
to
('Uni', 0)
Star Strider
Star Strider on 9 Jan 2016
My pleasure! I’m happy to have helped!
As for the ('Uni',0), I have to credit that to someone who I saw use it back in 2012. I learn a lot of interesting coding techniques from hanging out here!

Sign in to comment.

More Answers (2)

Jon Glassman
Jon Glassman on 16 Mar 2019
You can create this generic helper function:
function Z = Out2(FUN,varargin)
% Z = Out2(FUN,VARARGIN);
%
% Provides the second output from the function
[~,Z] = FUN(varargin{:});
This should allow you to do something like the following:
@(x) Out2(@max,[1 4 8 5 0])
  4 Comments
Alan
Alan on 7 Jan 2020
I didn't fully understand the answer here
"The number of outputs for an assignment gets propagated into the expression, and can be narrowed by the calls that are made, but can never be expanded [see exception]. If there are not two or more outputs on the assignment, then each expression along the way will be told to expect only one output."
Anonymous functions can have more than one output, but need to be called so that the assigment of those outputs is explicit. So to get the second output, you have to also ask for and get the first? And that has implications in the way you have to write anonymous functions. Is that right?
Walter Roberson
Walter Roberson on 7 Jan 2020
Anonymous functions can have more than one output, but need to be called so that the assigment of those outputs is explicit.
Not exactly. Suppose you have
T1 = cellfun(@min, YourCellArray);
For simplicity we will assume that YourCellArray contains entries that are only scalar or vector, so that min() for each one returns a scalar minimum.
This code will run through the cell array entries, find the minimum value for each, and return the minima in a (probably) numeric array the same size as YourCellArray.
Now suppose you want to get the positions as well as the values. You can do that with
[T1, T2] = cellfun(@min, YourCellArray);
and the position indices will be returned in T2.
Now ask yourself how this is is implemented. Is cellfun deliberately looking to see how many outputs it has been asked for, and deliberately doing something like
for idx = 1 : numel(InputCellArray)
[Output{1:nargout}] = UserFunction(InputCellArray{idx});
end
No, because that does not store all of the results. It would have to do something like,
Output = cell(1, nargout);
for idx = 1 : numel(InputArray)
[temp{1:nargout}] = UserFunction(InputArray{idx});
for K = 1 : nargout
Output{K}(idx) = temp{K};
end
end
which is do-able. But requiring that each step explicitly inquire about output size does not appear to be how MATLAB works internally. It is possible to construct chains of calls
[T1, T2] = OuterFunction(InnerFunction(Parameter))
In this situation, how does InnerFunction know how many output arguments there are? It is being called in a context that would normally imply that only the first output should be used, but that is not what happens: MATLAB appears to see that there are two outputs on the assignment, and appears to tell InnerFunction to expect two output arguments at the time that InnerFunction executes -- which, after all, is before OuterFunction executes.
I do not have an example at the moment; good examples where it makes a difference are hard to construct, but "magically" conveying the number of output arguments is the only way I have been able to explain some of the situations I have seen. It might have been something along the lines of my having InnerFunction display nargout and seeing that the number reported depended on the number of outputs in the assignment.
... And now I cannot reproduce those strange situations.... sigh.
The behavior that I can reproduce is as-if in each case, a function call other than the outer-most function call is only ever told that the number of output arguments is 1, but the outermost call can find the real number of output arguments. Perhaps cellfun really is deliberately handling multiple outputs...
If the model is that each call except the outermost one is told that there is only one output (which is what I was sure it was for a long time until I encountered some corner cases I could not explain), then it would hold true for anonymous functions, and it would be impossible for the anonymous function to access the second output of something that was passed in to it, on the basis that whatever was passed in to it would have been told there was only one output to create. There is no syntax in MATLAB for a function to request multiple outputs from something that is passed it it: remember that whatever is passed in as parameters will have been evaluated first and not given an option of producing multiple outputs.
This gets us down to the possibility of writing an anonymous function along the lines of
Out2 = @(FunctionHandle, varargin) some code that invokes FunctionHandle on varargin{:} and asks for the second output
However the rules apply to any function that Out2 might invoke: nothing that Out2 can invoke can possibly request multiple outputs of FunctionHandle in a way that Out2 could post-process the result to return only one of the outputs (other than the first.)
It would be nice sometimes if there was a built-in
CollectOutputs(FunctionHandle, N, varargin)
that asked FunctionHandle for N outputs and inserted them into a cell array, but no such built-in function exists. It is easy to write such a function yourself, but it does require at least one real function, and is not possible to construct only with MATLAB functional primitives.

Sign in to comment.


Modess Seyednezhad
Modess Seyednezhad on 20 Oct 2017
Edited: Walter Roberson on 7 Jan 2020
how about when we have:
function [f] = f024(theta)
f(1) = 23*sin(theta); f(2) = 23*cos(theta);
end
how can I call only f(2) ?

Products

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!