How to Create an Edge List from a Matrix in R

How to create an edge list from a matrix in R?

Using the igraph package:

x <- matrix(c(0,2,1,1,2,0,1,0,1,1,0,1,1,0,1,0), 4, 4)
rownames(x) <- colnames(x) <- LETTERS[1:4]

library(igraph)
g <- graph.adjacency(x)
get.edgelist(g)

# [,1] [,2]
# [1,] "A" "B"
# [2,] "A" "B"
# [3,] "A" "C"
# [4,] "A" "D"
# [5,] "B" "A"
# [6,] "B" "A"
# [7,] "B" "C"
# [8,] "C" "A"
# [9,] "C" "B"
# [10,] "C" "D"
# [11,] "D" "A"
# [12,] "D" "C"

I would also recommend you spend some time reading the igraph documentation at http://igraph.sourceforge.net/index.html since a lot of your recent questions are all simple case usages.

(As a bonus, plot(g) will answer your other question How to plot relationships in R?)

Better way to create edge list matrix using an adjacency matrix

We create a matrix of 0's based on the length of unique dimnames of the first dataset. Based on the match between the rownames and colnames of both the datasets, we assign the values of 'm1' to 'm2', take the transpose and add it with 'm2'

un1 <- unique(unlist(dimnames(m1)))
m2 <- matrix(0, length(un1), length(un1), dimnames = list(un1, un1))
i1 <- match(rownames(m1), rownames(m2), nomatch =0)
j1 <- match(colnames(m1), rownames(m2), nomatch = 0)
m2[i1, j1] <- m1
t(m2)+m2
# A B C D E F R S K
#A 0 0 0 0 0 0 1 1 0
#B 0 0 0 0 0 0 0 1 0
#C 0 0 0 0 0 0 1 0 0
#D 0 0 0 0 0 0 1 0 0
#E 0 0 0 0 0 0 0 0 1
#F 0 0 0 0 0 0 0 0 1
#R 1 0 1 1 0 0 0 0 0
#S 1 1 0 0 0 0 0 0 0
#K 0 0 0 0 1 1 0 0 0

Create edgelist from dataframe depending on groups and time period

Fun problem, even though the solution is more about managing your data than it is about igraph as such. Within each group, you're loking to list the persons who overlap in time.

We'd need a good method for evaluating the overlaps.

This is not the nicest solution (a hack with the result-format of the overlap-function makes it inposible to use the year 0), but at least it's pedagogical.

library('dplyr')

ppl <- c("pers1", "pers2","pers3","pers4","pers5","pers2","pers6","pers1")
grp <- c(1,1,1,2,2,2,3,3)
timeST <- c(2005,2005,2010,2012,2014,2007,2008,2008)
timeTER <- c(2010,2007,2018,2014,2015,2010,2020,2020)
# construct example data frame
example.df <- data.frame(ppl, grp, timeST, timeTER)

# Just like in your logic, these are peple that overlap within the same group.
df <- example.df %>% inner_join(example.df, by="grp")

# We get a structure with the start and end times for both i and j like:
names(df)

# This function is used to compute overlapping years between the intervals
# that exist between timeST.x-timeTER.x and "timeST.y-timeTER.y

time.period.overlap <- function(x_start, x_end, y_start, y_end)
{
# Return intersections of time-periods (x_start - x_end) and (y_start - y_end)
x <- seq(x_start, x_end)
y <- seq(y_start, y_end)

# Each result contains at least a row of 0 to stay true to the data-format and avoid NULLs
c(unique( c(x[x %in% y], y[y %in% x]) ), 0)
}

# Make an edge-list of person-to-person WITHIN grp and WITH overlapping years
# as defined by time.period.overlap(). Choose only rows that DO HAVE an overlap
all.edges <-
do.call('rbind',
lapply(1:nrow(df), function(x)
data.frame(
i = df[x, 'ppl.x'],
j = df[x, 'ppl.y'],
grp = df[x, 'grp'],
yr_overlap = time.period.overlap(df[x, 'timeST.x'], df[x, 'timeTER.x'], df[x, 'timeST.y'], df[x, 'timeTER.y'])
)
)
) %>% filter(yr_overlap != 0)

# Note that pairs like edges like pers4->pers2 in group 2 are not in this df
# since they never appeared in that group during the same year!
all.edges[all.edges$i == 'pers4',]
# For each pair i->j within each group, one row exists for each overlapping year

# Group by i, j and group to find the number of years of each pair's overlap to use in the network
el <- all.edges %>% group_by(i, j, grp) %>% summarise(n_yr_overlap = n(), first_overlap = min(yr_overlap))

Now the edge-list el can be sent to igraph for an aggregate network, or you could subsample networks for each given year using the all.edges.

Remember to handle self-loops (either in the data-frames or when creating your iGraph object) according to if it is logical that people have edges to themselves or not (which they do have in this output).

How to create an edgelist from a dataframe (expand, group_by?)

You can first construct a bipartite graph, then do a so-called bipartite projection.

library(igraph)

df <- read.table(header=T,text=
"INSTITUTION GROUP
University1 Group1
University1 Group1
University3 Group2
University4 Group2
University1 Group2
University3 Group3")

g <- graph_from_data_frame(df)

V(g)$type <- V(g)$name %in% df[,1]

igraph uses the type vertex attribute to encode which vertex belongs to which partition within the bipartite graph.

Now we can do the projection:

> bipartite_projection(g)
$proj1
IGRAPH 98f44c3 UNW- 3 2 --
+ attr: name (v/c), weight (e/n)
+ edges from 98f44c3 (vertex names):
[1] Group1--Group2 Group2--Group3

$proj2
IGRAPH ac701ec UNW- 3 3 --
+ attr: name (v/c), weight (e/n)
+ edges from ac701ec (vertex names):
[1] University1--University3 University1--University4
[3] University3--University4

This gives us the university-university, as well as the group-group relationships in two result graphs.

To get only the university-university relations, use

bipartite_projection(g, which="true")

This will give you the graph which has the vertices marked with TRUE in the type attribute. In our case that's the universities.

See here for details: https://igraph.org/r/html/latest/bipartite_projection.html

How to create weighted adjacency list/matrix from edge list?

This response uses base R only. The result is a standard matrix used to represent the adjacency matrix.

 el  <- cbind(a=1:5, b=5:1) #edgelist (a=origin, b=destination)
mat <- matrix(0, 5, 5)
mat[el] <- 1
mat
# [,1] [,2] [,3] [,4] [,5]
#[1,] 0 0 0 0 1
#[2,] 0 0 0 1 0
#[3,] 0 0 1 0 0
#[4,] 0 1 0 0 0
#[5,] 1 0 0 0 0

Here mat is your adjacency matrix defined from edgelist el, which is a simple cbind of the vectors 1:5 and 5:1.

If your edgelist includes weights, then you need a slightly different solution.

el <- cbind(a=1:5, b=5:1, c=c(3,1,2,1,1)) # edgelist (a=origin, b=destination, c=weight)
mat<-matrix(0, 5, 5)
for(i in 1:NROW(el)) mat[ el[i,1], el[i,2] ] <- el[i,3] # SEE UPDATE
mat
# [,1] [,2] [,3] [,4] [,5]
#[1,] 0 0 0 0 3
#[2,] 0 0 0 1 0
#[3,] 0 0 2 0 0
#[4,] 0 1 0 0 0
#[5,] 1 0 0 0 0

UPDATE

Some time later I realized that the for loop (3rd line) in the previous weighted edgelist example is unnecessary. You can replace it with the following vectorized operation:

mat[el[,1:2]] <- el[,3]

Creating edge list in R

Try

 res <- do.call(rbind,with(df, tapply(item, ID, 
FUN=function(x) if(length(x)>=2) t(combn(x,2)))))
paste(res[,1], res[,2], sep=";")
#[1] "a;b" "a;c" "b;c" "a;c" "b;a"

Convert data frame edgelist into matrix while keeping row and column names

You can retain the dimnames directly if you use xtabs:

xtabs(Similarity ~ Source + Target, example)
# Target
# Source 1 2 3 4
# 1 1.000 0.000 0.200 0.100
# 2 0.004 0.100 0.000 0.000
# 3 1.000 2.000 0.000 0.140
# 4 0.006 0.000 1.000 0.036


Related Topics



Leave a reply



Submit