Sorting Coordinate Matrix in Matlab - 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.

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 to compare each rows of table

I want to compare every row of table with every other row of the same table to find row having lowest values.
F=table(a,b,c,d)
a=[1 2 1 3, 8 3 1 6]'
b=[3 2 1 3]'
c=[7 9 1 8]'
d=[4 6 1 6]'
How can I do it using for loop. Purposely third row is least valued row and it is the final answer. In big table, this is not known in advance.
Actually, I think your are mistakenly inverting columns and rows here. Anyway, let's make an example with both, using a matrix for the sake of simplicity.
Let's start with rows:
A = [
1 2 1 3;
8 3 1 6;
3 2 1 3;
7 9 1 8;
4 6 1 6
];
rows_sum = sum(A,2); % row-wise summation
[rows_sum_min,rows_sum_min_idx] = min(rows_sum);
The variable rows_sum_min will contain the minimum value (7 in this case) while the variable rows_sum_min_idx will contain the row indices that correspond to the minimum value (1 in this case, the first row).
And now the columns:
A = [
1 2 1 3;
8 3 1 6;
3 2 1 3;
7 9 1 8;
4 6 1 6
];
cols_sum = sum(A,1); % column-wise summation
[cols_sum_min,cols_sum_min_idx] = min(cols_sum);
The variable cols_sum_min will contain the minimum value (3 in this case) while the variable cols_sum_min_idx will contain the row indices that correspond to the minimum value (3 in this case, the third column).
I don't know why you want to to it with a for loop, since the main objective when coding with Matlab is trying to vectorize calculations as much as possible in order to obtain a superior performance. Anyway, if you really want a for loop... here is the example with columns:
A = [
1 2 1 3;
8 3 1 6;
3 2 1 3;
7 9 1 8;
4 6 1 6
];
[n,m] = size(A);
cols_sum = zeros(1,m);
for i = 1:n
cols_sum = cols_sum + A(i,:);
end
[cols_sum_min,cols_sum_min_idx] = min(cols_sum);
The result will be the same, you just consumed more time and cycles.

Find series of the same value

Given a vector A that contains a sequence of numbers.
The objective is to find all series (longer than a given number "threshold") that contain the same value. The result should be the position of both first and last values of that series.
Example: given a vector A where:
A = [1 1 1 2 1 3 3 3 1 1 1 1 1 4 3 2 2 2 2 2 2 2 3 4];
and a threshold B = 5;
The results would be:
[9 13] % a series contain only the number 1 with length equal to 5
[16 22] % a series contain only the number 2 with length equal to 7
A=[1 1 1 2 1 3 3 3 1 1 1 1 1 4 3 2 2 2 2 2 2 2 3 4];
B = 5;
[l c]= size(A); % to know the size of 'A'
K=1; % to define the length of the series
W=1; % a value used to save the positions of the wanted series.
For i=1:c-1
If A(i)==A(i+1)
K=k+1;
Else
If k>= B % to test of the actual series is equal or longer than the given threshold
S(w,1)=i;
S(w,2)= S(w,1)-k+1; % saving the first position and the last position of the series in 'S'
w=w+1;
end
k=1;
end
S % the final result which is a table contain all wanted series.
the result is as follow:
S 13 9 % 13: the last position of the wanted series and 9 is the first position
16 22
This one work soo good. But still... it is soo slow when its come to a big table.
A faster, vectorized option is to modify the approach from this solution for finding islands of zeroes:
A = [1 1 1 2 1 3 3 3 1 1 1 1 1 4 3 2 2 2 2 2 2 2 3 4]; % Sample data
B = 5; % Threshold
tsig = (diff(A) ~= 0);
dsig = diff([1 tsig 1]);
startIndex = find(dsig < 0);
endIndex = find(dsig > 0)-1;
duration = endIndex-startIndex+1;
stringIndex = (duration >= (B-1));
result = [startIndex(stringIndex); endIndex(stringIndex)+1].';
And the results:
result =
9 13
16 22

Matlab Matrix Repeat Value

my matrix:
e =
1 2
2 3
3 3
4 3
5 2
i want to repeat value from first coloumn as much as number from the second coloumn in the same row. i want to make my matrix to be like:
e =
1 2
1 2
2 3
2 3
2 3
3 3
3 3
3 3
4 3
4 3
4 3
5 2
5 2
thank you for your help...
You can use repelem to repeat the row indices and then grab those rows from e:
new_e = e(repelem(1:size(e,1), e(:,2)), :);
If you're using a MATLAB version prior to 2015a that doesn't have repelem, here's another way to do it:
spacing = cumsum([1; e(:,2)]); % the rows of new_e where we change row values
row_indices(spacing) = 1; % make a vector with these elements = 1
row_indices = cumsum(row_indices); % convert to row indices, last index is invalid
new_e = e(row_indices(1:end-1), :); % select valid rows from e

How can I go through the columns of a matrix in matlab and add them each to a specific column of a sum matrix in matlab?

Supose there is a Matrix
A =
1 3 2 4
4 2 5 8
6 1 4 9
and I have a Vector containing the "class" of each column of this matrix for example
v = [1 , 1 , 2 , 3]
How can I sum the columns of the matrix to a new matrix as column vectors each to the column of their class? In this example columns 1 and 2 of A would added to the first column of the new matrix, column 2 to the 3 to the 2nd, column 4 the the 3rd.
Like
SUM =
4 2 4
6 5 8
7 4 9
Is this possible without loops?
One of the perfect scenarios to combine the powers of accumarray and bsxfun -
%// Since we are to accumulate columns, first step would be to transpose A
At = A.' %//'
%// Create a vector of linear IDs for use with ACCUMARRAY later on
idx = bsxfun(#plus,v(:),[0:size(A,1)-1]*max(v))
%// Use ACCUMARRAY to accumulate rows from At, i.e. columns from A based on the IDs
out = reshape(accumarray(idx(:),At(:)),[],size(A,1)).'
Sample run -
A =
1 3 2 4 6 0
4 2 5 8 9 2
6 1 4 9 8 9
v =
1 1 2 3 3 2
out =
4 2 10
6 7 17
7 13 17
An alternative with accumarray in 2D. Generate a grid with the vector v and then apply accumarray:
A = A.';
v = [1 1 2 3];
[X, Y] = ndgrid(v,1:size(A,2));
Here X and Y look like this:
X =
1 1 1
1 1 1
2 2 2
3 3 3
Y =
1 2 3
1 2 3
1 2 3
1 2 3
Then apply accumarray:
B=accumarray([X(:) Y(:)],A(:)),
SUM = B.'
SUM =
4 2 4
6 5 8
7 4 9
As you see, using [X(:) Y(:)] create the following array:
ans =
1 1
1 1
2 1
3 1
1 2
1 2
2 2
3 2
1 3
1 3
2 3
3 3
in which the vector v containing the "class" is replicated 3 times since there are 3 unique classes that are to be summed up together.
EDIT:
As pointed out by knedlsepp you can get rid of the transpose to A and B like so:
[X2, Y2] = ndgrid(1:size(A,1),v);
B = accumarray([X2(:) Y2(:)],A(:))
which ends up doing the same. I find it a bit more easier to visualize with the transposes but that gives the same result.
How about a one-liner?
result = full(sparse(repmat(v,size(A,1),1), repmat((1:size(A,1)).',1,size(A,2)), A));
Don't optimize prematurely!
The for loop performs fine for your problem:
out = zeros(size(A,1), max(v));
for i = 1:numel(v)
out(:,v(i)) = out(:,v(i)) + A(:,i);
end
BTW: With fine, I mean: fast, fast, fast!