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
How to Merge Two Data.Table by Different Column Names
Using Dplyr to Conditionally Replace Values in a Column
How to Order a Data Frame by One Descending and One Ascending Column
Add Dynamic Subtitle Using Ggplot
How to Use R (Rcurl/Xml Packages !) to Scrape This Webpage
How to Round a Data.Frame in R That Contains Some Character Variables
How to Generate a Frequency Table in R with With Cumulative Frequency and Relative Frequency
Plotting Cumulative Counts in Ggplot2
How to Select Columns in Data.Table Using a Character Vector of Certain Column Names
How to Extract Elements from a List with Mixed Elements
Data.Table Alternative for Dplyr Case_When
Interactively Change the Selectinput Choices
How to Find Useful R Tutorials with Various Implementations
Elegant Indexing Up to End of Vector/Matrix
Creating a Heat Map from (X,Y) Corrdinates in R