How to access multidimensional arrays in MATLAB with mixed index format - matlab

Suppose I have two arrays, M1 and M2. Both have dimensions m x n x p. I'm interested in the mxn array of M1 corresponding to the maximum element along the third dimension, so I do:
[M1max, indices]=max(M1,[],3);
Both M1max and indices are m x n arrays. But now suppose I want to access the elements of M2 that correspond to those maximum elements in M1 (that is, I want the all the elements of M2 with the same index as an element of M1 that ended up in M1max). How do I do this?

I think this should make it:
[y x]=ndgrid(1:size(M1,1),1:size(M1,2));
reshape(M2(sub2ind(size(M1),y(:),x(:),indices(:))),[size(M1,1),size(M1,2)]);
you want all the index with idx <-> (y,x,indices(y,x)), this will compute it. And then compute M2(idx) and reshape it well.

Another way is ignoring indices from max:
indices2 = M1 == repmat(M1max,[1,1,size(M1,3)]);
result = reshape(M2(indices2),size(M1max));
There might be a precision issue with comparing doubles. In this case you can do
indices2 = repmat(M1max,[1,1,size(M1,3)]) - M1 < eps;
In addition, there will be a problem with this code if multiple identical max values exist in M1 in the 3rd dimension. We can catch this case with
assert(sum(indices2(:))==numel(M1max),'Multiple maximum values found')

This might be slightly faster than #Oli's suggestion, but they're basically equivalent:
[M1max, indices] = max(M1,[],3);
[m n p] = size(M1);
idx = (1:m*n).' + (indices(:)-1)*m*n;
M2max = reshape(M2(idx), m, n);

Related

Matlab: How to extract specific cells from a 3D array using list of indices?

I have a source matrix A(m,n) for which I used "find" and now I have a list of desired indices [y,x].
I also have a 3D matrix with dimensions B(m,n,3).
I want to extract all the elements in B using the result from find.
So if find yields 4 pairs of results, I would like to have a 4x3 matrix with the contents of the Z dimension of B for the resulting indices.
I tried many things but keep failing:
A = rand(480,640);
[y,x] = find(A < 0.5);
o = B(y,x,:);
Requested 39024x39024x3 (34.0GB) array exceeds maximum array size preference.
I am clearly doing something wrong since B has dimensions (640,640,3).
With the way you are trying to index, matlab tries to index B with every combination of the elements in y and x resulting in a massive matrix. I've implemented a for loop to do what I think you are asking.
I would also note that in order to index across B the first two dimensions need to be the same size as A, Otherwise you will not be able to index B past the maximum row or column index in A.
A = rand(480,640);
B = rand(480,640,3);
[x,y] = find(A < 0.5);
o = zeros(size(x,1),1,3); % x and y are the same length so it doesn't matter
for i = 1:size(x,1)
o(i,1,:)= B(x(i),y(i),:);
end
o = reshape(o,size(x,1),3);
You can reshape B to a 2D matrix of size [m*n , 3] then use logical indexing to extract elements:
C = reshape(B, [], 3);
o = C(A<0.5, :);

Maximum of a subset of array (MATLAB)

Suppose in MATLAB I have a real matrix A which is n x m and a binary matrix B of the same size. The latter matrix defines the optimization set (all indices for which the element of B equals one): over this set I would like to find the maximal element of A. How can I do this?
The first idea I had is that I consider C = A.*B and look for the maximal element of C. This works fine for all matrices A which have at least one positive element, however it does not work for matrices with all negative elements.
You can do
C = A(B==1);
to give you an array of just the values of A corresponding to a value of 1 in B. And
max( C )
will give you the maximum value of A where B is 1
With this method you don't run into a problem when all values of A are negative as the zeros don't appear in C.
Obviously you can condense this to
desiredValue = max(A(B(:)==1));
I am using the colon operator to make sure that the result of A(B(:)==1) is a column vector - if B is all ones I am not sure if Matlab would return a vector or a nxm matrix (and I can't confirm right now).
update to get the index of the value, you can do:
f = find(B==1);
[m mi] = max(A(f));
maxIndex = f(mi);
And to get that back to the 2D elements:
[i j] = ind2sub(size(A), maxIndex);

MATLAB: Create a block diagonal matrix with same repeating block

I have a matrix K of dimensions n x n. I want to create a new block diagonal matrix M of dimensions N x N, such that it contains d blocks of matrix K as its diagonal.
I would have directly used M = blkdiag(K,K,K) etc. had d been smaller. Unfortunately, d is very large and I don't want to manually write the formula with d exactly same arguments for the blkdiag() function.
Is there any shorter, smarter way to do this?
you can use kron for that.
M = kron(X,Y)
returns the Kronecker tensor product of X and Y. The result is a large array formed by taking all possible products between the elements of X and those of Y. If X is m-by-n and Y is p-by-q, then kron(X,Y) is m*p-by-n*q. So in your case something like this will do:
M = kron(eye(L),K)
with L the # of blocks.
tmp = repmat({K},d,1);
M = blkdiag(tmp{:});
You should never use eval, or go into for loops unnecessarily.
Kron is a very elegant way.
Just wanted to share this as it also works.
The following should work:
d=5; K=eye(3); T = cell(1,d);
for j=1:d
T{j} =K;
end
M = blkdiag(T{:})
s = 'A,';
s = repmat(s,[1,n2]);
s = ['B=blkdiag(', s(1:end-1),');'];
eval(s);
It can be faster than using kron-eye.
A "for" loop may might help. Like:
M = k;
for i=1:N/n - 1
M=blkdiag(M,k);
end

Matlab compare different sized vectors

If I have 2 vectors: A with n elements and B with m elements and m < n,
how do I identify all the elements in A which are in B, without using a for loop?
Many thanks
C = intersect(A,B) will give you all the elements which are in both.
There's also ismember(A,B), which will return a logical array indicating for each member of A whether it is also a member of B.
Here is one solution to find which elements of the longer vector (x) are in the shorter vector (y)
x = 1:10;
y = 2:4;
xrep = repmat(x,length(y),1)
yrep = repmat(y',1,length(x))
idx = any(xrep==yrep)

Selecting entries from a matrix without using a loop

I have two matrices A and B, both of which are Nx3 matrices.
I'm currently getting the maximum value and index for each row of matrix A using:
[maxA, idx] = max(A, [], 2)
idx(j) indicates which column contained the maximum for row j. Now I'd like to select those same positions from matrix B.
I've currently implemented this using a loop:
for j = 1:numel(idx)
maxB(j) = B(j, idx(j))
end
My current implementation is fast enough, although I prefer to avoid unneeded loops so is there a way to express this without a loop?
You can build a vector of linear indices (I expect B to be the same size as A):
vec_indices = sub2ind(size(A), 1:numel(idx), idx);
Then you can use that vector directly for lookup:
maxB = B(vec_indices)
You can construct the single dimension index into the matrix and get them that way. All multidimensional matrices in matlab can be addressed.
You can use
maxB = B(sub2ind([1:length(idx)]',idx(:)));
In one line:
maxB = B(A == max(A, [], 2) * ones(1, 3));
But this is not safe. It assumes unique values in every row of A.