library(emln)
library(igraph)
library(tidyverse)
library(bipartite)
library(magrittr)

We provide examples with simulated toy networks and with real data to create an object of class monolayer. See help (?monolayer) to learn more about this class

Unipartite

We start with a toy example. We generate a 3x3 matrix with random weighted interactions.

n <- 3
adjacency_matrix <- matrix(round(runif(n*n,1,5),0), ncol = n, dimnames = list(paste0("A", 1:n), paste0("A", 1:n)))

# Convert a matrix to a monolayer object
matrix_to_list_unipartite(adjacency_matrix, directed = TRUE)
## $mode
## [1] "U"
## 
## $directed
## [1] TRUE
## 
## $nodes
## # A tibble: 3 × 2
##   node_id node_name
##     <int> <chr>    
## 1       1 A1       
## 2       2 A2       
## 3       3 A3       
## 
## $mat
##    A1 A2 A3
## A1  2  2  3
## A2  4  2  4
## A3  3  2  2
## 
## $edge_list
## # A tibble: 9 × 3
##   from  to    weight
##   <chr> <chr>  <dbl>
## 1 A1    A1         2
## 2 A1    A2         4
## 3 A1    A3         3
## 4 A2    A1         2
## 5 A2    A2         2
## 6 A2    A3         2
## 7 A3    A1         3
## 8 A3    A2         4
## 9 A3    A3         2
## 
## $igraph_object
## IGRAPH 2382cdb DNW- 3 9 -- 
## + attr: name (v/c), weight (e/n)
## + edges from 2382cdb (vertex names):
## [1] A1->A1 A1->A2 A1->A3 A2->A1 A2->A2 A2->A3 A3->A1 A3->A2 A3->A3
## 
## attr(,"class")
## [1] "monolayer"

Now let’s see a toy example for converting a link list.

# Generate a data frame with random links
link_list_unipartite <- data.frame(from=c("A1", "A1", "A1", "A2", "A2", "A2", "A3", "A3","A3"), to=c("A1", "A2", "A3", "A1", "A2", "A3", "A1", "A2","A3"), weight=round(runif(n*n,1,5),0))

list_to_matrix(link_list_unipartite, directed = TRUE, bipartite = FALSE)
## $mode
## [1] "U"
## 
## $directed
## [1] TRUE
## 
## $nodes
## # A tibble: 3 × 2
##   node_id node_name
##     <int> <chr>    
## 1       1 A1       
## 2       2 A2       
## 3       3 A3       
## 
## $mat
##    A1 A2 A3
## A1  3  4  4
## A2  4  4  2
## A3  1  4  1
## 
## $edge_list
##   from to weight
## 1   A1 A1      3
## 2   A1 A2      4
## 3   A1 A3      1
## 4   A2 A1      4
## 5   A2 A2      4
## 6   A2 A3      4
## 7   A3 A1      4
## 8   A3 A2      2
## 9   A3 A3      1
## 
## $igraph_object
## IGRAPH 3000e18 DNW- 3 9 -- 
## + attr: name (v/c), weight (e/n)
## + edges from 3000e18 (vertex names):
## [1] A1->A1 A1->A2 A1->A3 A2->A1 A2->A2 A2->A3 A3->A1 A3->A2 A3->A3
## 
## attr(,"class")
## [1] "monolayer"

To illustrate working with real data we will use a binary directed marine food web from Jacob, Ute, Aaron Thierry, Ulrich Brose, Wolf E. Arntz, Sofia Berg, Thomas Brey, Ingo Fetzer, et al. 2011. “The Role of Body Size in Complex Food Webs: A Cold Case.” In Advances in Ecological Research, edited by Andrea Belgrano, 45:181–223. Academic Press. Kongsfjorden is a glacial fjord on the northwest corner of the Svalbard archipelago. It is a 30 km open fjord with no marked sill at the entrance, and with a maximum depth exceeding 300 m. The network consists of 262 species with 1,544 feeding interactions. Note the warning messages; these are OK for this data set.

These data are organized as a link list, so it is possible to include node attributes.

# The data is included in the infomapecology package
data("kongsfjorden_links", package = 'infomapecology')
data("kongsfjorden_nodes", package = 'infomapecology')
nodes <- kongsfjorden_nodes %>%
  select(node_name=Species, node_id_original=NodeID, everything())

interactions <- kongsfjorden_links %>%
  select(from=consumer, to=resource) %>%
  mutate_if(is.factor, as.character) %>%
  mutate(weight=1)

monolayer_unipartite <- list_to_matrix(x=interactions, directed = T, bipartite = F, node_metadata = nodes)

head(monolayer_unipartite$nodes)
## # A tibble: 6 × 2
##   node_id node_name                    
##     <int> <chr>                        
## 1       1 Phaeocystis pouchetii        
## 2       2 Chaetoceros socialis         
## 3       3 Thalassiosira nordenskioeldii
## 4       4 Fragilariopsis               
## 5       5 Protoperidinium              
## 6       6 Gymnodinium
head(monolayer_unipartite$edge_list)
##                    from                            to weight
## 1 Calanus finnmarchicus         Phaeocystis pouchetii      1
## 2 Calanus finnmarchicus          Chaetoceros socialis      1
## 3 Calanus finnmarchicus Thalassiosira nordenskioeldii      1
## 4 Calanus finnmarchicus                Fragilariopsis      1
## 5 Calanus finnmarchicus               Protoperidinium      1
## 6 Calanus finnmarchicus                   Gymnodinium      1

Now let’s take the matrix we obtained as an example, and create a monolayer object from a matrix.

kongsfjorden_matrix <- monolayer_unipartite$mat
monolayer_unipartite <- matrix_to_list_unipartite(x = kongsfjorden_matrix, directed = T)
## [1] "Some nodes have no interactions. They will appear in the node table but not in the edge list"

Bipartite

We start with a toy example. We generate a random incidence matrix with dimensions P X A. Bipartite networks have two sets of nodes with no overlap between the sets.

# Case "Bipartite networks"
# 
P <- 4 # 4 rows
A <- 3 # 3 columns
incidence_matrix <- matrix(round(runif(P*A,1,5),0), ncol = A, dimnames = list(paste0("P", 1:P), paste0("A", 1:A)))

# Convert an matrix to an edge list for bipartite network
matrix_to_list_bipartite(incidence_matrix, group_names = c('A_set', 'P_set'))$edge_list
## # A tibble: 12 × 3
##    from  to    weight
##    <chr> <chr>  <dbl>
##  1 A1    P1         4
##  2 A1    P2         1
##  3 A1    P3         5
##  4 A1    P4         3
##  5 A2    P1         5
##  6 A2    P2         4
##  7 A2    P3         5
##  8 A2    P4         4
##  9 A3    P1         1
## 10 A3    P2         2
## 11 A3    P3         3
## 12 A3    P4         1

To demonstrate real data we use the incidence matrix olesen2002flores included in the bipartite package.

# create a monolayer object from a bipartite matrix
monolayer_bipartite <- matrix_to_list_bipartite(x = olesen2002flores, group_names = c('Pollinator', 'Plant'))

# What is included in the monolayer class object
names(monolayer_bipartite)
## [1] "mode"          "directed"      "nodes"         "mat"           "edge_list"     "igraph_object"
# The nodes in the network
monolayer_bipartite$nodes
## # A tibble: 22 × 3
##    node_id node_group node_name           
##      <int> <chr>      <chr>               
##  1       1 Pollinator Halictus.sp.        
##  2       2 Pollinator Sepsis.thoracica    
##  3       3 Pollinator Agrotis.ipsilon     
##  4       4 Pollinator Bombus.ruderatus    
##  5       5 Pollinator Colias.crocea       
##  6       6 Pollinator Musca.domestica     
##  7       7 Pollinator Apis.mellifera      
##  8       8 Pollinator Lucilia.sericata    
##  9       9 Pollinator Lasius.niger        
## 10      10 Pollinator Anothomyia.pluvialis
## # ℹ 12 more rows
# The link (edge) list
monolayer_bipartite$edge_list
## # A tibble: 30 × 3
##    from             to                    weight
##    <chr>            <chr>                  <dbl>
##  1 Halictus.sp.     Azorina:vidalii           98
##  2 Halictus.sp.     Solidago:sempervivens    141
##  3 Halictus.sp.     Daucus:carota             11
##  4 Halictus.sp.     Silene:vulgaris            7
##  5 Halictus.sp.     Chamomilla:suaveolens     21
##  6 Halictus.sp.     Reseda:luteola            11
##  7 Sepsis.thoracica Azorina:vidalii           51
##  8 Sepsis.thoracica Crithmum:maritimum        87
##  9 Sepsis.thoracica Beta:vulgaris              8
## 10 Sepsis.thoracica Silene:vulgaris           30
## # ℹ 20 more rows
# An igraph object is also created
monolayer_bipartite$igraph_object
## IGRAPH 972da5b UNWB 22 30 -- 
## + attr: type (v/l), name (v/c), weight (e/n)
## + edges from 972da5b (vertex names):
##  [1] Halictus.sp.    --Azorina:vidalii       Halictus.sp.    --Solidago:sempervivens Halictus.sp.    --Daucus:carota        
##  [4] Halictus.sp.    --Silene:vulgaris       Halictus.sp.    --Chamomilla:suaveolens Halictus.sp.    --Reseda:luteola       
##  [7] Sepsis.thoracica--Azorina:vidalii       Sepsis.thoracica--Crithmum:maritimum    Sepsis.thoracica--Beta:vulgaris        
## [10] Sepsis.thoracica--Silene:vulgaris       Agrotis.ipsilon --Solidago:sempervivens Bombus.ruderatus--Azorina:vidalii      
## [13] Bombus.ruderatus--Lotus:corniculatus    Bombus.ruderatus--Freesia:refracta      Colias.crocea   --Azorina:vidalii      
## [16] Colias.crocea   --Lotus:corniculatus    Colias.crocea   --Freesia:refracta      Musca.domestica --Azorina:vidalii      
## [19] Musca.domestica --Crithmum:maritimum    Musca.domestica --Daucus:carota         Musca.domestica --Chamomilla:suaveolens
## [22] Apis.mellifera  --Azorina:vidalii       Apis.mellifera  --Lotus:corniculatus    Apis.mellifera  --Reseda:luteola       
## + ... omitted several edges

We can also convert a link list to a monolayer object.

ll <- monolayer_bipartite$edge_list
monolayer_bipartite_2 <- list_to_matrix(ll, bipartite = T, group_names = c('Pollinator', 'Plant'))

Function create_monolayer_network

This function is a wrapper for the matrix_to_list_unipartite, matrix_to_list_bipartite, and list_to_matrix functions. It is convenient because it automatically selects the input type.

#Input is a link list
monolayer_unipartite <- create_monolayer_network(x=interactions, directed = T, bipartite = F, node_metadata = nodes)
## [1] "Input: an unipartite edge list"
# Input is a matrix
bipartite_monolayer <- create_monolayer_network(x = olesen2002flores, group_names = c('Pollinator', 'Plant'), bipartite = T)
## [1] "Input: a bipartite matrix"