Is there a splat operator (or equivalent) in Matlab? - matlab

If I have an array (of unknown length until runtime), is there a way to call a function with each element of the array as a separate parameter?
Like so:
foo = #(varargin) sum(cell2mat(varargin));
bar = [3,4,5];
foo(*bar) == foo(3,4,5)
Context: I have a list of indices to an n-d array, Q. What I want is something like Q(a,b,:), but I only have [a,b]. Since I don't know n, I can't just hard-code the indexing.

There is no operator in MATLAB that will do that. However, if your indices (i.e. bar in your example) were stored in a cell array, then you could do this:
bar = {3,4,5}; %# Cell array instead of standard array
foo(bar{:}); %# Pass the contents of each cell as a separate argument
The {:} creates a comma-separated list from a cell array. That's probably the closest thing you can get to the "operator" form you have in your example, aside from overriding one of the existing operators (illustrated here and here) so that it generates a comma-separated list from a standard array, or creating your own class to store your indices and defining how the existing operators operate for it (neither option for the faint of heart!).
For your specific example of indexing an arbitrary N-D array, you could also compute a linear index from your subscripted indices using the sub2ind function (as detailed here and here), but you might end up doing more work than you would for my comma-separated list solution above. Another alternative is to compute the linear index yourself, which would sidestep converting to a cell array and use only matrix/vector operations. Here's an example:
% Precompute these somewhere:
scale = cumprod(size(Q)).'; %'
scale = [1; scale(1:end-1)];
shift = [0 ones(1, ndims(Q)-1)];
% Then compute a linear index like this:
indices = [3 4 5];
linearIndex = (indices-shift)*scale;
Q(linearIndex) % Equivalent to Q(3,4,5)

Related

Matlab - Using two index arrays to operate on a sub-matrix

I'm trying to figure out how to access Matlab sub-arrays (part of an array) with a generic set of subscript vectors.
In general, the problem is defined as:
Given two n-dim endpoints of an array index (both size nd), one having the initial set of indices (startInd) and the other having the last set of indices (endInd), how to access the sub-matrix which is included between the pair of index-sets?
For example, I want to replace this:
Mat=rand(10,10,10,10);
Mat(2:7, 1:6, 1:6, 2:8) = 1.0;
With an operation that can accept any set of two n-dim vectors specifying the indices for the last operation, which is "abstractly" expressed as:
Mat=rand(10,10,10,10);
startInd=[2 1 1 2];
endInd =[7 6 6 8];
IndexVar=???
Mat(IndexVar) = 1.0;
Thus I want to access the sub-matrix Mat(2:7, 1:6, 1:6, 2:8) using a variable or some other generic form that allows a generic n-dim. Preferably not a loop (as it is slow).
I have tried using something of this nature:
% Generate each index list separately:
nDims=length(startInd);
ind=cell(nDims,1);
for j=1:nDims
ind{j}=startInd(j):1:endInd(j);
end
% Access the matrix:
S.type = '()';
S.subs = ind;
Mat=subsasgn(Mat,S,1.0)
This seems to get the job done, but is very slow and memory-expansive, but might give someone an idea...
If you don't mind looping over dimensions (which should be much faster than looping over array entries):
indexVar = arrayfun(#(a,b) colon(a,b), startInd, endInd, 'UniformOutput', false);
Mat(indexVar{:}) = 1;
This uses arrayfun (essentially a loop) to create a cell array with the indexing vectors, which is then expanded into a comma-separated list.
Now that I see your code: this uses the same approach, only that the loop is replaced by arrayfun and the comma-separated list allows a more natural indexing syntax instead of subsasgn.

cellfun with conditionals in MATLAB

Is it possible to use cellfun with a conditional. For example, I have a 144x53 cell array, where the first four columns are of type string, the rest are floats. However, among the numbers, there are empty cells. I wonder if it is possible to use cellfun(#(x)sqrt(x), cellarray) with my array. As it is know, its not possible due to strings and empty cells. Otherwise, this is the solution that I use,
for n = 1:length(results)
for k = 1:length(results(1,:))
if ~isstr(results{n,k})
results{n, k} = sqrt(results{n,k});
end
end
end
Otherwise, is it possible to do a vectorization here?
You can create a logical array by checking if each element is numeric. And then use this to perform your cellfun operation on the subset of the cell array that contains numeric data.
C = {1, 2, 'string', 4};
% Logical array that is TRUE when the element is numeric
is_number = cellfun(#isnumeric, C);
% Perform this operation and replace only the numberic values
C(is_number) = cellfun(#sqrt, C(is_number), 'UniformOutput', 0);
% 1 1.4142 'string' 2
As pointed out by #excaza, you may also consider leaving it as a loop as it is more performant on newer versions of MATLAB (R2015b and newer).

Accessing data in structures without loops

I have a set of strings vals, for example:
vals = {'AD', 'BC'}
I also have a struct info, inside of which are structs nested in fields corresponding to the elements in the array vals (that would be 'AD' and 'BC' in this example), each in turn storing a number in a field named lastcontract.
I can use a for loop to extract lastcontract for each of the vals like this:
for index = 1:length(vals)
info.(vals{index}).lastcontract
end
I'd like to find a way of doing this without a loop if at all possible, but I'm not having luck. I tried:
info.(vals{1:2}).lastcontract
without success. I think arrayfun may be the appropriate way, but I can't figure out the right syntax.
It is actually possible here to manage without an explicit loop (nor arrayfun/cellfun):
C = struct2cell(info); %// Convert to cell array
idx = ismember(fieldnames(info), vals); %// Find fields
C = [C{idx}]; %// Flatten to structure array
result = [C.lastcontract]; %// Extract values
P.S
cellfun would be more appropriate here than arrayfun, because you iterate vals (a cell array). For the sake of practice, here's a solution with cellfun:
result = cellfun(#(x)info.(x).lastcontract, vals);

How to get multiple outputs of a function in a vector?

Say I have a function whose outputs are two reals a and b
[a,b]=function(c)
I'd like to get all the outputs in a vector v.
v=function(c) doesn't do what I want, v is 'a' only.
Of course here I could do v=[a,b].
But the function in question is ind2sub for a N-D array so it gives n outputs that I'd like to have in a vector directly.
Is there a way to do it?
Thanks very much!
You can use a cell array and a comma-separated list like so:
X = cell(N, 1);
[X{:}] = function(C);
The syntax X{:} is in fact expanded to [X{1}, X{2}, ...], which provides a valid sink for your function. As a result, each output variable will be stored in a different cell in X.
If each output variable is a scalar, you can flatten out the cell array into a vector by using yet another comma-separated list expansion:
v = [X{:}];

Retrieving element index in spfun, cellfun, arrayfun, etc. in MATLAB

Is there any way to retrieve the index of the element on which a function called by cellfun, arrayfun or spfun acts? (i.e. retrieve the index of the element within the scope of the function).
For the sake of simplicity, imagine I have the following toy example:
S = spdiags([1:4]',0,4,4)
f = spfun(#(x) 2*x,S)
which builds a 4x4 sparse diagonal matrix and then multiplies each element by 2.
And say that now, instead of multiplying each element by the constant number 2, I would like to multiply it by the index the element has in the original matrix, i.e. assuming that linear_index holds the index for each element, it would be something like this:
S = spdiags([1:4]',0,4,4)
f = spfun(#(x) linear_index*x,S)
However, note the code above does not work (linear_index is undeclared).
This question is partially motivated by the fact that blocproc gives you access to block_struct.location, which one could argue references the location (~index) of the current element within the full object (an image in this case):
block_struct.location: A two-element vector, [row col], that specifies
the position of the first pixel (minimum-row, minimum-column) of the
block data in the input image.
No, but you can supply the linear index as extra argument.
Both cellfun and arrayfun accept multiple input arrays. Thus, with e.g. arrayfun, you can write
a = [1 1 2 2];
lin_idx = 1:4;
out = arrayfun(#(x,y)x*y,a,lin_idx);
This doesn't work with spfun, unfortunately, since it only accepts a single input (the sparse array).
You can possibly use arrayfun instead, like so:
S = spdiags([1:4]',0,4,4);
lin_idx = find(S);
out = spones(S);
out(lin_idx) = arrayfun(#(x,y)x*y,full(S(lin_idx)),lin_idx);
%# or
out(lin_idx) = S(lin_idx) .* lin_idx;
Note that the call to full won't get you into memory trouble, since S(lin_idx) is 0% sparse.
You can create a sparse matrix with linear_index filling instead of values.
Create A:
A(find(S))=find(S)
Then use A and S without spfun, e.g.: A.*S. This runs very fast.
It's simple. Just make a cell like:
C = num2cell(1:length(S));
then:
out=arrayfun(#(x,c) c*x,S,C)