This question already has answers here:
General method to find submatrix in matlab matrix
(3 answers)
Closed 8 years ago.
I have the vector:
1 2 3
and the matrix:
4 1 2 3 5 5
9 8 7 6 3 1
1 4 7 8 2 3
I am trying to find a simple way of locating the vector [1 2 3] in my matrix.
A function returning either coordinates (Ie: (1,2:4)) or a matrix of 1s where there is a match a 0s where there isn't, Ie:
0 1 1 1 0 0
0 0 0 0 0 0
0 0 0 0 0 0
So far, the only function I've found is is 'ismember', which however only tells me if the individual components of the vector appear in the matrix. Suggestions?
Use strfind with a linearized version of the matrix, and then convert linear indices to subindices. Care should be taken to remove matches of the vector spanning different rows.
mat = [1 2 3 1 2 3 1 2;
3 0 1 2 3 5 4 4]; %// data
vec = [1 2 3]; %// data
ind = strfind(reshape(mat.',[],1).', vec);
[col row] = ind2sub(fliplr(size(mat)), ind);
keep = col<=size(mat,2)-length(vec)+1; %// remove result split across rows
row = row(keep);
col = col(keep);
Result for this example:
>> row, col
row =
1 1 2
col =
1 4 3
meaning the vector appears three times: row 1, col 1; row 1, col 4; row 2, col 3.
The result can be expressed in zero-one form as follows:
result = zeros(fliplr(size(mat)));
ind_ones = bsxfun(#plus, ind(keep).', 0:numel(vec)-1);
result(ind_ones) = 1;
result = result.';
which gives
>> result
result =
1 1 1 1 1 1 0 0
0 0 1 1 1 0 0 0
One way to get the starting location of the vector in the matrix is using colfilt:
>> A = [1 2 3 1 2 3 1 2; ...
3 0 1 2 3 5 4 4]; % matrix from Luis Mendo
>> T = [1 2 3];
>> colFun = #(x,t) all(x==repmat(t,1,size(x,2)),1);
>> B = colfilt(A,size(T),'sliding',colFun,T(:))
B =
0 1 0 0 1 0 0 0
0 0 0 1 0 0 0 0
That gives a mask of the center points, which translate to (row,col) coordinates:
>> [ii,jj]=find(B);
>> locs = bsxfun(#minus,[ii jj],floor((size(T)-1)/2))
locs =
1 1
2 3
1 4
Related
I have a matrix in MATLAB with zeroes and I would like to get another matrix with the first N non-zero elements in each row. Let's say for example N = 3, and the matrix is
A = [ 0 0 2 0 6 7 9;
3 2 4 7 0 0 6;
0 1 0 3 4 8 6;
1 2 0 0 0 1 3]
I'd like the result to be:
B = [2 6 7;
3 2 4;
1 3 4;
1 2 1]
I have a huge matrix so I would like to do it without a loop, could you please help me? Thanks a lot!
Since MATLAB stores a matrix according to column-major order, I first transpose A, bubble up the non-zeros, and pick the first N lines, and transpose back:
N = 3;
A = [ 0 0 2 0 6 7 9;
3 2 4 7 0 0 6;
0 1 0 3 4 8 6;
1 2 0 0 0 1 3];
Transpose and preallocate output B
At = A';
B = zeros(size(At));
At =
0 3 0 1
0 2 1 2
2 4 0 0
0 7 3 0
6 0 4 0
7 0 8 1
9 6 6 3
Index zeros
idx = At == 0;
idx =
1 0 1 0
1 0 0 0
0 0 1 1
1 0 0 1
0 1 0 1
0 1 0 0
0 0 0 0
Bubble up the non-zeros
B(~sort(idx)) = At(~idx);
B =
2 3 1 1
6 2 3 2
7 4 4 1
9 7 8 3
0 6 6 0
0 0 0 0
0 0 0 0
Select first N rows and transpose back
B(1:N,:)'
You can do the bubbling in row-major order, but you would need to retrieve the row and column subscripts with find, and do some sorting and picking there. It becomes more tedious and less readable.
Using accumarray with no loops:
N = 3;
[ii,jj] = find(A); [ii,inds]=sort(ii); jj = jj(inds);
lininds = ii+size(A,1)*(jj-1);
C = accumarray(ii,lininds,[],#(x) {A(x(1:N)')}); %' cell array output
B = vertcat(C{:})
B =
2 6 7
3 2 4
1 3 4
1 2 1
Usually I don't go with a for loop solution, but this is fairly intuitive:
N = 3;
[ii,jj] = find(A);
B = zeros(size(A,1),N);
for iRow = 1:size(A,1),
nzcols = jj(ii==iRow);
B(iRow,:) = A(iRow,nzcols(1:N));
end
Since you are guaranteed to have more than N nonzeros per row of A, that should get the job done.
One-liner solution:
B = cell2mat(cellfun(#(c) c(1:N), arrayfun(#(k) nonzeros(A(k,:)), 1:size(A,1), 'uni', false), 'uni', false)).'
Not terribly elegant or efficient, but so much fun!
N = 3;
for ii=1:size(A,1);
B(ii,:) = A( ii,find(A(ii,:),N) );
end
Actually , you can do it like the code blow:
N=3
for n=1:size(A,1)
[a b]=find(A(n,:)>0,N);
B(n,:)=A(n,transpose(b));
end
Then I think this B matrix will be what you want.
I have a matrix with some zero values I want to erase.
a=[ 1 2 3 0 0; 1 0 1 3 2; 0 1 2 5 0]
>>a =
1 2 3 0 0
1 0 1 3 2
0 1 2 5 0
However, I want to erase only the ones after the last non-zero value of each line.
This means that I want to retain 1 2 3 from the first line, 1 0 1 3 2 from the second and 0 1 2 5 from the third.
I want to then store the remaining values in a vector. In the case of the example this would result in the vector
b=[1 2 3 1 0 1 3 2 0 1 2 5]
The only way I figured out involves a for loop that I would like to avoid:
b=[];
for ii=1:size(a,1)
l=max(find(a(ii,:)));
b=[b a(ii,1:l)];
end
Is there a way to vectorize this code?
There are many possible ways to do this, here is my approach:
arotate = a' %//rotate the matrix a by 90 degrees
b=flipud(arotate) %//flips the matrix up and down
c= flipud(cumsum(b,1)) %//cumulative sum the matrix rows -and then flip it back.
arotate(c==0)=[]
arotate =
1 2 3 1 0 1 3 2 0 1 2 5
=========================EDIT=====================
just realized cumsum can have direction parameter so this should do:
arotate = a'
b = cumsum(arotate,1,'reverse')
arotate(b==0)=[]
This direction parameter was not available on my 2010b version, but should be there for you if you are using 2013a or above.
Here's an approach using bsxfun's masking capability -
M = size(a,2); %// Save size parameter
at = a.'; %// Transpose input array, to be used for masked extraction
%// Index IDs of last non-zero for each row when looking from right side
[~,idx] = max(fliplr(a~=0),[],2);
%// Create a mask of elements that are to be picked up in a
%// transposed version of the input array using BSXFUN's broadcasting
out = at(bsxfun(#le,(1:M)',M+1-idx'))
Sample run (to showcase mask usage) -
>> a
a =
1 2 3 0 0
1 0 1 3 2
0 1 2 5 0
>> M = size(a,2);
>> at = a.';
>> [~,idx] = max(fliplr(a~=0),[],2);
>> bsxfun(#le,(1:M)',M+1-idx') %// mask to be used on transposed version
ans =
1 1 1
1 1 1
1 1 1
0 1 1
0 1 0
>> at(bsxfun(#le,(1:M)',M+1-idx')).'
ans =
1 2 3 1 0 1 3 2 0 1 2 5
I'm attempting to find a critical point in a matrix. The value at index (i,j) should be greater than or equal to all elements in its row, and less than or equal to all elements in its column.
Here is what I have (it's off but I'm close):
function C = critical(A)
[nrow ncol] = size(A);
C = [];
for i = 1:nrow
for j = 1:ncol
if (A(i,j) >= A(i,1:end)) && (A(i,j) <= A(1:end,j))
C = [C ; A(i,j)]
end
end
end
You can use logical indexing.
minI = min(A,[],1);
maxI = max(A,[],2);
[row,col] = find(((A.'==maxI.').' & A==minI) ==1)
Details
Remember that Matlab is column major. We therefore transpose A and maxI.
A = [
3 4 1 1 2
2 4 2 1 4
4 3 2 1 2
3 3 1 1 1
2 3 0 2 1];
A.'==maxI.'
ans =
0 0 1 1 0
1 1 0 1 1
0 0 0 0 0
0 0 0 0 0
0 1 0 0 0
Then do the minimum
A==minI
ans =
0 0 0 1 0
1 0 0 1 0
0 1 0 1 0
0 1 0 1 1
1 1 1 0 1
And then multiply the two
((A.'==maxI.').' & A==minI)
ans =
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
0 1 0 0 0
0 1 0 0 0
Then find the rows and cols
[row,col] = find(((A.'==maxI.').' & A==minI) ==1)
row =
4
5
col =
2
2
Try this vectorised solution using bsxfun
function [ r,c,criP ] = critical( A )
%// finding the min and max values of each col & row resptly
minI = min(A,[],1);
maxI = max(A,[],2);
%// matching all the values of min & max for each col and row resptly
%// getting the indexes of the elements satisfying both the conditions
idx = find(bsxfun(#eq,A,maxI) & bsxfun(#eq,A,minI));
%// getting the corresponding values from the indexes
criP = A(idx);
%// Also getting corresponding row and col sub
[r,c] = ind2sub(size(A),idx);
end
Sample Run:
r,c should be a vector of equal length which represents the row and column subs of each Critical point. While val is a vector of same length giving the value of the critical point itself
>> A
A =
3 4 1 1 2
2 4 2 1 4
4 3 2 1 2
3 3 1 1 1
2 3 0 2 1
>> [r,c,val] = critical(A)
r =
4
5
c =
2
2
val =
3
3
I think there is a simpler way with intersect:
>> [~, row, col] = intersect(max(A,[],2), min(A));
row =
4
col =
2
UPDATE:
With intersect, in case you have multiple critical points, it will only give you the first one. To have all the indicies, there is also another simple way:
>> B
B =
3 4 1 4 2 5
2 5 2 4 4 4
4 4 2 4 2 4
3 4 1 4 1 4
2 5 4 4 4 5
>> row = find(ismember(max(B,[],2),min(B)))
row =
3
4
>> col = find(ismember(min(B),max(B,[],2)))
col =
2 4 6
Note that the set of critical points now should be the combination of row and col, means you have total 6 critical points in this example: (3,2),(4,2),(3,4),(4,4),(3,6),(4,6).
Here you can find how to export such combination.
Given A is symmetry matrix with size n and
A =
1 2 3 4 5 % The Position
1 [0 5 2 4 1
2 5 0 3 0 2
3 2 3 0 0 0
4 4 0 0 0 5
5 1 2 0 5 0]
B is a row vector that permute the matrix A row and column
B = [2 4 1 5 3]
The output that I want is
C =
2 4 1 5 3 % The New Position given by Matrix B
2 [0 0 5 2 3
4 0 0 4 5 0
1 5 4 0 1 2
5 2 5 1 0 0
3 3 0 2 0 0]
I can get the output by using simple for loop
index = [2,4,1,5,3];
C = zeros(5,5);
for i = 1:5
for j = 1:5
% Position of in square matrix n
% (i,j) = (i-1)*n + j
C(i,j) = A((index(i)-1)*5+index(j));
end
end
However, if I want to permute a matrix with size 80x80, then I need to run 1600 times in order to get the output.
Is there any simple trick to do it instead of using for loop?
You should be able to rearrange your matrices as follows:
C = A(index,index);
This rearranges each dimension according to the index variable independently.
i'm trying to find local maxima of a vector of numbers using MATLAB. The built-in findpeaks function will work for a vector such as:
[0 1 2 3 2 1 1 2 3 2 1 0]
where the peaks (each of the 3's) only occupy one position in the vector, but if I have a vector like:
[0 1 2 3 3 2 1 1 2 3 2 1 0]
the first 'peak' occupies two positions in the vector and the findpeaks function won't pick it up.
Is there a nice way to write a maxima-finding function which will detect these sort of peaks?
You can use the REGIONALMAX function from the Image Processing Toolbox:
>> x = [0 1 2 3 3 2 1 1 2 3 2 1 0]
x =
0 1 2 3 3 2 1 1 2 3 2 1 0
>> idx = imregionalmax(x)
idx =
0 0 0 1 1 0 0 0 0 1 0 0 0
Something much easier:
a = [1 2 4 5 5 3 2];
b = find(a == max(a(:)));
output:
b = [4,5]
a = [ 0 1 2 3 3 2 1 2 3 2 1 ];
sizeA = length(a);
result = max(a);
for i=1:sizeA,
if a(i) == result(1)
result(length(result) + 1) = i;
end
end
result contains the max, followed by all the values locations that are equal to max.