How to Create a Bipartite Network in R with Igraph or Tnet

How to create a bipartite network in R with igraph or tnet

In igraph a bipartite network is one that has a type vertex attribute. This attribute must be logical and must the TRUE for one of the node types and FALSE for the others. So to create a bipartite network from your edge list, you simply create a regular graph and then add the type vertex attribute:

edgelist <- read.table(text="Person    Event
Amy football
Bob picnic
Sam artshow",
header=TRUE)
igraph <- graph.data.frame(edgelist)

V(igraph)$type <- V(igraph)$name %in% edgelist[,1]
igraph
# IGRAPH DN-B 6 3 --
# + attr: name (v/c), type (v/x)

The 'B' letter tells you that this is a bipartite graph. You can create the unipartite projections of this network via:

bipartite.projection(igraph)
# $proj1
# IGRAPH UN-B 3 0 --
# + attr: name (v/c), type (v/x)
#
# $proj2
# IGRAPH UN-B 3 0 --
# + attr: name (v/c), type (v/x)

This will return a list of two graphs. If you think that the projection might be too big, you can first call the bipartite.projection.size function, this will give you the number of vertices and edges in both projections. The memory requirement for an igraph graph is (4m+2n)*8+O(1) bytes, where 'n' is the number of vertices and 'm' is the number of edges.

Create bipartite graph in R?

If the graph is created straight from the data.frame it will not be a bipartite graph.

library(igraph)

g <- graph_from_data_frame(df)
is.bipartite(g)
#[1] FALSE

But it will be a bipartite graph if created from the incidence matrix.

tdf <- table(df)
g <- graph.incidence(tdf, weighted = TRUE)
is.bipartite(g)
#[1] TRUE

Now plot it.

colrs <- c("green", "cyan")[V(g)$type + 1L]
plot(g, vertex.color = colrs, layout = layout_as_bipartite)

Sample Image

How to construct bipartite graphs using igraph?

This resource at Shizuka Lab is really useful for exploring bipartite networks in R with igraph. In short:

library(igraph)

# Your matrix containing consumer choice by brands
m = matrix(data = sample(0:1, 25, replace = TRUE), nrow = 5, ncol = 5)
colnames(m) = c("A", "B", "C", "D", "E")
rownames(m) = c("Costa", "Starbucks", "Cafe2U", "Petes", "Philz")

# Convert it to a bipartitie network
bg = igraph::graph.incidence(m)
bg

# See the vertex attributes
V(bg)$type
V(bg)$name

# Plot the network
shape = ifelse(V(bg)$type, "circle", "square") # assign shape by node type
col = ifelse(V(bg)$type, "red", "yellow") # assign color by node type

plot(bg, vertex.shape = shape, vertex.color = col)

Gives:
Sample Image

Working on bipartite networks with igraph : problem with basic measures (density, normalized degree)

Density: you already got it

Degree
degv1 <- degree(g, V(g)[type == FALSE])
degv2 <- degree(g, V(g)[type == TRUE])

Normalized degree: divise by the vcount of the other node category

degnormv1 <- degv1/length(V(g)[type == TRUE])
degnormv2 <- degv2/length(V(g)[type == FALSE])

No answer regarding closeness, betweenness nor clustering coefficient

How to randomly change ties in a bipartite network without creating ties within the same level

Instead of doing something with edges, we may permute the vertices:

set.seed(1)
b1 <- sample_bipartite(10,4,type = "gnp", p = 0.5)
table(degree(b1))
# 1 2 3 4 5 6
# 4 3 2 2 2 1

(b2 <- permute(b1, c(sample(1:10), sample(11:14))))
# IGRAPH cf35948 U--B 14 20 -- Bipartite Gnp random graph
# + attr: name (g/c), p (g/n), type (v/l)
# + edges from cf35948:
# [1] 5--14 4--14 9--14 3--14 5--11 8--11 4--11 7--11 10--11 5--13 8--13 6--13
# [13] 7--13 3--13 10--13 5--12 4--12 2--12 1--12 10--12
table(degree(b2))
# 1 2 3 4 5 6
# 4 3 2 2 2 1

This approach is also easily generalizable to, say, multiple communities. It all depends on the blocks of shuffled vertex indices in the second argument of permute. Instead of thinking about shuffling, an easy way to see why this works is to think that we only switch around the names of the vertices.

How to plot a bipartite graph in R

From the ?bipartite_graph help:

Bipartite graphs have a type vertex attribute in igraph, this is boolean and FALSE for the vertices of the first kind and TRUE for vertices of the second kind.

So you could do something like this (igraph 1.0.1):

library(igraph)

set.seed(123)

# generate random bipartite graph.
g <- sample_bipartite(10, 5, p=.4)
# check the type attribute:
V(g)$type

# define color and shape mappings.
col <- c("steelblue", "orange")
shape <- c("circle", "square")

plot(g,
vertex.color = col[as.numeric(V(g)$type)+1],
vertex.shape = shape[as.numeric(V(g)$type)+1]
)

bipartite graph

Check also ?bipartite.


Using the example provided by the OP in the comments. Since the graph is multipartite and given the provided data format, I would first create a bipartite graph, then add the additional edges. Note that although the resulting graph returns TRUE for is_bipartite() the type argument is specified as numeric instead of logical and may not work properly with other bipartite functions.

set.seed(123)
V1 <- sample(LETTERS[1:10], size = 10, replace = TRUE)
V2 <- sample(1:10, size = 10, replace = TRUE)

d <- data.frame(V1 = V1, V2 = V2, weights = runif(10))
d
> d
V1 V2 weights
1 C 10 0.8895393
2 H 5 0.6928034
3 E 7 0.6405068
4 I 6 0.9942698
5 J 2 0.6557058
6 A 9 0.7085305
7 F 3 0.5440660
8 I 1 0.5941420
9 F 4 0.2891597
10 E 10 0.1471136

g <- graph_from_data_frame(d, directed = FALSE)
V(g)$label <- V(g)$name # set labels.

# create a graph connecting central node FOO to each V2.
e <- expand.grid(V2 = unique(d$V2), V2 = "FOO")
> e
V2 V2
1 10 FOO
2 5 FOO
3 7 FOO
4 6 FOO
5 2 FOO
6 9 FOO
7 3 FOO
8 1 FOO
9 4 FOO

g2 <- graph.data.frame(e, directed = FALSE)

# join the two graphs.
g <- g + g2

# set type.
V(g)$type <- 1
V(g)[name %in% 1:10]$type <- 2
V(g)[name %in% "FOO"]$type <- 3

V(g)$type
> V(g)$type
[1] 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 3

col <- c("steelblue", "orange", "green")
shape <- c("circle", "square", "circle")

library(rTRM) # Bioconductor package containing layout.concentric()
# the fist element in the list for concentric is the central node.
l <- layout.concentric(g, concentric = list("FOO", 1:10, LETTERS[1:10]))
plot(g,
layout = l,
vertex.color = col[V(g)$type],
vertex.shape = shape[V(g)$type],
edge.width = E(g)$weights * 5 # optional, plot edges width proportional to weights.
)

concentric

The function layout.concentric() is in (my) package rTRM, available from Bioconductor. It is really a simple implementation I wrote to do exactly what you want. I am not completely sure whether the latest igraph version has the same functionality though (it may be).

two mode network different colors for nodes

Try this instead

library(igraph)

col <- c("#a0d468", "#ffce54")
shape <- c("circle", "square")

d_edgelist <- read.csv("G:\\DATA.csv")

summary(d_edgelist)

library(igraph)
g <- graph.data.frame(d_edgelist, directed = FALSE)
g <- simplify(g)
V(g)$type <- FALSE
V(g)$type[V(g)$name %in% d_edgelist[, 1]] <- TRUE
g.proj <- bipartite.projection(g)
plot(g, layout = layout.bipartite,
vertex.size = 40,
vertex.shape = shape[as.numeric(V(g)$type) + 1],
vertex.color = col[as.numeric(V(g)$type) + 1],
edge.color = "#333333",
edge.width = E(g)$weight * 2
)


Related Topics



Leave a reply



Submit