Convert edge list to adjacency matrix - matlab

if i have the following code in matlab
function adj=edgeL2adj(el)
nodes=sort(unique([el(:,1) el(:,2)])); % get all nodes, sorted
adj=zeros(numel(nodes)); % initialize adjacency matrix
% across all edges
for i=1:size(el,1);adj(find(nodes==el(i,1)),find(nodes==el(i,2)))=el(i,3);
end
that convert edge list m x 3 to adjacency list n x n but i have a matrix of edge list m x 2 so what is the required change in previous code that give me true result.
example:
if edge list =[1 2;2 3;2 4] then adjacency matrix=[0 1 0 0;0 0 1 1;0 0 0 0; 0 0 0 0]

I would personally do away with your loop code and take advantage of sparse then convert back to a full matrix if desired. The first column consists of the source node and the second column consists of the target node. You simply set all of these entries in the sparse matrix to 1. However, judging from your code, the third column of your edge list is also the corresponding weight, and so I'll write code that will assume both cases. Also, make sure you filter out duplicate rows using unique:
Example with weights just being 1
edgelist = [1 2;2 3;2 4];
edgelist = unique(edgelist, 'rows');
sz = max(edgelist(:));
A = sparse(edgelist(:,1), edgelist(:,2), 1, sz, sz);
The first line of code denotes your edge list where each row pair consists of two nodes incident with each other (i.e. they are connected by an edge). The second line removes any duplicate rows from the edge list. The third line determines how big the adjacency matrix should be. We need to figure out what the largest node ID is so that we can allocate a N x N sparse matrix where N is the largest node ID. The last line of code simply uses the first column and second column of the edge list to populate the entries in the sparse matrix, we set them to one and ensure that the size of the matrix is N x N.
We get this:
>> A
A =
(1,2) 1
(2,3) 1
(2,4) 1
You can optionally convert the matrix to full by using the full function:
>> full(A)
ans =
0 1 0 0
0 0 1 1
0 0 0 0
0 0 0 0
As you can see, this corresponds with your desired result.
Example with weights in the third column
edgelist = [1 2 0.1;2 3 0.2;2 4 0.3];
edgelist = unique(edgelist, 'rows');
sz = max(max(edgelist(:, 1:2)));
A = sparse(edgelist(:,1), edgelist(:,2), edgelist(:,3), sz, sz);
Same code as before, but you're changing the third parameter to sparse with the third column of edgelist.
This is what we get:
>> A
A =
(1,2) 0.1000
(2,3) 0.2000
(2,4) 0.3000
>> full(A)
ans =
0 0.1000 0 0
0 0 0.2000 0.3000
0 0 0 0
0 0 0 0

An adjacency matrix should only contain boolean values to indicate an edge is present between vertices. I think this function assumed the third column of el is all ones. In the comments it is clarified that perhaps they are in fact weights. The function can be simplified too. Here's the modified code:
function adj=edgeL2adj(el)
nodes=unique(el(:, 1:2)); % get all nodes, sorted
adj=zeros(numel(nodes)); % initialize adjacency matrix
% across all edges
for i=1:size(el,1)
adj(nodes==el(i,1),(nodes==el(i,2)))=1;
% if third column is weights,
% adj(nodes==el(i,1),(nodes==el(i,2)))=el(i, 3);
end

Related

How to find number of occurrences of a subset of elements in a vector without using loops in MATLAB?

Say X is the given vector:
X=[1
2
4
2
3
1
4
5
2
4
5];
And Y is the given subset of elements from X:
Y=[3
4
5];
The required output is the number of times the elements in Y occur in X:
out=[1
3
2];
My solution to do this would be to use for loop:
for i=1:size(X,1)
temp = X(X(:,1)==Y(i,1),:);
out(i,1) = size(temp,1);
end
But when X and Y are large, this is inefficient. So, how to do it faster making use of vectorization? I know about hist and histc, but I can't think of how to use them in this case to get the desired output.
A Fast Option
You could use bsxfun combined with sum to compute this
sum(bsxfun(#eq, Y, X.'), 2)
Explanation
In this example, bsxfun performs a given operation on every combination of elements in X and Y. The operation we're gonig to use is eq (a check for equality). The result is a matrix that has a row for each element in Y and a column for each element in X. It will have a 1 value if the element in X equals the element in Y that corresponds to a given row.
bsxfun(#eq, Y, X.')
% 0 0 0 0 1 0 0 0 0 0 0
% 0 0 1 0 0 0 1 0 0 1 0
% 0 0 0 0 0 0 0 1 0 0 1
We can then sum across the columns to count the number of elements in X that were equal to a given value in Y.
sum(bsxfun(#eq, Y, X.'), 2)
% 1
% 3
% 2
On newer versions of MATLAB (since R2016b), you can omit the bsxfun since the equality operation will automatically broadcast.
sum(Y - X.', 2)
A Memory-Efficient Option
The first option isn't the most efficient since it requires creating a matrix that is [numel(Y), numel(X)] elements large. Another way which may be more memory efficient may be to use the second output of ismember combined with accumarray
[tf, ind] = ismember(X, Y);
counts = accumarray(ind(tf), ones(sum(tf), 1), [numel(Y), 1], #numel);
Explanation
ismember is used to determine if the values in one array are in another. The first input tells us if each element of the first input is in the second input and the second output tells you where in the second input each element of the first input was found.
[tf, ind] = ismember(X, Y);
% 0 0 1 0 1 0 1 1 0 1 1
% 0 0 2 0 1 0 2 3 0 2 3
We can use the second input to "group" the same values together. The accumarray function does exactly this, it uses the ind variable above to determine groups and then applies a given operation to each group. In our case, we want to simply determine the number of element within each group. So to do that we can pass a second input the size of the ind input (minus the ones that didn't match) of ones, and then use numel as the operation (counts the number in each group)
counts = accumarray(ind(tf), ones(sum(tf), 1), [numel(Y), 1], #numel);
% 1
% 3
% 2

how to convert edge list to adjacency matrix

I have this code
function adj=edgeL2adjj(e)
Av = [e; fliplr(e)];
nodes = unique(Av(:, 1:2)); % get all nodes, sorted
adj = zeros(numel(nodes)); % initialize adjacency matrix
% across all edges
for i=1:size(Av,1)
adj(nodes==Av(i,1),(nodes==Av(i,2))) = 1;
end
end
to convert an edge list to an adjacency matrix but if I input u=[8 5;1 4;3 5;6 7]
and then I divide u into two set [8 5;1 4], [3 5,6 7] and apply previous code on [3 5;6 7] I will get a 7 x 7 matrix.
but I want a 8 x 8 matrix to any input.
You have a 7x7 matrix because numel(nodes)=7. Indeed node #2 is not present in u. I suggest you to give this function a second input, that is the maximum number of nodes in your network (in this case, 8) and preallocate the adjacency matrix with such input parameter instead of numel(nodes). Or, alternatively, you can preallocate a zeros() square matrix by giving in input not numel(nodes), but the maximum value in input u. The first option will make your code more robust, the second option will make your code robust as long as the 8-th node is in input u.
Also, there is no need for the fliplr(): if your graph is undirected (that is, the adjacency matrix is symmetric) you can rely on such structure inside the for-loop, without concatenation in Av.
Such function can indeed be simplified as follows:
function adj=edgeL2adjj(e)
adj=zeros(max(max(e))); % initialize adjacency matrix
% across all edges
for i=1:size(e,1)
adj(e(i,1),e(i,2))=1;
adj(e(i,2),e(i,1))=1;
end
end
If the input e is your matrix:
e= [8 5;1 4;3 5;6 7];
such code returns
adj =
0 0 0 1 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 1 0 0 0
1 0 0 0 0 0 0 0
0 0 1 0 0 0 0 1
0 0 0 0 0 0 1 0
0 0 0 0 0 1 0 0
0 0 0 0 1 0 0 0
As you can see you simply exploit the input e. The maximum value is 8, so you build a square 8x8 matrix. Also inside the for-loop by swapping the column indices 1 and 2 in e, you automatically take care of the symmetric structure of the adjacency matrix. Finally, this code automatically sort you out in case of missing nodes: indeed, as you can see, the second row and the second column in adj are all-zeros because node #2 is not present in the edge list e.
Note: I do not recommend to split your input edge list. In this manner, you will loose all the global informations regarding your network. Indeed you will have (let's say) two "sub-networks" which you later have to concatenate (in terms of adjacency matrix). That is, if you split e in two sub-matrices, you'll have two adjacency matrices. By taking into account the code as it is now in my answer, the first matrix will be 8x8 whereas the second will be 7x7 because max(max([3 5;6 7]))=7.
If I may, I'd like to offer an altogether different solution to your problem, which avoids looping over all edges (always a good idea to avoid loops in MATLAB!).
Since you have all your row and column indices, your edge list basically already specifies a (albeit sparse) matrix. You can simply allocate the matrix like this:
function adj = edgeL2adjj(e)
r = e(:,1);
c = e(:,2);
vals = ones(size(r));
% maximum node index determines matrix dimensions
n_nodes = max(e(:));
% create sparse matrix
adj = sparse(r, c, vals, n_nodes, n_nodes);
% if really necessary, you can just convert it to a full matrix
adj = full(adj);
end
If you know you always want the graph to have the same dimensions, even if you only use part of your edges as an input to the function, you could just pass n_nodes as an input instead of determining it from e:
function adj = edgeL2adjj(e, n_nodes)

Rearranging elements in a row matlab

I have two matrices in Matlab.
A =
and
B =
I want to assign the elements having the same cell-value according to it's corresponding column number in A matrix and move the elements there. I want to map the elements of B with A so that B elements also moves in that position.
I want this
A =
And therefore,
B =
Is there a way to do this?!
Thanks.
Easiest way I can think of is to create row/column pairs where the rows correspond row locations of the matrix and column locations are the actual elements of the matrix themselves. The values seen at these row/column pairs are again just the matrix values themselves.
You can very easily do this with sparse. Recreating the matrix above and storing this in A:
A = [1 2 5 8; 1 2 4 7];
... I would do it this way:
r = repmat((1:size(A,1)).', 1, size(A,2)); %'
S = full(sparse(r(:),A(:),A(:)));
The first line of code generates row locations for each value in the matrix A, then using sparse to specify row/column pairs and the associated values and we use full to convert to a proper numeric matrix.
We get:
S =
1 2 0 0 5 0 0 8
1 2 0 4 0 0 7 0
You can also do the same for the matrix B. You'd use sparse and specify the third parameter to be B instead:
B = [0.5 0.2 0.6 0.8; 0.4 0.6 0.8 0.9];
S2 = full(sparse(r(:),A(:),B(:)));
We get:
>> S2
S2 =
0.5000 0.2000 0 0 0.6000 0 0 0.8000
0.4000 0.6000 0 0.8000 0 0 0.9000 0

sparse matrix parallel to the full matrix syntax of A(ind,ind)=1 in Matlab

In Matlab I can populate all combinations of elements in a full matrix by doing the following:
A=zeros(5);
ind=[1 4 5];
A(ind,ind)=1
A =
1 0 0 1 1
0 0 0 0 0
0 0 0 0 0
1 0 0 1 1
1 0 0 1 1
How can I accomplish that when my matrix A is sparse? (say A=zeros(1e6) and I only want ~1000 elements to be 1 etc...)
You can use the sparse command, as follows:
% create a 5x5 sparse matrix A, with 1's at A(ind,ind)
[row,col] = meshgrid(ind,ind); % form indexing combinations
row = row(:); % rearrange matrices to column vectors
col = col(:);
A = sparse(row, col, 1, 5, 5);
While it is possible to index sparse matrices using the conventional A(1,2) = 1 style, generally this is not a good idea. MATLAB sparse matrices are stored very differently to full matrices behind the scenes and do not support efficient dynamic indexing of this kind.
To get good performance sparse matrices should be built in one go using the sparse(i,j,x,m,n) syntax.

Create a 3D matrix

How can I define a 3D matrix in MATLAB?
For example a matrix of size (8 x 4 x 20) or add a 3rd dimension to an existing 2D matrix?
Create a 3D matrix
A = zeros(20, 10, 3); %# Creates a 20x10x3 matrix
Add a 3rd dimension to a matrix
B = zeros(4,4);
C = zeros(size(B,1), size(B,2), 4); %# New matrix with B's size, and 3rd dimension of size 4
C(:,:,1) = B; %# Copy the content of B into C's first set of values
zeros is just one way of making a new matrix. Another could be A(1:20,1:10,1:3) = 0 for a 3D matrix. To confirm the size of your matrices you can run: size(A) which gives 20 10 3.
There is no explicit bound on the number of dimensions a matrix may have.
If you want to define a 3D matrix containing all zeros, you write
A = zeros(8,4,20);
All ones uses ones, all NaN's uses NaN, all false uses false instead of zeros.
If you have an existing 2D matrix, you can assign an element in the "3rd dimension" and the matrix is augmented to contain the new element. All other new matrix elements that have to be added to do that are set to zero.
For example
B = magic(3); %# creates a 3x3 magic square
B(2,1,2) = 1; %# and you have a 3x3x2 array
I use Octave, but Matlab has the same syntax.
Create 3d matrix:
octave:3> m = ones(2,3,2)
m =
ans(:,:,1) =
1 1 1
1 1 1
ans(:,:,2) =
1 1 1
1 1 1
Now, say I have a 2D matrix that I want to expand in a new dimension:
octave:4> Two_D = ones(2,3)
Two_D =
1 1 1
1 1 1
I can expand it by creating a 3D matrix, setting the first 2D in it to my old
(here I have size two of the third dimension):
octave:11> Three_D = zeros(2,3,2)
Three_D =
ans(:,:,1) =
0 0 0
0 0 0
ans(:,:,2) =
0 0 0
0 0 0
octave:12> Three_D(:,:,1) = Two_D
Three_D =
ans(:,:,1) =
1 1 1
1 1 1
ans(:,:,2) =
0 0 0
0 0 0