Compare two vectors in MATLAB without using loop - matlab

Given two vector a , b of different length in MATLAB, I want the output as follows:
Example :
a = [3 5 10 20 45 80]
b = [3 5 80]
y = [1 1 0 0 0 1]
where y is of length similar to a in which 1's indicate existence of an item in b and 0 its non-existence.
I want to do this without using loops. Thanks
Note that all the numbers in each vector will be repeated only once as they correspond to some ids.

ismember()
Lia = ismember(A,B) returns an array containing 1 (true) where the data in A is found in B. Elsewhere, it returns 0 (false).
a = [3 5 10 20 45 80];
b = [3 5 80];
ismember(a,b)
ans =
1 1 0 0 0 1

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.

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

Find if 2 vectors have 4 consecutive identical elements

I am trying to compare 2 vectors to discover if they share 4 consecutive values.
For example
w = [6 7 8 9 10 11 12 13 14]
v = [5 6 7 8 9]
Has 4 consecutive values 6 7 8 9
But
x = [6 7 8 9 10 11 12 13 14]
y = [6 7 1 2 3 4 5 6 13 14]
has four identical values (6 7 13 14) but they aren't consecutive.
The code I am currently using is:
if length(intersect(v, w)) >= 4
condition = true;
but this doesn't test for consecutive elements, so it would return true for both cases listed above whereas I want it to only return true for the first case.
Can somebody please help me find a way to test for identical consecutive elements rather than just identical elements.
Building on Marcos' answer:
Create all possible search vectors from your initial search (i.e. [5 6 7 8] [6 7 8 9]) - however we will make it a 3D matrix which will be m-by-1-by-n
n = 4;
m = numel(v)-n+1;
V = permute(v(bsxfun(#plus,(0:m-1)',1:n)),[1,3,2])
Check if any of these sub-vectors are a subset of the vector being searched
check = sum(any(bsxfun(#eq, V, w),3),2) >= n;
match = squeeze(V(check,:,:))' %'// The ' is debatable here, it depends on how many matches you get
you can compare
bsxfun(#eq, w,v')
Resulting with
ans =
0 0 0 0 0 0 0 0 0
1 0 0 0 0 0 0 0 0
0 1 0 0 0 0 0 0 0
0 0 1 0 0 0 0 0 0
0 0 0 1 0 0 0 0 0
As you can see four consecutive matching elements form a diagonal of length 4.
To find the location of this diagonal you can conv2 with a 4 diagonal filter (eye(4)):
[rr cc] = find( conv2( single(bsxfun(#eq, [1 2 3 w],v')), eye(4), 'same' ) == 4 )
compensating for the center of the filter
loc_in_w = cc - 1
loc_in_v = rr - 1
yielding
loc_in_w =
1
loc_in_v =
2
which are the first index of the sequence in w and v respectively.
This method can work with more than one occurrence of a 4-substring of v in w...
I haven't meddled in matlab for ages, but my "general" approach to this in computing terms would be splitting the problem into a needle-and-haystack solution with two parts:
Create all possible search vectors from your initial search (i.e. [5 6 7 8] [6 7 8 9])
Check if any of these sub-vectors are a subset of the vector being searched.
Basically just two set-operations in a row.
You could convert your vectors to strings, and use strfind.
If x and y are your vectors:
x_str = mat2str(x);
y_str = mat2str(y);
n = strfind(x_str(2:end-1), y_str(2:end-1))
Note that you have to remove the first and last characters of the string version, as they correspond to the square brackets of the vectors.

Creating a variable with unequal rows

I want to create a variable that finds a pattern (let's say [1 1]) in different rows of a matrix (A). Of course there aren't an equal number of occurrences of this string in each row.
A = [ 0 0 0 1 1
1 1 1 0 0
0 1 0 1 1
1 1 1 0 0
0 1 0 0 1
1 0 1 1 1
0 1 0 1 0
1 1 1 0 1];
I could do:
for i = 1:n
var(i,:) = strfind(A(i,:),[1 1]);
end
but then both sides of the equation won't be equal.
ERROR: ??? Subscripted assignment dimension mismatch.
I try to preallocate. I create a matrix with what I think would be the maximum number of occurrences of this string in each row of matrix A (let's say 50).
for i = 1:n
var(i, :) = NaN(1,50)
end
That's followed by the previous bit of code and it's no good either.
I've also tried:
for i = 1:n
var(i,1:numel(strfind(A(i,:),[1 1])) = strfind(A(i,:),[1 1])
end
Error: The expression to the left of the equals sign is not a valid
target for an assignment.
How should I go about doing this?
The output I expect is a matrix var(i,:) that gives me the position in the matrix where each of these patterns occur. It works fine for just one row.
For example:
var(1,:) = [1 2 5 8 10 22 48]
var(2,:) = [2 3 4 7 34 45 NaN]
var(3,:) = [4 5 21 32 33 NaN]
Thanks!
In your first try: you tried to build a matrix with different length of rows.
In your second try: you pre-allocated, but then run it over by re-definning var(i,:), while you tried to put there your desired result.
In your third try: unfortunately you just missed one brackets- ) at the end of left expression.
This code suppose to work (what you did at 2nd and 3rd attempts, with pre-allocate and fixed brackets):
var=NaN(1,50);
for i = 1:n
var(i,1:numel(strfind(A(i,:),[1 1]))) = strfind(A(i,:),[1 1])
end

How to group rows with same column values?

Given the matrix with coordinates in 3D space and values for two variables (say a and b) in two matrices I would like to merge rows for same points into a common matrix.
To clearly explain the matter, let's say we have matrices
A=[posX, posY, posZ, a]
and
B=[posX, posY, posZ, b]
and would like to combine them into
AB = [posX, posY, posZ, a, b]
for example
A = [0 0 1 1; 0 1 0 4; 5 0 12 8];
B = [0 0 0 5; 0 1 0 3; 5 11 7 7];
would give
AB = [0 0 0 0 5; 0 0 1 1 0; 0 1 0 4 3; 5 0 12 8 0; 5 11 7 0 7];
In order to do that I first created
ATemp = [A, zeros(length(A,0)]
and
BTemp = [B(:, [1 2 3]), zeros(length(B),1), B(:,4)]
and then tried to use functions accumarray and grpstats but haven't managed to form the AB matrix.
I would be very thankful if anyone suggested the way to get the desired matrix.
AB=union(A(:,1:3),B(:,1:3),'rows');
AB(ismember(AB,A(:,1:3),'rows'),4)=A(:,4);
AB(ismember(AB(:,1:3),B(:,1:3),'rows'),5)=B(:,4)
[edit] This solution is only valid if each (x,y,z)-point occurs only once in each matrix. If there are several, there is a dimension mismatch in the second line (and/or the third).