Maximum of a subset of array (MATLAB) - 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);

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, :);

Matlab - indexing

I have a matrix A which is 21x1 and contains only ones and twos.
Then I have a matrix B which is 6 * 600 matrix of numbers ranging between 0 and 21.
I want to generate a matrix C which is 6 * 600 matrix containing ones and twos such that:
If B matrix has a zero, matrix C should have a zero on that place. If B matrix has number 5, then matrix C should have the element on row 5 of matrix A and so on and so forth.
Please let me know if this is not clear.
Let us generate some sample inputs:
A = randi(2,21,1);
B = randi(22,6,600)-1;
The output C will then be:
C = B*0; %// preallocation + take care of the elements that need to be 0
C(B>0) = A(B(B>0)); %// logical indexing
The explanation of the 2nd line is as follows:
RHS
B>0 - return a logical array the size of B which has the meaning of whether this specific element of B is larger-than-0 value.
B(B>0) - return the elements of B for which there are true values in B>0 (i.e. numbers that can be used to index into A).
A(...) - return the elements of A that correspond to the valid indices from B.
% Generate matrices fitting the description
A = round(rand(21,1))+1;
B = round(rand(6,600)*21);
C = zeros(6,600);
% Indexing impossible since zeroes cannot be used as index. So treat per element using linear indexing.
for ii = 1:(6*600)
if B(ii) == 0
C(ii) = 0;
else
C(ii) = A(B(ii));
end
end
Although the piece of code could be optimized further this is the most clear way of creating understanding and speed is not needed if it's only this small matrix evaluated a limited number of times.

MATLAB function that gives all the positive integers in a column vector

I need to create a function that has the input argument n, a integer , n>1 , and an output argument v, which is a column vector of length n containing all the positive integers smaller than or equal to n, arranged in such a way that no element of the vector equals its own index.
I know how to define the function
This is what I tried so far but it doesn't work
function[v]=int_col(n)
[1,n] = size(n);
k=1:n;
v=n(1:n);
v=k'
end
Let's take a look at what you have:
[1,n] = size(n);
This line doesn't make a lot of sense: n is an integer, which means that size(n) will give you [1,1], you don't need that. (Also an expression like [1,n] can't be on the left hand side of an assignment.) Drop that line. It's useless.
k=1:n;
That line is pretty good, k is now a row vector of size n containing the integers from 1 to n.
v=n(1:n);
Doesn't make sense. n isn't a vector (or you can say it's a 1x1 vector) either way, indexing into it (that's what the parentheses do) doesn't make sense. Drop that line too.
v=k'
That's also a nice line. It makes a column vector v out of your row vector k. The only thing that this doesn't satisfy is the "arranged in such a way that no element of the vector equals its own index" part, since right now every element equals its own index. So now you need to find a way to either shift those elements or shuffle them around in some way that satisfies this condition and you'd be done.
Let's give a working solution. You should really look into it and see how this thing works. It's important to solve the problem in smaller steps and to know what the code is doing.
function [v] = int_col(n)
if n <= 1
error('argument must be >1')
end
v = 1:n; % generate a row-vector of 1 to n
v = v'; % make it a column vector
v = circshift(v,1); % shift all elements by 1
end
This is the result:
>> int_col(5)
ans =
5
1
2
3
4
Instead of using circshift you can do the following as well:
v = [v(end);v(1:end-1)];

How to vectorize this Matlab loop

I need some help to vectorize the following operation since I'm a little confused.
So, I have a m-by-2 matrix A and n-by-1 vector b. I want to create a n-by-1 vector c whose entries should be the values of the second column of A whose line is given by the line where the correspondent value of b would fall...
Not sure if I was clear enough. Anyway, the code below does compute c correctly so you can understand what is my desired output. However, I want to vectorize this function since my real n and m are in the order of many thousands.
Note that values of bare non-integer and not necessarily equal to any of those in the first column of A (these ones could be non-integers too!).
m = 5; n = 10;
A = [(0:m-1)*1.1;rand(1,m)]'
b = (m-1)*rand(n,1)
[bincounts, ind] = histc(b,A(:,1))
for i = 1:n
c(i) = A(ind(i),2);
end
All you need is:
c = A(ind,2);

How can I extract n elements from each m-by-m window of a matrix without using loops in MATLAB?

I have a matrix and 2 parameters. The first parameter n is the number of elements to be selected. The second is the window size m.
I want to select n number of elements from every m-by-m window from the matrix. As a result we will have a p-by-q cell array where p is matrix_height/m and q is matrix_width/m.
Each element of the cell array contains the n greatest numbers from the corresponding window. It does not necessarily need to be a cell array, it can be anything that will store the necessary data.
One way to do this is by first finding all the unique m-by-m submatrices of your matrix using the function IM2COL, then sorting each column in descending order using the function SORT, and finally extracting the top n rows. If your initial matrix is A and your output matrix is B, this is what it would look like:
B = sort(im2col(A,[m m],'distinct'),1,'descend');
B = B(1:n,:); %# Get the top n values
Note that B will be an n-by-m^2 matrix. If you want to turn this into a p-by-q cell array, you can do this using the functions NUM2CELL and RESHAPE:
nBlocks = ceil(size(A)./m); %# The number of blocks in each dimension
B = reshape(num2cell(B,1),nBlocks(1),nBlocks(2));
EDIT:
If you also want to get the indices of each value with respect to the input matrix A, that is a bit more complicated. You can do it by getting the second output from SORT, which in this case will be the linear indices of the values within each m-by-m submatrix. You can convert these to subscripts using the function IND2SUB, then shift the row and column indices to account for the position of each m-by-m block:
[B,index] = sort(im2col(A,[m m],'distinct'),1,'descend');
B = B(1:n,:); %# Get the top n values
index = index(1:n,:); %# Get the top n values
[r,c] = ind2sub([m m],index); %# Convert linear indices to subscripts
nBlocks = size(A)./m; %# The number of blocks in each dimension
r = r+repmat(0:m:(nBlocks(1)-1)*m,n,nBlocks(2)); %# Shift the row indices
c = c+kron(0:m:(nBlocks(2)-1)*m,ones(n,nBlocks(1))); %# Shift the column indices
And now you can collect the row indices, column indices, and values together into a cell array using the functions MAT2CELL and RESHAPE:
B = mat2cell([r(:) c(:) B(:)],n.*ones(1,size(B,2)));
B = reshape(B,nBlocks(1),nBlocks(2));
Alternatively, you can create a structure array instead of a cell array using the functions NUM2CELL, STRUCT, and RESHAPE:
B = struct('rowIndices',num2cell(r,1),...
'colIndices',num2cell(c,1),...
'values',num2cell(B,1));
B = reshape(B,nBlocks(1),nBlocks(2));
NOTE:
The function IM2COL will pad partial blocks with zeroes in the event that a dimension of A is not an even multiple of m. If any of this zero-padding appears in the top n values for a block, one or both of the corresponding row and column indices will be out of range (i.e. have a value larger than the size of that dimension for the matrix A). Thus by checking that the row and column indices are in range you can make sure you are not including any of the zero-padding in your subsequent analysis.