How to intersect two or more matrices? - matlab

Let say I have some number of matrices with non-negative entries, N matrices for example of equal sizes MxM. For example, I have 3 matrices as follow:
A1=[2, 2, 0;
2, 2, 0;
0, 2, 0];
A2=[4, 0, 4;
4, 3, 0;
0, 0, 1];
A3=[2, 0, 0;
1, 0, 3;
3, 4, 3];
I want to find the intersection of A1, A2, and A3 in matlab. That means I want to get the follow matrix:
B=[-1, 2, 4;
-1, -1, 3;
3, -1, -1];
If the intersection of the (i, j) element of the N matrices, i.e., the elements A1(i, j), A2(i, j), A3(i, j), is at most one nonzero number then B(i,j) equals that number. Else if the intersection is at least two numbers I output -1 as I showed in the previous example.
How can I do this in matlab without loops?

First, concatenate to a single 3D-Matrix:
A=cat(3,A1,A2,A3)
Then count the non-zero elements, which gives the location of the -1-Elements:
L=(sum(A~=0,3)>1)
Finally, There where L=1 we want a -1, otherwise we want the single element at that location, which is the sum because the others are zero:
L*-1+(1-L).*sum(A,3)

I would do it something like so:
First, count up the non-zero elements at each location across all matrices:
foo = (A1 ~= 0) + (A2 ~= 0) + (A3 ~= 0);
Next, find those which have more than one non-zero element, and set them to -1 in the result:
B = zeros(3,3);
B(foo > 1) = -1;
Finally, find the non-zero elements (since in the elements we care about there is only one non-zero element, this is the same as the sum across matrices)
sumA = A1 + A2 + A3;
And add them into the result matrix
B(foo == 1) = sumA(foo == 1);

Related

Integer random matrix with different ranges in matlab

How can one create a random matrix (say B) which element B(i,j) is a uniform random integer number in the close interval 0 and A(i,j). A is a given matrix. For example:
A = [6, 2, 5, 7;
12, 0, 4, 0;
8, 0, 10, 1;
11, 6, 2, 5];
B = floor(rand(size(A)).*(A+1))
Explanation
rand(size(A)) creates a matrix of the same size as A, with elements uniformly distributed between 0 and 1.
rand(size(A)).*(A+1) each element (i, j) is a random distribution between 0 and A(i, j)+1
floor(rand(size(A)).*(A+1)) each unit width range is mapped to the nearest lower integer
Note that B = round(rand(size(A)).*A) will almost do the same, except that 0 and A(i, j) will have less probability to occur.

How to extract values from a Matrix that recorded in a vector?

Just came across this problem might be interesting in many applications, for example,
I have a vector A = [2; 5; 10], the values in vector A is sorted and unique.
I have got a matrix (2D or 3D), for example, B = [2, 8, 10; 2, 5, 5; 9, 1, 10];
Want to get a matrix C = [1, 0, 1; 1, 1, 1; 0, 0, 1].
It means if the element in B is also an element of A, we set it to one; otherwise, set the value to zero.
I did this in a for-loop, but for a large 3D matrix, it takes a long time to finish the loop.
Just wondering if there is a smarter method to do this without 'for' loop.
C = zeros(size(B));
for i = 1:size(A,1)
a = A(i);
C(B==a) = 1;
end
This is exactly what ismember does:
A = [2; 5; 10];
B = [2, 8, 10; 2, 5, 5; 9, 1, 10];
C = ismember(B,A)
C =
1 0 1
1 1 1
0 0 1
From the documentation:
ismember(A,B) returns an array containing 1 (true) where the data in A
is found in B. Elsewhere, it returns 0 (false).

Generating an equal number of random integers in response to a previous input in Matlab

I'm trying to create a random number matrix in matlab. However, I'm struggling a little with the logic of some of it. What I want is this:
I need it to loop through a predefined random matrix (2 rows, n columns) 50% of which are 1's and 50% are 0's (I can already do this part). Everytime it encounters a 1 it should then enter another loop which puts a 1,2,3 or 4 in the corresponding position in the second row. However (and this is the part I'm struggling with) I need it to have an equal number of 1's, 2's, 3's and 4's in the second row. So for example:
The matrix n = [0, 1, 0, 1, 1, 1, 0, 0; 0,0,0,0,0,0,0,0] should run through the script and produce something like: n = [0, 1, 0, 1, 1, 1, 0, 0; 0, 1, 0, 3, 2, 4, 0, 0]
This is what I have so far:
function pureToneTimer
ptpschedule = [0, 1, 0, 1, 1, 1, 0, 0; 0, 0, 0, 0, 0 , 0, 0, 0]
a = 0;
b = 0;
c = 0;
d = 0;
x = length(ptpschedule)/4
for n = 1:length(ptpschedule)
if ptpschedule(1,n) == 1
while a < x && b < x && c < x && d < x
i = randi(4)
ptpschedule(2,n) = i;
switch i
case 1
a = a + 1;
case 2
b = b + 1;
case 3
c = c + 1;
case 4
d = d + 1;
end
end
end
end
assignin('caller', 'ptpschedule', ptpschedule)
end
Sorry if this turns out to be a really trivial question. I'm just struggling to wrap my head around it!
Thanks,
Martin
This does what you want:
V = 4; %// number of values you want. 4 in your example
ind = ptpschedule(1,:)>0; %// logical index of positive values in the first row
n = nnz(ind);
vals = mod(0:n-1, V)+1; %// values to be randomly thrown in.
%// This guarantees the same number of each value if n is a multiple of V
ptpschedule(2,ind) = vals(randperm(n)); %// fill values in second row in random order
If the number of 1's in the first row is a multiple of V, this generates each value 1, 2, ... V the same number of times in the second row.
Otherwise, some values in the second row will appear once more than other values.
Example:
ptpschedule = [0, 1, 0, 1, 1, 1, 0, 0
0, 0, 0, 0, 0, 0, 0, 0];
V = 4;
produces
ptpschedule =
0 1 0 1 1 1 0 0
0 3 0 4 2 1 0 0

Intersection of matrices in matlab?

It is almost the same question as this one Matrices intersection!
The difference is this: If the intersection of the element (i, j) of all matrices is the same number then do not output -1 but output this number. An example is the following:
A1 = [2, 2, 0;
2, 2, 0;
0, 2, 0];
A2 = [2, 0, 4;
4, 3, 0;
0, 0, 1];
A3 = [2, 0, 0;
1, 0, 3;
3, 4, 3];
I want to get the follow matrix:
B = [2, 2, 4;
-1, -1, 3;
3, -1, -1];
Version 1
out1 = -1.*(A1~=A2).*(A1~=A3).*(A2~=A3)
max_mat = max(cat(3,A1,A2,A3),[],3)
out1(~out1) = max_mat(~out1)
Output
out1 =
2 2 4
-1 -1 3
3 -1 -1
Version 2: Maybe a faster version
Assumption - If out of the three elements in the corresponding positions across A1, A2 and A3, only two are same, then take the max of those three elements for the final matrix, B.
Code
%%// Concatenate all three A matrices
A=cat(3,A1,A2,A3,A1);
%%// Logical matrix with ones where all three elements are different from each other
out1 = -1.*all(diff(A,[],3)~=0,3)
%%// Get the max values, to be stored where -1 all three corresponding elements
%%// are not different from each other
max_mat = max(A,[],3)
%%// Get the final output
out1(~out1) = max_mat(~out1)
This produces the same output as the previous version.
Version 3
Assumption - If out of the three elements in the corresponding positions across A1, A2 and A3, only two are same, then take the element that is different from the other two for the final matrix, B.
Code
A=cat(3,A1,A2,A3,A1);
AA = A(:,:,1:3);
t1 = bsxfun(#ne,AA,mode(AA,3));
out1 = max(AA.*t1,[],3) + all(~t1,3).*A1;
out1(all(diff(A,[],3)~=0,3))=-1;
This produces the same output as the previous versions.
I would do this
A = A1+A2+A3;
B = (A1==A2)&(A1==A3);
C = (A1==0)+(A2==0)+(A3==0);
D = ones(3)*-1;
D(B==1) = A1(B==1);
D(C==2) = A(C==2);
B records the position of the elements whose number is same for all the matrices.
C records the position of the elements where two of the matrices have 0.
Then we can modify the elements of D, whose values are set -1 initially, using the information in matrices B and C.
A1 = [2, 2, 0;
2, 2, 0;
0, 2, 0];
A2 = [2, 0, 4;
4, 3, 0;
0, 0, 1];
A3 = [2, 0, 0;
1, 0, 3;
3, 4, 3];
A=cat(3,A1,A2,A3);
%identify all fields with identical values on the 3rd dimension
[X,Y]=find(sum(abs(diff(A,1,3)),3)==0);
%delete all but the first repetition, then use the previous code
A(X,Y,2:end)=0;
L=(sum(A~=0,3)>1);
L*-1+(1-L).*sum(A,3)
/update: Had to fix the code, now it should be correct.

How to "chop up" matrix in Matlab using combination of logical indexing and slicing?

I have a matrix M that looks similar to this:
M = [ 1, 2, 3, 0, 0;
1, 2, 0, 0, 0;
2, 3, 4, 5, 0;
4, 5, 6, 0, 0;
1, 2, 3, 4, 5;
]
I'm trying to get a column vector with the rightmost non-zero value of each row in A, but ONLY for the rows that have the first column == 1.
I'm able to calculate a filter for the rows:
r = M( :, 1 ) == 1;
> r = [ 1; 1; 0; 0; 1 ]
And I have a set of indices for "the rightmost non-zero value of each row in M":
> c = [ 3, 2, 4, 3, 5 ]
How do I combine these in a slicing of A in order to get what I'm looking for? I'm looking for something like:
A( r, c )
> ans = [ 3; 2; 5 ]
But doing this gets me a 3x3 matrix, for some reason.
The shortest way I can think of is as follows:
% Get the values of the last non-zero entry per row
v = M(sub2ind(size(M), 1:size(M,1), c))
% Filter out the rows that does not begin with 1.
v(r == 1)
This seems to work (I assume other operations defining r,c have been performed):
M(sub2ind(size(A),find(r==1).',c(r==1))).'
Short interpretation of the problem and solution:
M( r, c )
gives a 3 x 5 matrix (not 3 x 1 as desired) due to mixing of logical and subscript indices. The logical indices in r pick out rows in A with r==1. Meanwhile row array c picks out elements from each row according to the numeric index:
ans =
3 2 0 3 0
0 2 0 0 0
3 2 4 3 5
What you really want are indices into the rightmost nonzero elements in each row starting with 1. The solution uses linear indices (numeric) to get the correct elements from the matrix.
I think this should do the trick. I wonder if there is more elegant way of doing this though.
% get only rows u want, i.e. with first row == 1
M2 = M(r,:);
% get indices of
% "the rightmost non-zero value of each row in M"
% for the rows u want
indicesOfinterest = c(r==1);
noOfIndeciesOfinterest = numel(indicesOfinterest);
% desired output column vector
output = zeros(noOfIndeciesOfinterest, 1);
% iterate through the indeces and select element in M2
% from each row and column indicated by the indice.
for idx = 1:noOfIndeciesOfinterest
output(idx) = M2(idx, indicesOfinterest(idx));
end
output % it is [3; 2 ; 5]
You can use
arrayfun(#(x) M(x,c(x)), find(r))
But unless you need r and c for other purposes, you can use
arrayfun(#(x) M(x,find(M(x,:),1,'last')), find(M(:,1)==1))
Here is a way to do it using linear indexing:
N = M';
lin_index = (0:size(N,1):prod(size(N))-1) + c;
v = N(lin_index);
v(r)