Creating matrix from two vectors of (duplicated) indices in MATLAB - matlab

Suppose now I have two vectors of same length:
A = [1 2 2 1];
B = [2 1 2 2];
I would like to create a matrix C whose dim=m*n, m=max(A), n=max(B).
C = zeros(m,n);
for i = 1:length(A)
u = A(i);
v = B(i);
C(u,v)=C(u,v)+1;
end
and get
C =[0 2;
1 1]
More precisely, we treat the according indices in A and B as rows and columns in C, and C(u,v) is the number of elements in {k | A(i)=u and B(i)=v, i = 1,2,...,length(A)}
Is there a faster way to do that?

Yes. Use sparse. It assembles (i.e., sums up) the matrix values for repeating row-column pairs for you. You need an additional vector with the values that will be assembled into the matrix entries. If you use ones(size(A)), you will have exactly what you need - counting of repeated row-column pairs
spA=sparse(A, B, ones(size(A)));
full(spA)
ans =
0 2
1 1
The same can be obtained by simply passing scalar 1 to sparse function instead of a vector of values.
For matrices that have a large number of zero entries this is absolutely crucial that you use sparse storage. Another function you could use is accumarray. It can essentially do the same thing, but also works on dense matrix structure:
AA=accumarray([A;B]', 1);
AA =
0 2
1 1
You can pass size argument to accumarray if you want to create a matrix of specific size
AA=accumarray([A;B]', 1, [2 3]);
AA =
0 2 0
1 1 0
Note that you can actually also make it produce sparse matrices, and use a different operator in assembly (i.e., not necessarily a sum)
AA=accumarray([A;B]', 1, [2 3], #sum, 0, true)
will produce a sparse matrix (last parameter set to true) using sum for assembly and 0 as a fill value, i.e. a value which is used in cases a given row-column pair does not exist in A/B.

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));

Assigning values to a matrix through vector addition from an adjacency matrix

Very new to Matlab, I usually use STATA.
I want to use the nchoosek fuction to get the sum of vectors in one matrix.
I have a 21x21 adjacency matrix, with either 0 or 1 as the inputs. I want to create a new matrix, that will give me a sum of inputs between all possible triads from the adjacency matrix.
The new matrix should look have four variables, indexes (i, j, k) - corresponding to each combination from the 21x21. And a final variable which is a sum of the inputs.
The code I have so far is:
C = nchoosek(21,3)
B = zeros(nchoosek(21,3), 4)
for i=1:C
for j=i+1:C
for k=j+1:C
B(?)=B(i, j, k, A(i)+A(j)+A(k)) #A is the 21x21 adj mat
end
end
end
I know my assignment statement is incorrect as I don't completed understand the indexing role of the ":" operator. Any help will be appreciated.
Thanks!
This might be what you want:
clear all
close all
clc
A = rand(21,21); % Replace this with actual A
rowNum = 0;
for i=1:21
for j=i+1:21
for k=j+1:21
rowNum = rowNum+1;
B(rowNum,:) = [i, j, k, sum(A(:,i)+A(:,j)+A(:,k))];
end
end
end
There are some points:
You loop for different combinations. the total number of combination is nchoosek(21,3) which you can check after 3 nested loop. Your code with for i=1:C was the first error since you're actually looping for different values of i and different values of j and k. So these just 21 values not more.
To avoid repeated combinations, it's enough to start new index after the previous one, which you've realized in your code.
There are other possible approaches such as vectorized format, but to stick to your approach, I used a counter: rowNum which is the loop counter and updated along the loop.
B(rowNum,:) means all element of rowNum'th row of the matrix B.
Below is an algorithm to find the triads in an adjacency matrix. It checks all possible triads and sums the values.
%basic adjacency matrix with two triads (1-2-5) (2-3-5)
A=[];
A(1,:) = [0 1 0 0 1];
A(2,:) = [1 0 1 0 1];
A(3,:) = [0 1 0 0 1];
A(4,:) = [0 0 0 0 1];
A(5,:) = [1 1 1 1 0];
A=A==1; %logical matrix
triads=nchoosek(1:5,3);
S=nan(size(triads,1),4);
for ct = 1:size(triads,1)
S(ct,1:3)=[A(triads(ct,1),triads(ct,2)),A(triads(ct,1),triads(ct,3)),A(triads(ct,2),triads(ct,3))];
S(ct,4)=sum(S(ct,1:3));
end
triads(find(S(:,4)==3),:)
ans =
1 2 5
2 3 5

Using vector as indices not working as expected, matlab

Given a nXm matrix A and a mX2 matrix B and a matrix C of size mX1 containing 1s and 2s C=[1 2 1 2 1...], depending on which column, I want every row of A to be multiplied with. How can this be done? Or equivalently, given D = A*B how can I access only the values dictated by C. I tried D(:,C), but the result is not the expected.
Example a =[1 2; 3 4; 5 6] . c = [1 2 1] . a(?) = [1 4 5]
Any idea?
%example data
n=10;m=20;
A=rand(n,m)
B=rand(m,2)
C=round(rand(m,1))+1;
%solution:
B2=B(:,1); %multiplication vector
B2(C==2)=B(C==2,2) %change the ones where C==2
A*B2
You can run the following command for the last example:
a(sub2ind([3,2],1:3,c))'
In general case you can do like the following:
% n is the length of the D which is nx2 matrix
D(sub2ind([n,2],1:n,C))'

Eliminating zeros in a matrix - Matlab

Hi I have the following matrix:
A= 1 2 3;
0 4 0;
1 0 9
I want matrix A to be:
A= 1 2 3;
1 4 9
PS - semicolon represents the end of each column and new column starts.
How can I do that in Matlab 2014a? Any help?
Thanks
The problem you run into with your problem statement is the fact that you don't know the shape of the "squeezed" matrix ahead of time - and in particular, you cannot know whether the number of nonzero elements is a multiple of either the rows or columns of the original matrix.
As was pointed out, there is a simple function, nonzeros, that returns the nonzero elements of the input, ordered by columns. In your case,
A = [1 2 3;
0 4 0;
1 0 9];
B = nonzeros(A)
produces
1
1
2
4
3
9
What you wanted was
1 2 3
1 4 9
which happens to be what you get when you "squeeze out" the zeros by column. This would be obtained (when the number of zeros in each column is the same) with
reshape(B, 2, 3);
I think it would be better to assume that the number of elements may not be the same in each column - then you need to create a sparse array. That is actually very easy:
S = sparse(A);
The resulting object S is a sparse array - that is, it contains only the non-zero elements. It is very efficient (both for storage and computation) when lots of elements are zero: once more than 1/3 of the elements are nonzero it quickly becomes slower / bigger. But it has the advantage of maintaining the shape of your matrix regardless of the distribution of zeros.
A more robust solution would have to check the number of nonzero elements in each column and decide what the shape of the final matrix will be:
cc = sum(A~=0);
will count the number of nonzero elements in each column of the matrix.
nmin = min(cc);
nmax = max(cc);
finds the smallest and largest number of nonzero elements in any column
[i j s] = find(A); % the i, j coordinates and value of nonzero elements of A
nc = size(A, 2); % number of columns
B = zeros(nmax, nc);
for k = 1:nc
B(1:cc(k), k) = s(j == k);
end
Now B has all the nonzero elements: for columns with fewer nonzero elements, there will be zero padding at the end. Finally you can decide if / how much you want to trim your matrix B - if you want to have no zeros at all, you will need to trim some values from the longer columns. For example:
B = B(1:nmin, :);
Simple solution:
A = [1 2 3;0 4 0;1 0 9]
A =
1 2 3
0 4 0
1 0 9
A(A==0) = [];
A =
1 1 2 4 3 9
reshape(A,2,3)
ans =
1 2 3
1 4 9
It's very simple though and might be slow. Do you need to perform this operation on very large/many matrices?
From your question it's not clear what you want (how to arrange the non-zero values, specially if the number of zeros in each column is not the same). Maybe this:
A = reshape(nonzeros(A),[],size(A,2));
Matlab's logical indexing is extremely powerful. The best way to do this is create a logical array:
>> lZeros = A==0
then use this logical array to index into A and delete these zeros
>> A(lZeros) = []
Finally, reshape the array to your desired size using the built in reshape command
>> A = reshape(A, 2, 3)

how to Conditionally add values to a matrix without using a for loop?

I have written a for loop code and I want to write in more succinct way without using a for loop, but instead use matrix conditional.
I am teaching myself matlab and I would appreciate any feedback.
I want to create a new matrix, the first column is y, and the second column is filled with zero except for the y's whose indices are contained in the indices matrix. And in the latter case, add 1 instead of 0.
Thanks.
y=[1;2;3;4;5;6;7];
indices=[1;3;5];
[m,n]=size(y);
tem=zeros(m,1);
data=[y,tem];
[r,c]=size(indices);
for i=1:r
a=indices(i);
data(a,2 )=1;
end
Output:
data =
1 1
2 0
3 1
4 0
5 1
6 0
7 0
A shorter alternative:
data = [y(:), full(sparse(indices, 1, 1, numel(y), 1))];
The resulting matrix data is composed of two column vectors: y(:) and a sparse array, with "1"s at the positions corresponding to indices.
Using proper initialization and sparse matrices can be really useful in MATLAB.
How about
data = zeros( m, 2 );
data(:,1) = y;
data( indices, 2 ) = 1;