Find closest element with same value in matrix in matlab - matlab

Consider the following matrix:
0 3 0 1 1 4
1 3 5 6 7 0
2 5 6 2 6 1
4 4 2 1 5 1
When I specify the position of an element, I would like to obtain the position of the nearest element with the same value.For example, if I select the element in row 3,column 3, i.e. '6', I would like to obtain the the row and column values of the nearest '6',in this case, it is at row 2, column 4.And similarly, for the '1' at row 4,column 4, the nearest is at row 4,col 5 and row 4,col 6, any one of them is are fine.I have looked up the 'bwdist' and 'find' functions but they don't give the proper result.Can anyone help me on this?
Edit:
a1 = randi(10,10,5);
disp(a1);
%// For an array of search numbers
search_array = a1(4,5);
disp(search_array);
%%// Find the linear index of the location
[~,ind] = min(abs(bsxfun(#minus,a1(:),search_array')));%//'
%%// Convert the linear index into row and column numbers
[x,y] = ind2sub(size(a1),ind)
The 'min' function will not work here as each position where the required element is present will be converted to zero and 'min' scans the matrix row-wise and gives the position of the first zero.The following is such a case:
2 6 10 9 2
6 6 7 5 3
1 8 5 2 1
8 1 9 5 5
9 7 6 4 3
10 6 6 5 3
10 2 7 9 5
6 10 4 5 2
3 6 3 4 5
2 5 6 4 8
Even though there is a '5' right next to the '5' in row 4,col 5,the '5' in row 10, column 2 is selected.

Assuming A to be the input 2D matrix, this could be one approach -
%// Row and column indices of the "pivot"
row_id = 4;
col_id = 5;
%// Get the linear index from row and column indices
lin_idx = sub2ind(size(A),row_id,col_id)
%// Logical array with ones at places with same values
search_matches = false(size(A));
search_matches(A==A(lin_idx)) = 1;
%// Create a logical array with just a single 1 at the "pivot"
A_pivot = false(size(A));
A_pivot(lin_idx) = 1;
%// Use BWDIST to find out the distances from the pivot to all the places
%// in the 2D matrix. Set the pivot place and places with non-similar
%// values as Inf, so that later on MIN could be used to find the nearest
%// same values location
distmat = bwdist(A_pivot)
distmat(lin_idx) = Inf
distmat(~search_matches)=Inf
[~,min_lin_idx] = min(distmat(:))
[closest_row_idx,closest_col_idx] = ind2sub(size(A),min_lin_idx)

This approach doesn't require any toolbox. It returns [] if no other entry with the same value exists.
A = [0 3 0 1 1 4
1 3 5 6 7 0
2 5 6 2 6 1
4 4 2 1 5 1]; %// data matrix
pos_row = 3; %// row of reference element
pos_col = 3; %// col of reference element
ref = A(pos_row,pos_col); %// take note of value
A(pos_row,pos_col) = NaN; %// remove it, to avoid finding it as closest
[ii, jj] = find(A==ref); %// find all entries with the same value
A(pos_row,pos_col) = ref; %// restore value
d = (ii-pos_row).^2+ (jj-pos_col).^2; %// compute distances
[~, ind] = min(d); %// find arg min of distances
result_row = ii(ind); %// index with that to obtain result
result_col = jj(ind);

Related

Matlab: combining multiple matrices row-wise

I have some data in 10 matrices. Each matrix has a different number of rows, but the same number of columns.
I want to combine all 10 matrices to one matrix row-wise, interleaved, meaning the rows in that matrix will look like:
row 1 from matrix 0
...
row 1 from matrix 9
row 2 from matrix 0
...
row 2 from matrix 9
...
Example (with 3 matrices):
Matrix 1: [1 2 3 ; 4 5 6; 7 8 9]
Matrix 2: [3 2 1 ; 6 5 4]
Matrix 3: [1 1 1 ; 2 2 2 ; 3 3 3]
Combined matrix will be: [1 2 3 ; 3 2 1 ; 1 1 1 ; 4 5 6 ; 6 5 4 ; 2 2 2 ; 7 8 9 ; 3 3 3]
You can download the function interleave2 here https://au.mathworks.com/matlabcentral/fileexchange/45757-interleave-vectors-or-matrices
z = interleave2(a,b,c,'row')
you can see the way the function works in the source code of course
Here's a general solution that allows you to place however many matrices you want (with matching number of columns) into the starting cell array Result:
Result = {Matrix1, Matrix2, Matrix3};
index = cellfun(#(m) {1:size(m, 1)}, Result);
[~, index] = sort([index{:}]);
Result = vertcat(Result{:});
Result = Result(index, :);
This will generate an index vector 1:m for each matrix, where m is its number of rows. By concatenating these indices and sorting them, we can get a new index that can be used to sort the rows of the vertically-concatenated set of matrices so that they are interleaved.

How find rows and columns in matlab

I have variable matrix :
A = [1 2 8 8 1
4 6 8 1 1
5 3 1 1 8];
and I have variable B :
B=[2 3 1 8 8];
Question is how to find rows and columns (sort by rows) in variable A from variable B.
Example, first index in variable B is 2, and then I want to find value 2 in variable A and get to first rows and columns, and next process until index 5, but if rows and columns has been used so get second position (ex. index 4 & 5 having same value).
rows;
columns;
Result is:
rows = 1 3 1 1 1
columns = 2 2 1 3 4
Use can use find and sub2ind to achieve what you want
but for that you have to take transpose of your A first
A = [1 2 8 8 1
4 6 8 1 1
5 3 1 1 8];
B= [2 3 1 8 8];
TMP = A.';
for i = 1:length(B)
indx = find(TMP== B(i),1,'first') %Finding the element of B present in A
if(~isempty(indx )) % If B(i) is a member of A
[column(i),row(i)] = ind2sub(size(TMP),indx) % store it in row and column matrix
TMP(indx) = nan; % remove that element
end
end
column =
2 2 1 3 4
row =
1 3 1 1 1
As in one of the comments Usama suggested preallocation of memory
you can do that by using
row = zeros(1,sum(ismember(B,A)))
column= zeros(1,sum(ismember(B,A)))
The above code works even if there are some members of B not present in A
Use find. The function could return both a linear index or a row/col index.
Using linear index a solution could be
idx = zeros(size(B));
for i = 1:numel(B)
% Find all indexes
tmpIdx = find(A == B(i));
% Remove those already used
tmpIdx = setdiff(tmpIdx, idx);
% Get the first new unique
idx(i) = tmpIdx(1);
end
% Convert index to row and col
[rows, cols] = ind2sub(size(A),idx)
Giving:
rows = 1 3 1 1 2
cols = 2 2 1 3 3
Note that as the linear indexing goes down column by column, the result here differs from the one in your example (although still a correct index)
rows = 1 3 1 1 1
columns= 2 2 1 3 4
But to get this you could just transpose the A matrix (A.') and flip the rows and cols (the result from ind2sub)
Here is on solution where I use for loop, I tried to optimize the number of iteration and the computational cost. If there is no corresponding value between B and A the row/col index return NaN.
[Bu,~,ord] = unique(B,'stable');
% Index of each different values
[col,row] = arrayfun(#(x) find(A'==x),Bu,'UniformOutput',0)
% For each value in vector B we search the first "non already used" corresponding value in A.
for i = 1:length(B)
if ~isempty(row{ord(i)})
r(i) = row{ord(i)}(1);
row{ord(i)}(1) = [];
c(i) = col{ord(i)}(1);
col{ord(i)}(1) = [];
else
r(i) = NaN;
c(i) = NaN;
end
end
RESULT:
c = [2 2 1 3 4]
r = [1 3 1 1 1]

Find two MAXIMUM values' position in 3D matrix (MATLAB)

I have been having problem with identifying two maximum values' position in 3D matrix (MATLAB). Say I have matrix A output as follows:
A(:,:,1) =
5 3 5
0 1 0
A(:,:,2) =
0 2 0
8 0 8
A(:,:,3) =
3 0 0
0 7 7
A(:,:,4) =
6 6 0
4 0 0
For the first A(:,:,1), I want to identify that the first row have the highest value (A=5). But I need the two index position, which in this case, 1 and 3. And this is the same as the other A(:,:,:).
I have searched through SO but since I am bad in MATLAB, I couldn't find way to work this through.
Please do help me on this. It would be better if I don't need to use for loop to get the desired output.
Shot #1 Finding the indices for maximum values across each 3D slice -
%// Reshape A into a 2D matrix
A_2d = reshape(A,[],size(A,3))
%// Find linear indices of maximum numbers for each 3D slice
idx = find(reshape(bsxfun(#eq,A_2d,max(A_2d,[],1)),size(A)))
%// Convert those linear indices to dim1, dim2,dim3 indices and
%// present the final output as a Nx3 array
[dim1_idx,dim2_idx,dim3_idx] = ind2sub(size(A),idx)
out_idx_triplet = [dim1_idx dim2_idx dim3_idx]
Sample run -
>> A
A(:,:,1) =
5 3 5
0 1 0
A(:,:,2) =
0 2 0
8 0 8
A(:,:,3) =
3 0 0
0 7 7
A(:,:,4) =
6 6 0
4 0 0
out_idx_triplet =
1 1 1
1 3 1
2 1 2
2 3 2
2 2 3
2 3 3
1 1 4
1 2 4
out_idx_triplet(:,2) is what you are looking for!
Shot #2 Finding the indices for highest two numbers across each 3D slice -
%// Get size of A
[m,n,r] = size(A)
%// Reshape A into a 2D matrix
A_2d = reshape(A,[],r)
%// Find linear indices of highest two numbers for each 3D slice
[~,sorted_idx] = sort(A_2d,1,'descend')
idx = bsxfun(#plus,sorted_idx(1:2,:),[0:r-1]*m*n)
%// Convert those linear indices to dim1, dim2,dim3 indices
[dim1_idx,dim2_idx,dim3_idx] = ind2sub(size(A),idx(:))
%// Present the final output as a Nx3 array
out_idx_triplet = [dim1_idx dim2_idx dim3_idx]
out_idx_triplet(:,2) is what you are looking for!
The following code gives you the column and row of the respective maximum.
The first step will obtain the maximum of each sub-matrix containing the first and second dimension. Since max works per default with the first dimension, the matrix is reshaped to combine the original first and second dimension.
max_vals = max(reshape(A,size(A,1)*size(A,2),size(A,3)));
max_vals =
5 8 7 6
In the second step, the index of elements equal to the respective max_vals of each sub-matrix is obtained using arrayfun over the third dimension. Since the output of arrayfun are cells, cell2mat is used to transform the output into a matrix. As a last step, the linear index from find is transformed into sub-indices by ind2sub.
[i,j] = ind2sub(size(A(:,:,1)),cell2mat(arrayfun(#(i)find(A(:,:,i)==max_vals(i)),1:size(A,3),'UniformOutput',false)))
i =
1 2 2 1
1 2 2 1
j =
1 1 2 1
3 3 3 2
Hence, the values in j are the ones you want to have.

Sorting Coordinate Matrix in Matlab

In Matlab I have a big matrix containing the coordinates (x,y,z) of many points (over 200000). There is an extra column used as identification. I have written this code in order to sort all coordinate points. My final goal is to find duplicated points (rows with same x,y,z). After sorting the coordinate points I use the diff function, two consecutive rows of the matrix with the same coordinates will take value [0 0 0], and then with ismember I can find which rows of that matrix resulting from applying "diff" have the [0 0 0] row. With the indices returned from ismember I can find which points are repeated.
Back to my question...This is the code I wrote to sort properly my coordintes+id matrix. I guess It could be done better. Any suggestion?
%coordinates are always positive
a=[ 1 2 8 4; %sample matrix
1 0 5 6;
2 4 7 1;
3 2 1 0;
2 3 5 0;
3 1 2 8;
1 2 4 8];
b=a; %for checking purposes
%sorting first column
a=sortrows(a,1);
%sorting second column
for i=0:max(a(:,1));
k=find(a(:,1)==i);
if not(isempty(k))
a(k,:)=sortrows(a(k,:),2);
end
end
%Sorting third column
for i=0:max(a(:,2));
k=find(a(:,2)==i);
if not(isempty(k))
%identifying rows with same value on first column
for j=1:length(k)
[rows,~] = ismember(a(:,1:2), [ a(k(j),1),i],'rows');
a(rows,3:end)=sortrows(a(rows,3:end),1);
end
end
end
%Checking that rows remain the same
m=ismember(b,a,'rows');
if length(m)~=sum(m)
disp('Error while sorting!');
end
Why don't you just use unique?
[uniqueRows, ii, jj] = unique(a(:,1:3),'rows');
Example
a = [1 2 3 5
3 2 3 6
1 2 3 9
2 2 2 8];
gives
uniqueRows =
1 2 3
2 2 2
3 2 3
and
jj =
1
3
1
2
meaning third row equals first row.
If you need the full unique rows, including the fourth column: use ii to index a:
fullUniqueRows = a(ii,:);
which gives
fullUniqueRows =
1 2 3 9
2 2 2 8
3 2 3 6
Trying to sort a based on the fourth column? Do this -
a=[ 1 2 8 4; %sample matrix
1 0 5 6;
2 4 7 1;
3 2 1 0;
2 3 5 0;
3 2 1 8;
1 2 4 8];
[x,y] = sort(a(:,4))
sorted_a=a(y,:)
Trying to get the row indices having repeated x-y-z coordinates being represented by the first three columns? Do this -
out = sum(squeeze(all(bsxfun(#eq,a(:,1:3),permute(a(:,1:3),[3 2 1])),2)),2)>1
and use it similarly for sorted_a.

average 3rd column when 1st and 2nd column have same numbers

just lets make it simple, assume that I have a 10x3 matrix in matlab. The numbers in the first two columns in each row represent the x and y (position) and the number in 3rd columns show the corresponding value. For instance, [1 4 12] shows that the value of function in x=1 and y=4 is equal to 12. I also have same x, and y in different rows, and I want to average the values with same x,y. and replace all of them with averaged one.
For example :
A = [1 4 12
1 4 14
1 4 10
1 5 5
1 5 7];
I want to have
B = [1 4 12
1 5 6]
I really appreciate your help
Thanks
Ali
Like this?
A = [1 4 12;1 4 14;1 4 10; 1 5 5;1 5 7];
[x,y] = consolidator(A(:,1:2),A(:,3),#mean);
B = [x,y]
B =
1 4 12
1 5 6
Consolidator is on the File Exchange.
Using built-in functions:
sparsemean = accumarray(A(:,1:2), A(:,3).', [], #mean, 0, true);
[i,j,v] = find(sparsemean);
B = [i.' j.' v.'];
A = [1 4 12;1 4 14;1 4 10; 1 5 5;1 5 7]; %your example data
B = unique(A(:, 1:2), 'rows'); %find the unique xy pairs
C = nan(length(B), 1);
% calculate means
for ii = 1:length(B)
C(ii) = mean(A(A(:, 1) == B(ii, 1) & A(:, 2) == B(ii, 2), 3));
end
C =
12
6
The step inside the for loop uses logical indexing to find the mean of rows that match the current xy pair in the loop.
Use unique to get the unique rows and use the returned indexing array to find the ones that should be averaged and ask accumarray to do the averaging part:
[C,~,J]=unique(A(:,1:2), 'rows');
B=[C, accumarray(J,A(:,3),[],#mean)];
For your example
>> [C,~,J]=unique(A(:,1:2), 'rows')
C =
1 4
1 5
J =
1
1
1
2
2
C contains the unique rows and J shows which rows in the original matrix correspond to the rows in C then
>> accumarray(J,A(:,3),[],#mean)
ans =
12
6
returns the desired averages and
>> B=[C, accumarray(J,A(:,3),[],#mean)]
B =
1 4 12
1 5 6
is the answer.