MATLAB: extract submatrix with logical indexing - matlab

I'm looking for an elegant solution to this very simple problem in MATLAB. Suppose I have a matrix
>> M = magic(5)
M =
17 24 1 8 15
23 5 7 14 16
4 6 13 20 22
10 12 19 21 3
11 18 25 2 9
and a logical variable of the form
I =
0 0 0 0 0
0 1 1 0 0
0 1 1 0 0
0 0 0 0 0
0 0 0 0 0
If I try to retrieve the elements of M associated to 1 values in I, I get a column vector
>> M(I)
ans =
5
6
7
13
What would be the simplest way to obtain the matrix [5 7 ; 6 13] from this logical indexing?
If I know the shape of the non-zero elements of I, I can use a reshape after the indexing, but that's not a general case.
Also, I'm aware that the default behavior for this type of indexing in MATLAB enforces consistency with respect to the case in which non-zero values in I do not form a matrix, but I wonder if there is a simple solution for this particular case.

This is a one way to do this. It is assumed that all rows of I have same number of ones. It is also assumed that all columns of I have same number have ones, because Submatrix must be rectangular.
%# Define the example data.
M = magic(5);
I = zeros(5);
I(2:3, 2:3) = 1;
%# Create the Submatrix.
Submatrix = reshape(M(find(I)), max(sum(I)), max(sum(I')));

Here is a very simple solution:
T = I(any(I'),any(I));
T(:) = M(I);

M = magic(5);
I = [ ... ];
ind = find(I); %# find indices of ones in I
[y1, x1] = ind2sub(size(M), ind(1)); %# get top-left position
[y2, x2] = ind2sub(size(M), ind(end)); %# get bottom-right position
O = M(y1:y2, x1:x2); %# copy submatrix

Related

How to find minimum of each column in MATLAB and sum op all elements of that column?

I have a 300x178 matrix and I want to find the minimum of each column of that matrix, i.e. resulting in a 1x178 array. Then I want to store the sum of all elements but the minimum in each column in the 300x178 matrix on the location/pixel of the minimum value, leaving all other elements zero. How can I do this using MATLAB?
Example:
1 4 6 3
2 6 7 4
5 1 5 7
becomes:
1 0 0 1
0 0 0 0
0 1 1 0
and eventually:
8 0 0 14
0 0 0 0
0 11 18 0
Your example and title do not correspond to the question text. Your example sums all values in a column and stores them at the location of the minimum, which the title also asks. You can do this by making smart use of sub2ind:
A = [1 4 6 3
2 6 7 4
5 1 5 7];
C = zeros(size(A));
[tmp, idx] = min(A); % find the locations of minima
% one liner to store the sum of columns
C(sub2ind(size(A), idx, 1:size(A,2))) = sum(A,1);
C =
8 0 0 14
0 0 0 0
0 11 18 0
If, on the other hand, you're after what your question text asks about, subsequently subtract A on the minimum locations using the same sub2ind trick:
C(sub2ind(size(A), idx, 1:size(A,2))) = C(sub2ind(size(A), idx, 1:size(A,2))) - A(sub2ind(size(A), idx, 1:size(A,2)))
C =
7 0 0 11
0 0 0 0
0 10 13 0
This way you get the sum of all elements but the minimum.
For an in-depth explanation what sub2ind does, read this fantastic Q/A by Luis Mendo keeping in mind that in A(2,3) the 2 and 3 are called subscripts, which, in case of a 3-by-4 matrix, translates to linear index 8.
I cannot test this on my R2007b, but according to the documentation on min you could use [M, I] = min(A, [], 1, 'linear') to get the linear indices into I directly, without going through sub2ind:
C = zeros(size(A));
[tmp, idx] = min(A, [], 1, 'linear');
C(idx) = sum(A, 1);
% Optional, to sum all but the minimum
C(idx) = C(idx) - A(idx);
Small note from the documentation on the occurrence of multiple same-valued minima in your original matrix:
If the smallest element occurs more than once, then I contains the index to the first occurrence of the value.

Find last true element of columns

I'd like to extract one value per column of a matrix using a condition. Multiple values on each column match that condition, but only the last one should be selected. It is safe to assume that each row contains at least one such value.
So given an NxM matrix and an equally-sized boolean, extract M values for which the boolean is true and it is the last true value in a column. For example:
m = magic(4);
i = (m > 10);
% m =
% 16 2 3 13
% 5 11 10 8
% 9 7 6 12
% 4 14 15 1
% i =
% 1 0 0 1
% 0 1 0 0
% 0 0 0 1
% 0 1 1 0
And the expected output:
% i_ =
% 1 0 0 0
% 0 0 0 0
% 0 0 0 1
% 0 1 1 0
% x = [16, 14, 15, 12]
I know this could be easily achieved by looping through the columns and using find, but in my experience there often are better ways of formulating these problems.
This would do it
m(max(i.*reshape([1:numel(m)],size(m))))
Explanation
So we are generating an array of indices
reshape([1:numel(m)],size(m))
ans =
1 5 9 13
2 6 10 14
3 7 11 15
4 8 12 16
That represents the indices for each value. The we multiply that with I to get the values we are interested in
i.*reshape([1:numel(m)],size(m))
ans =
1 0 0 13
0 6 0 0
0 0 0 15
0 8 12 0
Then we do a max on that since max works on columns. This will give us the last index in each column.
max(i.*reshape([1:numel(m)],size(m)))
ans =
1 8 12 15
Then apply those indices on m to get the values
m(max(i.*reshape([1:numel(m)],size(m))))
ans =
16 14 15 12
You can use the second output of max to find the last true element of each column. Before that the logical matrx should be multiplied by an increasing column vector.
[~, idx] = max((1:size(i, 1)).' .* i, [], 1, 'linear') ;
x = m(idx) ;
Here's another way, using accumarray:
[~, col] = find(i); % column indices
lin = find(i); % linear indices
x = accumarray(col, m(lin), [], #(x) x(end));

Position of integers in vector

I think the question is pretty basic, but still keeps me busy since some time.
Lets assume we have a vector containing 4 integers randomly repetetive, like:
v = [ 1 3 3 3 4 2 1 2 3 4 3 2 1 4 3 3 4 2 2]
I am searching for the vector of all positions of each integer, e.g. for 1 it should be a vector like:
position_one = [1 7 13]
Since I want to search every row of a 100x10000 matrix I was not able to deal with linear indeces.
Thanks in advance!
Rows and columns
Since your output for every integer changes, a cell array will fit the whole task. For the whole matrix, you can do something like:
A = randi(4,10,30); % some data
Row = repmat((1:size(A,1)).',1,size(A,2)); % index of the row
Col = repmat((1:size(A,2)),size(A,1),1); % index of the column
pos = #(n) [Row(A==n) Col(A==n)]; % Anonymous function to find the indices of 'n'
than for every n you can write:
>> pos(3)
ans =
1 1
2 1
5 1
6 1
9 1
8 2
3 3
. .
. .
. .
where the first column is the row, and the second is the column for every instance of n in A.
And for all ns you can use an arrayfun:
positions = arrayfun(pos,1:max(A(:)),'UniformOutput',false) % a loop that goes over all n's
or a simple for loop (faster):
positions = cell(1,max(A(:)));
for n = 1:max(A(:))
positions(n) = {pos(n)};
end
The output in both cases would be a cell array:
positions =
[70x2 double] [78x2 double] [76x2 double] [76x2 double]
and for every n you can write positions{n}, to get for example:
>> positions{1}
ans =
10 1
2 3
5 3
3 4
5 4
1 5
4 5
. .
. .
. .
Only rows
If all you want in the column index per a given row and n, you can write this:
A = randi(4,10,30);
row_pos = #(k,n) A(k,:)==n;
positions = false(size(A,1),max(A(:)),size(A,2));
for n = 1:max(A(:))
positions(:,n,:) = row_pos(1:size(A,1),n);
end
now, positions is a logical 3-D array, that every row corresponds to a row in A, every column corresponds to a value of n, and the third dimension is the presence vector for the combination of row and n. this way, we can define R to be the column index:
R = 1:size(A,2);
and then find the relevant positions for a given row and n. For instance, the column indices of n=3 in row 9 is:
>> R(positions(9,3,:))
ans =
2 6 18 19 23 24 26 27
this would be just like calling find(A(9,:)==3), but if you need to perform this many times, the finding all indices and store them in positions (which is logical so it is not so big) would be faster.
Find linear indexes in a matrix: I = find(A == 1).
Find two dimensional indexes in matrix A: [row, col] = find(A == 1).
%Create sample matrix, with elements equal one:
A = zeros(5, 4);
A([2 10 14]) = 1
A =
0 0 0 0
1 0 0 0
0 0 0 0
0 0 1 0
0 1 0 0
Find ones as linear indexes in A:
find(A == 1)
ans =
2
10
14
%This is the same as reshaping A to a vector and find ones in the vector:
B = A(:);
find(B == 1);
B' =
0 1 0 0 0 0 0 0 0 1 0 0 0 1 0 0 0 0 0 0
Find two dimensional indexes:
[row, col] = find(A == 1)
row =
2
5
4
col =
1
2
3
You can do that with accumarray using an anonymous function as follows:
positions = accumarray(v(:), 1:numel(v), [], #(x) {sort(x.')});
For
v = [ 1 3 3 3 4 2 1 2 3 4 3 2 1 4 3 3 4 2 2];
this gives
positions{1} =
1 7 13
positions{2} =
6 8 12 18 19
positions{3} =
2 3 4 9 11 15 16
positions{4} =
5 10 14 17

creating diagonal matrix with selected elements

I have a 4x5 matrix called A from which I want to select randomly 3 rows, then 4 random columns and then select those elements which coincide in those selected rows and columns so that I have 12 selected elements.Then I want to create a diagonal matrix called B which will have entries either 1 or 0 so that multiplication of that B matrix with reshaped A matrix (20x1) will give me those selected 12 elements of A.
How can I create that B matrix? Here is my code:
A=1:20;
A=reshape(A,4,5);
Mr=4;
Ma=3;
Na=4;
Nr=5;
M=Ma*Mr;
[S1,S2]=size(A);
N=S1*S2;
y2=zeros(size(A));
k1=randperm(S1);
k1=k1(1:Ma);
k2=randperm(S2);
k2=k2(1:Mr);
y2(k1,k2)=A(k1,k2);
It's a little hard to understand what you want and your code isn't much help but I think I've a solution for you.
I create a matrix (vector) of zeros of the same size as A and then use bsxfun to determine the indexes in this vector (which will be the diagonal of B) that should be 1.
>> A = reshape(1:20, 4, 5);
>> R = [1 2 3]; % Random rows
>> C = [2 3 4 5]; % Random columns
>> B = zeros(size(A));
>> B(bsxfun(#plus, C, size(A, 1)*(R-1).')) = 1;
>> B = diag(B(:));
>> V = B*A(:);
>> V = V(V ~= 0)
V =
2
3
4
5
6
7
8
9
10
11
12
13
Note: There is no need for B = diag(B(:)); we could have simply used element by element multiplication in Matlab.
>> V = B(:).*A(:);
>> V = V(V ~= 0)
Note: This may be overly complex or very poorly put together and there is probably a better way of doing it. It's my first real attempt at using bsxfun on my own.
Here is a hack but since you are creating y2 you might as well just use it instead of creating the useless B matrix. The bsxfun answer is much better.
A=1:20;
A=reshape(A,4,5);
Mr=4;
Ma=3;
Na=4;
Nr=5;
M=Ma*Mr;
[S1,S2]=size(A);
N=S1*S2;
y2=zeros(size(A));
k1=randperm(S1);
k1=k1(1:Ma);
k2=randperm(S2);
k2=k2(1:Mr);
y2(k1,k2)=A(k1,k2);
idx = reshape(y2 ~= 0, numel(y2), []);
B = diag(idx);
% "diagonal" matrix 12x20
B = B(all(B==0,2),:) = [];
output = B * A(:)
output =
1
3
4
9
11
12
13
15
16
17
19
20
y2 from example.
y2 =
1 0 9 13 17
0 0 0 0 0
3 0 11 15 19
4 0 12 16 20

Matlab , matrix operations

I have for example this matrix
A=[
11 15 19 13
12 16 0 114
13 17 111 115
14 18 112 116
];
I want to find nonzero elements of two matrix of indices:
i1=[1 3];
i2=[2 4];
The result:
B=A(i2,i1);
B=[12 0
14 112];
index of matrix in A.
index=[2 4 12];
So, How to obtain the indices without loop?
Thanks.
There is a one-liner which is not quite readable of course:
index = find(diag(ismember(1:size(A,1), i2))*A*diag(ismember(1:size(A,2), i1)));
or alternatively
index=find(sparse(i2,i2,1,size(A,1),size(A,1))*A*sparse(i1,i1,1,size(A,2),size(A,2)));
and there is more elaborate and readable one:
z=zeros(size(A));
z(i2,i1) = A(i2,i1);
index=find(z);
Note that the first one-liner fails if the matrix contains Inf or NaN values because those values will be multiplied by zero, the second and third methods are more robust in that sense.
This is one solution:
% sub2ind does not work, use this hack instead
z = zeros(size(A));
z(i2,i1) = 1
ind = find(z) % get linear indices
%only keep the ones for which A is nonzero
ind = ind(A(ind) ~= 0)
Result:
z =
0 0 0 0
1 0 1 0
0 0 0 0
1 0 1 0
ind =
2
4
10
12
ind =
2
4
12
Slightly more compact than Bas Swinckels answer:
I=reshape(1:numel(A),size(A));
J=I(i2,i1);
J(~~B)