Retrieving element index in spfun, cellfun, arrayfun, etc. in MATLAB - 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)

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.

Extract values from a vector and sort them based on their original squence

I have a vector of numbers (temperatures), and I am using the MATLAB function mink to extract the 5 smallest numbers from the vector to form a new variable. However, the numbers extracted using mink are automatically ordered from lowest to largest (of those 5 numbers). Ideally, I would like to retain the sequence of the numbers as they are arranged in the original vector. I hope my problem is easy to understand. I appreciate any advice.
The function mink that you use was introduced in MATLAB 2017b. It has (as Andras Deak mentioned) two output arguments:
[B,I] = mink(A,k);
The second output argument are the indices, such that B == A(I).
To obtain the set B but sorted as they appear in A, simply sort the vector of indices I:
B = A(sort(I));
For example:
>> A = [5,7,3,1,9,4,6];
>> [~,I] = mink(A,3);
>> A(sort(I))
ans =
3 1 4
For older versions of MATLAB, it is possible to reproduce mink using sort:
function [B,I] = mink(A,k)
[B,I] = sort(A);
B = B(1:k);
I = I(1:k);
Note that, in the above, you don't need the B output, your ordered_mink can be written as follows
function B = ordered_mink(A,k)
[~,I] = sort(A);
B = A(sort(I(1:k)));
Note: This solution assumes A is a vector. For matrix A, see Andras' answer, which he wrote up at the same time as this one.
First you'll need the corresponding indices for the extracted values from mink using its two-output form:
[vals, inds] = mink(array);
Then you only need to order the items in val according to increasing indices in inds. There are multiple ways to do this, but they all revolve around sorting inds and using the corresponding order on vals. The simplest way is to put these vectors into a matrix and sort the rows:
sorted_rows = sortrows([inds, vals]); % sort on indices
and then just extract the corresponding column
reordered_vals = sorted_rows(:,2); % items now ordered as they appear in "array"
A less straightforward possibility for doing the sorting after the above call to mink is to take the sorting order of inds and use its inverse to reverse-sort vals:
reverse_inds = inds; % just allocation, really
reverse_inds(inds) = 1:numel(inds); % contruct reverse permutation
reordered_vals = vals(reverse_inds); % should be the same as previously

MATLAB: bsxfun unclear. Want to accelerate minimum distance between segments

Using MATLAB,
Imagine a Nx6 array of numbers which represent N segments with 3+3=6 initial and end point coordinates.
Assume I have a function Calc_Dist( Segment_1, Segment_2 ) that takes as input two 1x6 arrays, and that after some operations returns a scalar, namely the minimal euclidean distance between these two segments.
I want to calculate the pairwise minimal distance between all N segments of my list, but would like to avoid a double loop to do so.
I cannot wrap my head around the documentation of the bsxfun function of MATLAB, so I cannot make this work. For the sake of a minimal example (the distance calculation is obviously not correct):
function scalar = calc_dist( segment_1, segment_2 )
scalar = sum( segment_1 + segment_2 )
end
and the main
Segments = rand( 1500, 6 )
Pairwise_Distance_Matrix = bsxfun( #calc_dist, segments, segments' )
Is there any way to do this, or am I forced to use double loops ?
Thank you for any suggestion
I think you need pdist rather than bsxfun. pdist can be used in two different ways, the second of which is applicable to your problem:
With built-in distance functions, supplied as strings, such as 'euclidean', 'hamming' etc.
With a custom distance function, a handle to which you supply.
In the second case, the distance function
must be of the form
function D2 = distfun(XI, XJ),
taking as arguments a 1-by-N vector XI containing a single row of X, an
M2-by-N matrix XJ containing multiple rows of X, and returning an
M2-by-1 vector of distances D2, whose Jth element is the distance
between the observations XI and XJ(J,:).
Although the documentation doesn't tell, it's very likely that the second way is not as efficient as the first (a double loop might even be faster, who knows), but you can use it. You would need to define your function so that it fulfills the stated condition. With your example function it's easy: for this part you'd use bsxfun:
function scalar = calc_dist( segment_1, segment_2 )
scalar = sum(bsxfun(#plus, segment_1, segment_2), 2);
end
Note also that
pdist works with rows (not columns), which is what you need.
pdist reduces operations by exploiting the properties that any distance function must have. Namely, the distance of an element to itself is known to be zero; and the distance for each pair can be computed just once thanks to symmetry. If you want to arrange the output in the form of a matrix, use squareform.
So, after your actual distance function has been modified appropriately (which may be the hard part), use:
distances = squareform(pdist(segments, #calc_dist));
For example:
N = 4;
segments = rand(N,6);
distances = squareform(pdist(segments, #calc_dist));
produces
distances =
0 6.1492 7.0886 5.5016
6.1492 0 6.8559 5.2688
7.0886 6.8559 0 6.2082
5.5016 5.2688 6.2082 0
Unfortunately I don't see any "smarter" (i.e. read faster) solution than the double loop. For speed consideration I'd organize the points as a 6×N array, not the other way, because column access is way faster than row access in MATLAB.
So:
N = 150000;
Segments = rand(6, N);
Pairwise_Distance_Matrix = Inf(N, N);
for i = 1:(N-1)
for j = (i+1):N
Pairwise_Distance_Matrix(i,j) = calc_dist(Segments(:,i), Segments(:,j));
end;
end;
Minimum_Pairwise_Distance = min(min(Pairwise_Distance_Matrix));
Contrary to common wisdom, explicit loops are faster now in MATLAB compared to the likes of arrayfun, cellfun or structfun; bsxfun beats everything else in terms of speed, but it doesn't apply to your case.

Matlab -- Copy Structure Array without For Loop

I have a fairly simple question in Matlab. I want to copy n items of structure array (sumRT.P) to a matrix (m). In C, I would just use a for loop, like this:
for i = 1:n
m(i) = sumRT(i).P;
end
But I bet there's a simpler way to copy an array in Matlab (that's the whole point of language right?). I tried this:
m = sumRT(1:n).P;
But this just copies the first item in sumRT.P to m, resulting in a 1 X 1 matrix. Note, if I type, sumRT(2).P for example, I can see the second item. Same for any number up to n. Why is this wrong and how do I fix it?
It depends on the data types in your structure array. If they are types of variables, or if they are variables of the same size in arrays of different dimensions, then you can't put them into an array, but you can make them into a cell:
m={sumRT(1:n).P}
and cells are pretty simple to deal with, so this oughtn't be a big problem.
If they are all scalar numerical values, you can create a matrix:
m=cell2mat({sumRT(1:n).P})
Try the following:
m = squeeze(cell2mat(struct2cell(sumRT(1:n))));
This converts the struct array to a cell array, and then to a (numeric) array, and then squeezes it by remoiving singleton dimensions.
Example:
>> sumRT(1).P = 10; sumRT(2).P = 20; sumRT(3).P = 30;
>> n = 2; %// copy first two elements only
>> m = squeeze(cell2mat(struct2cell(sumRT(1:n))))
m =
10
20

Is there a splat operator (or equivalent) in 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)