Linear index of the maximum of a multi-dimensional matrix - MATLAB - matlab

Let's say I have a 3-dimensional matrix and have computed the max along the second dimension, and want to get the linear indices of the max values. However, the max-function only returns the subscripts along one dimension.
A = randn([5,5,5]); % Generate random matrix
[M, Ind] = max(A,[],2); % Take the max along dimension 2
How do I transfer the index to linear indexing, such that
M == A(Ind)
becomes true?
My intention for this problem is that I have two multi-dimensional matrices and need to compute the max in the first one. Then, I want to access the values in the second matrix at exactly those positions where I found a max in the first one.

One way is to use sub2ind:
A = randn([5,5,5]);
[M, col] = max(A,[],2);
[m,n,o] = size(A);
dim1 = mod((0:m*o-1)', m)+1;
dim2 = col(:);
dim3 = ceil((1:m*o)/m)';
ind = sub2ind(size(A), dim1, dim2, dim3)
verify it works with
isequal(M(:), A(ind))
to get them to have the same shape as M:
reshape(ind, m, 1, o)

Create the indices for the other dimensions.
In dim 1 the index needs to change fastest: [1,2,...,size(A,1)] and this size(A,3) times:
idx1 = repmat((1:size(A,1))',size(A,3),1);
In dim 2 the index is given by Ind.
In dim 3 the index need to change slowest: [1,1,...,1] for size(A,1) times and then [2,2,...,2] and so on until size(A,3).
idx3 = ones(size(A,1),1)*(1:size(A,3));
Access single values:
M_ = A(sub2ind(size(A),idx1(:),Ind(:),idx3(:)));
Compare:
M(:) == M_

3-dimensional case:
[m, n, p] = size(A);
[M, Ind] = max(A,[],2);
LinInd = bsxfun(#plus, (1:m).', (0:p-1)*m*n); %'//
LinInd = LinInd(:) + (Ind(:)-1)*m;
The desired linear index is LinInd. This produces
A(LinInd) == M(:)
with all true entries (note you need (:) on the right-hand side so that the comparison makes sense).
General multi-dimensonal case:
d = 3; %// dimension along which max will be computed
s = size(A);
sLow = prod(s(1:d-1));
sHigh = prod(s(d+1:end));
[M, Ind] = max(A,[],d);
LinInd = bsxfun(#plus, (1:sLow).', (0:sHigh-1)*sLow*s(d)); %'//
LinInd = LinInd(:) + (Ind(:)-1)*sLow;

Let's suppose A and B are the two matrices you have and you need to get max indices from A and use those indices to index into B for the desired output. One approach to achieve the same could be like this -
%// Your code to get Ind
A = randn([5,5,5]); % Generate random matrix
[M, Ind] = max(A,[],2); % Take the max along dimension 2
%// ------- Solution code -------------
%// Get the size of A
[n1,n2,n3] = size(A)
%// Linear indices corresponding to column and third dimension indices
col_dim3_lin_idx = bsxfun(#plus,(Ind-1)*n1,permute([0:n3-1]*n1*n2,[1 3 2]))
%// Finally get the overall linear indices
linear_index = bsxfun(#plus,col_dim3_lin_idx,[1:n1]') %//'
%// Get the corresponding elements from B
out = B(linear_index)
Slightly different way to have the desired linear indices as a 2D array would be like this -
[n1,n2,n3] = size(A) %// Get the size of A
idx = bsxfun(#plus,bsxfun(#plus,squeeze((Ind-1)*n1),[0:n3-1]*n1*n2),[1:n1]')
idx(:) would be the column vector of linear indices with this new approach, which you can index into B i.e. B(idx(:)) to have the desired output as a column vector.

Related

Generalise indexing of a multi-dimensional array in Matlab

I want to generalise to any n the Matlab code below.
Let A be an n-dimensional array:
clear
rng default
n=4;
A=randn(n,n,n,n);
n=5;
A=randn(n,n,n,n,n);
Note that A is composed of n^(n-2) 2-dimensional matrices, each of size nxn.
For example, when n=4 these matrices are A(:,:,1,1),...,A(:,:,4,1),A(:,:,1,2),...,A(:,:,4,4).
Suppose I'm interested in a code which:
1) deletes the last column and row in each of the n^(n-2) 2-dimensional matrices
%when n=4
A(n,:,:,:)=[];
A(:,n,:,:)=[];
%when n=5
A(n,:,:,:,:)=[];
A(:,n,:,:,:)=[];
2) deletes the 2-dimensional matrices with the 3-th,4-th,5-th,n-th index equal to n.
%when n=4
A(:,:,n,:)=[];
A(:,:,:,n)=[];
%when n=5
A(:,:,n,:,:)=[];
A(:,:,:,n,:)=[];
A(:,:,:,:,n)=[];
Question: could you help me to generalise the code above to any n? I cannot see how to proceed.
You can index your matrix with a cell containing multiple elements. Each element will be interpreted as a new index (more information here):
%Example 1: A(:,:,1:3,1:3,1:3}
%elements per dimension
n = 4;
%number of dimension
d = 5;
%random matrix
repdim = repmat({n},d,1)
A = rand(repdim{:});
%We want A(:,:,1:3,1:3,1:3}, so we create c = {1:3,1:3,1:3}
c = repmat({1:n-1},d-2,1);
%Get the new matrix
A = A(:,:,c{:});
%Example 2: A(1:3,1:3,:,:,:}
%elements per dimension
n = 4;
%number of dimension
d = 5;
%random matrix
repdim = repmat({n},d,1)
A = rand(repdim{:});
%We want A(1:3,1:3,:,:,:}, so we create c1 = {1:3,1:3} and c2 = {':',':',':'}
c1 = repmat({1:n-1},2,1);
c2 = repmat({':'},d-2,1); %thanks to #LuisMendo for the suggestion.
%Get the new matrix
A = A(c1{:},c2{:});

How to find the location of the maximum element in a 3D MATLAB matrix?

I have a 3D matlab matrix of size 100*10*1344.
I want to find the three indices of the maximal element of the matrix.
When I try finding it with the command find, I get :
>> [i j k]=find(max(A(:))==A)
i =
52
j =
9601
k =
1
But using these indices gives the following result:
>> A(i ,j, k)
??? Index exceeds matrix dimensions.
How to solve the problem ??
You can't have find return three indices, only two. The third output is the value, not an index.
I suggest you get a single index, which will then be a linear index. You can use that directly into A, or convert to three indices with ind2sub.
Example:
A = rand(3,4,5); % example 2D array
ind = find(max(A(:))==A(:));
A(ind) % use linear index directly into A
[ii, jj, kk] = ind2sub(size(A), ind); % or convert to three indices...
A(ii, jj, kk) % ...and use them into A
Also, if you only need the first occurrence of the maximum (in case there are more than one), you can use the second output of max instead of find:
A = rand(3,4,5); % example 2D array
[~, ind] = max(A(:)); % second output of this function gives position of maximum
A(ind) % use linear index directly into A
[ii, jj, kk] = ind2sub(size(A), ind); % or convert to three indices...
A(ii, jj, kk) % ...and use them into A

MATLAB - Create psudorandom sparse matrix

Is there an easy way of making a 'random' sparse matrix with a specific number of nonzero entries?
Here is my attempt:
r = randperm(n,m) % n = size of matrix, m = number of nonzeros in each column
H = sparse(r, r,1,n,n);
But the matrix H doesn't have exactly m nonzeros in each column. For example if I use this to make a 100 x 100 matrix with 10 nonzeros in each column only 10 columns have exactly 10 1's in them.
I'm sure there's an easy way to do this but I can't see it.
This will generate a 100-by-100 matrix with exactly ten 1s per column:
n = 100;
m = 10;
nonzerosPerColumn = repmat(m, 1, n);
%%// Build vector of linear indices to nonzero entries
pos = cell2mat(arrayfun(#(i)randperm(n,nonzerosPerColumn(i))+(i-1)*n,1:n,'uni',0));
%%// Generate the matrix
M = reshape(sparse(pos,1,1,n*n,1),n,n);
Here's a vectorized approach:
r = 100; %// number of rows
c = 100; %// number of columns
m = 10; %// number of ones that there should be in each column
H = sparse([], [], [], r, c, c*m); %// preallocate and initiallize to zero
[~, ind] = sort(rand(r,c)); %// randomly generate...
ind = ind(1:m,:); %// ... m row indices per column
H(bsxfun(#plus, ind, (0:c-1)*r)) = 1; %// fill in ones, using linear indexing

Matlab: store array in matrix?

I have many array (n*1 dimension), how can I do something like
matrix = [];
for i = 1:5
for j =1:5
matrix (i,j) = zeros(n,1); % store a given array to a cell of a matrix
end
end
I find Array of Matrices in MATLAB
But this is store matrices into array, not the otherwise.
Ying Xiong's suggestion is what you want if the vectors are of different lengths. But assuming the number of elements is constant (which they seem to be) you may also use a 3-dimensional array, where each (i,j) element contains a vector in the third dimension, like this:
rows = 5; cols = 5; n = 10; %// Dimensions
matrix = zeros(rows, cols, n); %// Initialize matrix
vector = 1:n; %// Just an example
for ii = 1:rows %// Bad practice to use i as a variable name
for jj = 1:cols %// Bad practice to use j as a variable name
matrix(ii,jj,:) = vector; %// Assignment
end
end
Now each index (i,j) contains the vectors you want, for instance:
squeeze(matrix(1,1,:))
ans =
1
2
3
4
5
6
7
8
9
10
Having all values in a single matrix can be a good thing if you want to do similar operations on all elements, as vectorized approaches are usually very fast in MATLAB. You might want to check out permute, reshape and functions like bsxfun.
Note that you might be able to vectorize the loops, but without knowing the specifics, that's impossible to know.
You need to use cell array.
n = 10;
matrix = cell(5,5);
for i = 1:5
for j = 1:5
matrix{i,j} = zeros(n,1);
end
end

Matlab - Generating random coordinates for a matrix

I need to create a list (of size n) of random, non-repeating set of coordinates on a matrix of predefined size.
Is there a fast way to generate this in Matlab?
My initial idea was to create a list of size n with permutations the size of (width x length) and to translate them back to Row and Col values, but it seems to me too much.
Thanks,
Guy
You can use randperm to generate a linear index, and convert it to [row,col] if needed using ind2sub.
x = rand(7,9);
n = 20;
ndx = randperm(numel(x), n);
[row,col] = ind2sub(size(x), ndx);
As long as n is less than the number of elements in the matrix this is simple:
% A is the matrix to be sampled
% N is the number of coordinate pairs you want
numInMat = numel(A);
% sample from 1:N without replacement
ind = randperm(numInMat, N);
% convert ind to Row,Col pairs
[r, c] = ind2sub( size(A), ind )
Your idea is a good one, although you don't even have to convert your linear indices back to row and col indices, you can do linear indexing directly into a 2D array.
idx = randperm(prod(size(data)))
where data is your matrix. This will generate a vector of random integers between 1 and prod(size(data)), i.e. one index for each element.
e.g.
n = 3;
data = magic(n);
idx = randperm(prod(size(data)));
reshape(data(idx), size(data)) %this gives you your randomly indexed data matrix back