Introduction to bipartiteD3

Chris Terry

2024-09-02

library(r2d3)
library(bipartite )
library(purrr) 
library(dplyr) 
library(tidyr) 
library(stringr)
library(tibble)
library(RColorBrewer)
library(bipartiteD3)

Background

Bipartite networks describe the interactions between two discrete sets of nodes, for example plants and their pollinators or parasitoids and their hosts. The analysis of bipartite networks within ecology is commonly done with the bipartite package (Dormann et al.) which includes routines for a wide variety of network analyses.

Bipartite networks are often highly complex and visualising their structure is a considerable challenge. It is very common that bipartite networks include so many overlapping interactions that it becomes challenging to interpret. Furthermore there has recently been concerted effort within ecology to compare multiple networks simultaneously across time, space of interaction type.

Recent developments to improve the connections between R code and other coding languages make it possible to smoothly relate the data-analysis power and familiarity of R to languages and packages that are well suited to interactive plots. The D3 (Data-Driven Documents) is a JavaScript library for visualising and interacting with data, that is now supported within RStudio. However, coding in JavaScript is significantly different to R coding and for most ecologists it would not be a worthwhile investment in time to learn how to use an additional language

This package is designed to provide a smooth interface from R and data formats used in the bipartite package to make html widgets containing bipartite graphs that can be explored interactively.

These can either be viewed in RStudio v1.2+ or in a browser. The r2D3 package also provides the save_d3_png and save_d3_html functions to directly save d3 objects. When used together gist.github.com and bl.ocks.org together provide a route to straightforward free public hosting of D3 widgets, which can be useful for sharing.

Basic example and comparison

testdata <- data.frame(higher = c("bee1","bee1","bee1","bee2","bee1","bee3"), 
lower = c("plant1","plant2","plant1","plant2","plant3","plant4"), 
webID = c("meadow","meadow","meadow","meadow","meadow","meadow"), freq=c(5,9,1,2,3,7))
bipartite::frame2webs(testdata)-> SmallTestWeb

SmallTestWeb
## $meadow
##         higher
## lower    bee1 bee2 bee3
##   plant1    6    0    0
##   plant2    9    2    0
##   plant3    3    0    0
##   plant4    0    0    7
bipartite::plotweb(SmallTestWeb$meadow)

# If nothing appears then either update RStudio to version 1.2 or open in a modern web browser
bipartite_D3(SmallTestWeb)

Simultaneous Webs

Doubletestdata <- data.frame(higher = c("bee1","bee1","bee1","bee2","bee1","bee3", "bee1","bee1","bee1","bee2","bee1","bee3"), 
lower = c("plant1","plant2","plant1","plant2","plant3","plant4","plant2","plant1","plant2","plant3","plant4", 'plant5'), 
webID = c("meadow","meadow","meadow","meadow","meadow","meadow","bog","bog","bog","bog","bog","bog"), freq=c(5,9,1,2,3,7, 2,3,4,7,4,2))
bipartite::frame2webs(Doubletestdata)-> DoubleTestWeb

bipartite_D3(data =DoubleTestWeb, colouroption = 'brewer', filename = 'demo1')
## Warning in bipartite_D3(data = DoubleTestWeb, colouroption = "brewer", filename
## = "demo1"): Making too few facets. Guessing you want 1 row

Data format

The core function bipartite_D3() can take in the matrix formats used by bipartite, either as a discrete matrix, a list of matrices or an array, as may be output by frame2webs() or webs2array(). It’s native format however is a data frame where each row is an interaction. The first column details the name of the primary level species, the second column the secondary level species and the third the strength of the link. Additional webs can be added by adding fourth and subsequent columns, each named to indicate the site. Hence:

List2DF(DoubleTestWeb)
##    Primary Secondary bog meadow
## 1   plant1      bee1   3      6
## 2   plant2      bee1   6      9
## 3   plant3      bee1   0      3
## 4   plant4      bee1   4      0
## 5   plant5      bee1   0      0
## 6   plant1      bee2   0      0
## 7   plant2      bee2   0      2
## 8   plant3      bee2   7      0
## 9   plant4      bee2   0      0
## 10  plant5      bee2   0      0
## 11  plant1      bee3   0      0
## 12  plant2      bee3   0      0
## 13  plant3      bee3   0      0
## 14  plant4      bee3   0      7
## 15  plant5      bee3   2      0

Customisation Options

It is worth noting that many options can be set directly using the CSS file, but here is not really the place to describe that.

##Colour

Which level to colour by?

bipartite_D3(SmallTestWeb, colouroption = 'brewer', ColourBy = 1, filename = 'demo2')
bipartite_D3(SmallTestWeb, colouroption = 'brewer', ColourBy = 2, filename = 'demo3')

Monochrome, with highlighting

bipartite_D3(SmallTestWeb, monoChromeCol = 'BLUE',HighlightLab = 'plant2',
            HighlightCol = 'PINK', ColourBy = 1 , filename = 'demo4')

RColorBrewer

bipartite_D3(SmallTestWeb,colouroption = 'brewer',BrewerPalette ='Dark2', filename = 'demo5')

Manual, colouring by secondary layer

ManualColours<- c(bee1='green', bee2='red', bee3='yellow')

bipartite_D3(SmallTestWeb, colouroption = 'manual',
            NamedColourVector = ManualColours, ColourBy = 2,
            filename = 'demo7')

Orientation

Note that horizontal isn’t yet supported as well as vertical, and labels often overlap when interactions are rare. Generally it is not recommended.

bipartite_D3(SmallTestWeb,colouroption = 'brewer',
            Orientation = 'horizontal', filename = 'demo8')

Labels

Main Labels

bipartite_D3(SmallTestWeb, PrimaryLab = 'Flowers',
            SecondaryLab = 'Pollinators',
            SiteNames = 'Nice Meadow', filename = 'demo9')

Species Labels

These are inherited directly from the data. If you want to change these it is best to do that upstream of bipartite_d3.

Percentage Labels

If you have many small interactions, that would otherwise be 0, can extend the number of decimal places shown:

bipartite_D3(SmallTestWeb, PercentageDecimals = 2, filename = 'demo9.2')

Other properties of the Labels

To change other properties of the labels, the easiest way is to change the CSS file, then set the CSS_Output_Supress argument to TRUE. The CSS file controls things like the size, color and font of the labels. Detailing this is beyond the scope of this vignette. If you want an easy way experiment with this, you can open a html document in a browser and use the ‘inspect element’ or equivalent feature to see how the different elements relate to each other.

Edge mode

bipartite_D3(SmallTestWeb,EdgeMode = 'straight', filename = 'demo10')

Sorting

Can supply the order of either sides of the bipartite diagram.

df<-List2DF(SmallTestWeb)


# To sort primary by total size:
df %>%
  group_by(Primary) %>%
  summarise(Total=sum(meadow))%>%
  arrange(Total)-> SortDf

# To sort secondary manually

SortSec <- c('bee2', 'bee3', 'bee1')

bipartite_D3(df, SortPrimary = SortDf$Primary, SortSecondary = SortSec,
            filename = 'demo11')

Also included is a function that finds an order that minimises crossovers, by first dividing the networks by compartments, then sorting by compartment size, then minimising within-compartment cross-over using a CCA, similar to that used by the plotweb function in bipartite. These orders can be then passed to bipartite_D3()

OrderByCrossover(Safariland)
## $PrimaryOrder
## [1] "Cynanchum diemii"         "Mutisia decurrens"       
## [3] "Berberis darwinii"        "Alstroemeria aurea"      
## [5] "Calceolaria crenatiflora" "Ribes magellanicum"      
## [7] "Rosa eglanteria"          "Aristotelia chilensis"   
## [9] "Schinus patagonicus"     
## 
## $SecondaryOrder
##  [1] "Formicidae3"             "Nitidulidae"            
##  [3] "Braconidae3"             "Torymidae2"             
##  [5] "Bombus dahlbomii"        "Phthiria"               
##  [7] "Manuelia gayi"           "Sapromyza.Minettia"     
##  [9] "Vespula germanica"       "Phthiria1"              
## [11] "Sphecidae"               "Thomisidae"             
## [13] "Ichneumonidae2"          "Ruizantheda proxima"    
## [15] "Svastrides melanura"     "Trichophthalma jaffueli"
## [17] "Syrphus octomaculatus"   "Staphilinidae"          
## [19] "Corynura prothysteres"   "Chalepogenus caeruleus" 
## [21] "Trichophthalma amoena"   "Allograpta.Toxomerus"   
## [23] "Platycheirus1"           "Ruizantheda mutabilis"  
## [25] "Policana albopilosa"     "Braconidae2"            
## [27] "Ichneumonidae4"

Sizing and Positioning

The sizing and positioning of the various elements is one of the hardest features to balance. Large plots need to have sufficient space, but too much leads to large white gaps. With a more complex plot it is worth playing around with the various parameters. These are:

MainFigSize : The total size of the html canvas or widget that the figure is put in. By default it is 700x700 per facet. Increase this if things look cramped, the sides of the plot are uneven or bars are getting inverted.

IndivFigSize : This controls the size of the area in which the links are plotted. Only really worth changing in most cases if you want to change the ratios in some way

mp: This sets the number of rows and columns to plot the individual facets of multiple graphs. By default it assumes you want just one row.

BarSize: This the thickness of the bars. Defaults to 35

MinWidth: This sets the size to which each segment shrinks too when others are highlighted

Pad: Padding between boxes to make them more readable.

BoxLabPos: This is two numbers for primary and secondary levels that shift the label text. Larger values shift the text away from the graph. By default it takes a guess based on the maximum number of characters.

PercPos: This does the equivalent for the percentages

# With larger datasets the default can look a bit awkward, with overlaps and inversions

data(Safariland, vazquenc, package = 'bipartite')
data2<-bipartite::webs2array(Safariland, vazquenc)

bipartite_D3(data = data2, filename = 'demo12')
## Warning in bipartite_D3(data = data2, filename = "demo12"): Making too few
## facets. Guessing you want 1 row
#Adjusting Sizes to fit a complex figure:

bipartite_D3(data = data2,
            mp=c(2,1),
            MainFigSize = c(800, 1500), 
            IndivFigSize = c(200, 600),
            BoxLabPos = c(20, 20),
            PercPos = c(200,200),
            BarSize = 20,
            MinWidth = 5,
            Pad=5,
            PercentageDecimals = 2,
            filename = 'demo13')

Tidying up

The scripts above leave behind two JavaScript files, the library vizjs.js, and the graph itself which by default is called JSBP.js, and a matching style file JSBP.css. Most of the time these are no problem, and in fact are actually useful to be able to inspect and directly edit. However, to keep the CRAN checks happy, this tidies them up.

file.remove('vizjs.js')
## [1] TRUE
file.remove('JSBP.js')
## Warning in file.remove("JSBP.js"): cannot remove file 'JSBP.js', reason 'No
## such file or directory'
## [1] FALSE
file.remove('JSBP.css')
## Warning in file.remove("JSBP.css"): cannot remove file 'JSBP.css', reason 'No
## such file or directory'
## [1] FALSE