How to compare vector with a value in matlab? - matlab

I have six node (1-6). I have two matrices, "x" and "distance" given below. The distance matrix represents the distance of all the nodes from node 3.
x=[1 2 4 6];
distance=[110 115 0 16 10 2];
I want to make an if condition that should take some action if the distance of any of the nodes in matrix x from node 3 is greater than 13. I tried the following but it doesn't work.
if distance(1,x(1:4))<13
c=222;
end
I can do it using for loop, but i need a shorter command. Regards.

Since the output of distance(1,x(1:end)) < 13 is a vector, your code doesn't work, you can do this:
if ( sum(distance(x) < 13) > 0 )
c = 222;
end

Related

Finding equal rows in Matlab

I have a matrix suppX in Matlab with size GxN and a matrix A with size MxN. I would like your help to construct a matrix Xresponse with size GxM with Xresponse(g,m)=1 if the row A(m,:) is equal to the row suppX(g,:) and zero otherwise.
Let me explain better with an example.
suppX=[1 2 3 4;
5 6 7 8;
9 10 11 12]; %GxN
A=[1 2 3 4;
1 2 3 4;
9 10 11 12;
1 2 3 4]; %MxN
Xresponse=[1 1 0 1;
0 0 0 0;
0 0 1 0]; %GxM
I have written a code that does what I want.
Xresponsemy=zeros(size(suppX,1), size(A,1));
for x=1:size(suppX,1)
Xresponsemy(x,:)=ismember(A, suppX(x,:), 'rows').';
end
My code uses a loop. I would like to avoid this because in my real case this piece of code is part of another big loop. Do you have suggestions without looping?
One way to do this would be to treat each matrix as vectors in N dimensional space and you can find the L2 norm (or the Euclidean distance) of each vector. After, check if the distance is 0. If it is, then you have a match. Specifically, you can create a matrix such that element (i,j) in this matrix calculates the distance between row i in one matrix to row j in the other matrix.
You can treat your problem by modifying the distance matrix that results from this problem such that 1 means the two vectors completely similar and 0 otherwise.
This post should be of interest: Efficiently compute pairwise squared Euclidean distance in Matlab.
I would specifically look at the answer by Shai Bagon that uses matrix multiplication and broadcasting. You would then modify it so that you find distances that would be equal to 0:
nA = sum(A.^2, 2); % norm of A's elements
nB = sum(suppX.^2, 2); % norm of B's elements
Xresponse = bsxfun(#plus, nB, nA.') - 2 * suppX * A.';
Xresponse = Xresponse == 0;
We get:
Xresponse =
3×4 logical array
1 1 0 1
0 0 0 0
0 0 1 0
Note on floating-point efficiency
Because you are using ismember in your implementation, it's implicit to me that you expect all values to be integer. In this case, you can very much compare directly with the zero distance without loss of accuracy. If you intend to move to floating-point, you should always compare with some small threshold instead of 0, like Xresponse = Xresponse <= 1e-10; or something to that effect. I don't believe that is needed for your scenario.
Here's an alternative to #rayryeng's answer: reduce each row of the two matrices to a unique identifier using the third output of unique with the 'rows' input flag, and then compare the identifiers with singleton expansion (broadcast) using bsxfun:
[~, ~, w] = unique([A; suppX], 'rows');
Xresponse = bsxfun(#eq, w(1:size(A,1)).', w(size(A,1)+1:end));

sum matrix using logical matrix - index exceeds matrix dimensions

I have two matrices.
mcaps which is a double 1698 x 2
index_g which is a logical 1698 x 2
When using the line of code below I get the error message that Index exceeds matrix dimensions. I don't see how this is the case though?
tsp = nansum(mcaps(index_g==1, :));
Update
Sorry I should have mentioned that I need the sum of each column in the mcaps vector
** Example of data **
mcaps index_g
5 6 0 0
4 3 0 0
6 5 1 1
4 6 0 1
8 7 0 0
There are two problems here. I missed one. Original answer is below.
What I missed is that when you use the logical index in this way, you are picking out elements of the matrix that may have different numbers of elements in each column, so MATLAB can't return a well formed matrix back to nansum, and so returns a vector. To get around this, use the fact that 0 + anything = 0
% create a mask of values you don't want to sum. Note that since
% index_g is already logical, you don't have to test equal to 1.
mask = ~index_g & isnan(mcaps)
% create a temporary variable
mcaps_to_sum = mcaps;
% change all of the values that you don't want to sum to zero
mcaps_to_sum(mask) = 0;
% do the sum
sum(mcaps_to_sum,1);
This is basically all that the nansum function does internally, is to set all of the NaN values to zero and then call the sum function.
index_g == 1 returns a 1698 x 2 logical matrix, but then you add in an extra dimension with the colon. To sum the columns, use the optional dim input. You want:
tsp = nansum(mcaps(index_g == 1),1);

difference of each two elements of a column in the matrix

I have a matrix like this:
fd =
x y z
2 5 10
2 6 10
3 5 11
3 9 11
4 3 11
4 9 12
5 4 12
5 7 13
6 1 13
6 5 13
I have two parts of my problem:
1) I want to calculate the difference of each two elements in a column.
So I tried the following code:
for i= 1:10
n=10-i;
for j=1:n
sdiff1 = diff([fd(i,1); fd(i+j,1)],1,1);
sdiff2 = diff([fd(i,2); fd(i+j,2)],1,1);
sdiff3 = diff([fd(i,3); fd(i+j,3)],1,1);
end
end
I want all the differences such as:
x1-x2, x1-x3, x1-x4....x1-x10
x2-x3, x2-x4.....x2-x10
.
.
.
.
.
x9-x10
same for y and z value differences
Then all the values should stored in sdiff1, sdiff2 and sdiff3
2) what I want next is for same z values, I want to keep the original data points. For different z values, I want to merge those points which are close to each other. By close I mean,
if abs(sdiff3)== 0
keep the original data
for abs(sdiff3) > 1
if abs(sdiff1) < 2 & abs(sdiff2) < 2
then I need mean x, mean y and mean z of the points.
So I tried the whole programme as:
for i= 1:10
n=10-i;
for j=1:n
sdiff1 = diff([fd(i,1); fd(i+j,1)],1,1);
sdiff2 = diff([fd(i,2); fd(i+j,2)],1,1);
sdiff3 = diff([fd(i,3); fd(i+j,3)],1,1);
if (abs(sdiff3(:,1)))> 1
continue
mask1 = (abs(sdiff1(:,1)) < 2) & (abs(sdiff2(:,1)) < 2) & (abs(sdiff3:,1)) > 1);
subs1 = cumsum(~mask1);
xmean1 = accumarray(subs1,fd(:,1),[],#mean);
ymean1 = accumarray(subs1,fd(:,2),[],#mean);
zmean1 = accumarray(subs1,fd(:,3),[],#mean);
fd = [xmean1(subs1) ymean1(subs1) zmean1(subs1)];
end
end
end
My final output should be:
2.5 5 10.5
3.5 9 11.5
5 4 12
5 7 13
6 1 13
where, (1,2,3),(4,6),(5,7,10) points are merged to their mean position (according to the threshold difference <2) whereas 8 and 9th point has their original data.
I am stuck in finding the differences for each two elements of a column and storing them. My code is not giving me the desired output.
Can somebody please help?
Thanks in advance.
This can be greatly simplified using vectorised notation. You can do for instance
fd(:,1) - fd(:,2)
to get the difference between columns 1 and 2 (or equivalently diff(fd(:,[1 2]), 1, 2)). You can make this more elegant/harder to read and debug with pdist but if you only have three columns it's probably more trouble than it's worth.
I suspect your first problem is with the third argument to diff. If you use diff(X, 1, 1) it will do the first order diff in direction 1, which is to say between adjacent rows (downwards). diff(X, 1, 2) will do it between adjacent columns (rightwards), which is what you want. Matlab uses the opposite convention to spreadsheets in that it indexes rows first then columns.
Once you have your diffs you can then test the elements:
thesame = find(sdiff3 < 2); % for example
this will yield a vector of the row indices of sdiff3 where the value is less than 2. Then you can use
fd(thesame,:)
to select the elements of fd at those indexes. To remove matching rows you would do the opposite test
notthesame = find(sdiff > 2);
to find the ones to keep, then extract those into a new array
keepers = fd(notthesame,:);
These won't give you the exact solution but it'll get you on the right track. For the syntax of these commands and lots of examples you can run e.g. doc diff in the command window.

Matlab: How to output a matrix which is sorted by distance

I have a cluster of points in 3D point clouds, says
A = [ 1 4 3;
1 2 3;
1 6 3;
1 5 3];
The distance matrix then was found:
D= pdist(A);
Z= squareform(D);
Z =
0 2 2 1
2 0 4 3
2 4 0 1
1 3 1 0
I would like to sort the points so that the sum of the distance travelled through the points will be the smallest, and output in another matrix. This is similar to TSP problem but in a 3D model. Is there any function can do this?
Your help is really appreciated in advance.
This could be one approach and must be efficient enough for a wide range of datasizes -
D = pdist(A);
Z = squareform(D); %// Get distance matrix
N = size(A,1); %// Store the size of the input array for later usage
Z(1:N+1:end) = Inf; %// Set diagonals as Infinites as we intend to find
%// minimum along each row
%// Starting point and initialize an array to store the indices according
%// to the sorted requirements set in the question
idx = 1;
out_idx = zeros(N,1);
out_idx(1) = idx;
%// Perform an iterative search to look for nearest one starting from point-1
for k = 2:N
start_ind = idx;
[~,idx] = min(Z(start_ind,:));
Z(:,start_ind) = Inf;
out_idx(k) = idx;
end
%// Now that you have the list of indices based on the next closest one,
%// sort the input array based on those indices and have the desired output
out = A(out_idx,:)
Sample run for given input -
A =
1 4 3
1 2 3
1 6 3
1 5 3
1 2 3
out =
1 4 3
1 5 3
1 6 3
1 2 3
1 2 3
The only way I can see you do this is by brute force. Also bear in mind that because this is brute force, this will scale very badly as the total number of points increases. This is fine for just 4 points, but if you want to scale this up, the total number of permutations for N points would be N! so be mindful of this before using this approach. If the number of points increases, then you may get to a point where you run out of memory. For example, for 10 points, 10! = 3628800, so this probably won't bode well with memory if you try and go beyond 10 points.
What I can suggest is to generate all possible permutations of visiting the 4 points, then for each pair of points (pt. 1 -> pt. 2, pt. 2 -> pt. 3, pt. 3 -> pt. 4), determine and accumulate the distances, then find the minimum distance accumulated. Whichever distance is the minimum will give you the sequence of nodes you need to visit.
Start with perms to generate all possible ways to visit four points exactly once, then for each pair of points, figure out the distances between the pairs and accumulate the distances. Keep considering pairs of points along each unique permutation until we reach the end. Once we're done, find the smallest distance that was generated, and return the sequence of points to generate this sequence.
Something like:
%// Your code
A = [ 1 4 3;
1 2 3;
1 6 3;
1 5 3];
D = pdist(A);
Z = squareform(D);
%// Generate all possible permutations to visit for our points
V = perms(1:size(A,1));
%// Used to accumulate our distances per point pair
dists = zeros(size(V,1), 1);
%// For each point pair
for idx = 1 : size(V,2)-1
%// Get the point pair in the sequence
p1 = V(:,idx);
p2 = V(:,idx+1);
%// Figure out the distance between the two points and add them up
dists = dists + Z(sub2ind(size(Z), p1, p2));
end
%// Find which sequence gave the minimum distance travelled
[~,min_idx] = min(dists);
%// Find the sequence of points to generate the minimum
seq = V(min_idx,:);
%// Give the actual points themselves
out = A(seq,:);
seq and out give the actual sequence of points we need to visit, followed by the actual points themselves. Note that we find one such possible combination. There may be a chance that there is more than one possible way to get the minimum distance travelled. This code just returns one possible combination. As such, what I get with the above is:
>> seq
seq =
3 4 1 2
>> out
out =
1 6 3
1 5 3
1 4 3
1 2 3
What the above is saying is that we need to start at point 3, then move to point 4, point 1, then end at point 2. Also, the sequence of pairs of points we need to visit is points 3 and 4, then points 4 and 1 and finally points 1 and 2. The distances are:
Pt. 3 - Pt. 4 - 1
Pt. 4 - Pt. 1 - 1
Pt. 1 - Pt. 2 - 2
Total distance = 4
If you take a look at this particular problem, the minimum possible distance would be 4 but there is certainly more than one way to get the distance 4. This code just gives you one such possible traversal.

MATLAB Combine matrices of different dimensions, filling values of corresponding indices

I have two matrices, 22007x3 and 352x2. The first column in each is an index, most (but not all) of which are shared (i.e. x1 contains indices that aren't in x2).
I would like to combine the two matrices into a 22007x4 matrix, such that column 4 is filled in with the values that correspond to particular indices in both original matrices.
For example:
x1 =
1 1 5
1 2 4
1 3 5
2 1 1
2 2 1
2 3 2
x2 =
1 15.5
2 -5.6
becomes
x3 =
1 1 5 15.5
1 2 4 15.5
1 3 5 15.5
2 1 1 -5.6
2 2 1 -5.6
2 3 2 -5.6
I've tried something along the lines of
x3(1:numel(x1),1:3)=x1;
x3(1:numel(x2(:,2)),4)=x2(:,2);
but firstly I get the error
??? Subscripted assignment dimension mismatch.
and then I can't figure out I would fill the rest of it.
An important point is that there are not necessarily an equal number of rows per index in my data.
How might I make this work?
Taking Amro's answer from here
[~, loc] = ismember(x1(:,1), x2(:,1));
ismember's second argument returns the location in x2 where each element of x1 can be found (or 0 if it can't)
a = x2(loc(loc > 0), 2);
get the relevant values using these row indices but excluding the zeros, hence the loc > 0 mask. You have to exclude these as 1, they are not in x2 and 2 you can't index with 0.
Make a new column of default values to stick on the end of x1. I think NaN() is probably better but zeros() is also fine maybe
newCol = NaN(size(x1,1),1)
Now use logical indexing to get the locations of the non zero elements and put a in those locations
newCol(loc > 0) = a
Finnaly stick it on the end
x3 = [x1, newCol]