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

Viewed 339

I have a dataframe in R similar to the following:

INSTITUTION  GROUP
University1  Group1
University1  Group1
University3  Group2
University4  Group2
University1  Group2
University3  Group3

My intent is to create an edgelist to create a network in igraph of all the possible connections between institutions within their respective groups. The idea is that relationships wouldn't repeat. It would look something like this (I assume):

EDGE1        EDGE2       
University1  University1 # for group 1
University3  University4 # for group 2
University3  University1 # for group 2
University4  University1 # for group 2
 
# and I assume that group 3 would not have an edge represented because it is alone.

I have been trying to figure out how to do this with igraph or with tidyverse and I am not sure how to go about it, as I always end up with repeated pairs or elements like Univerisity3 from group3 that I don't need.

Bonus question!

Once I have my edgelist set up, how can I limit the number edges shown in the network graph? For example, I would like to eliminate all edges that appear less than 10 times. The actual dataset is quite large and has many institutions.

Thank you so much!

3 Answers

I really love a base R solution, so that's what you'll get from me. It's a bit hacky and ad hoc but it seems to get the job done pretty quickly.

out <- do.call("rbind", lapply(split(data, data$GROUP), function(d) {
   if (nrow(d) > 1) unique(do.call("rbind", combn(d$INSTITUTION, 2, simplify = FALSE)))
}))

This splits the original data by group, then performs an operation within each group. That operation is, if there is more than one row in the group, take all pairwise combinations of members of the group (removing duplicates, though there shouldn't be any). Finally, it binds all the results into a single matrix.

To restrict the dataset to edges that appear more than ten times, first, create an "edge ID", then tabulate the edge IDs and remove all that have 10 or fewer appearances.

edgeID <- do.call(paste, as.data.frame(out))
tab <- table(edgeID)
out <- out[edgeID %in% names(tab)[tab > 10],,drop = FALSE]

Try the following code with split + make_full_graph + get.data.frame

do.call(
  rbind,
  lapply(
    with(df, split(INSTITUTION, GROUP)),
    function(v) {
      make_full_graph(length(v)) %>%
        set_vertex_attr(name = "name", value = v) %>%
        get.data.frame()
    }
  )
)

which gives

                from          to
Group1   University1 University1
Group2.1 University3 University4
Group2.2 University3 University1
Group2.3 University4 University1

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

Related