Plotting bipartite networks with ggbipart

Pedro Jordano

ggbipart is a set of R functions for plotting bipartite networks, using ggplot2, network, and igraph graphics. The ggplot2-based routines rely heavily on code developed by Francois Briatte for the ggnet library (now part of GGally).

Bipartite networks are a special type of network where nodes belong to two distinct sets (modes), and links only connect nodes of different sets. As with other networks, bipartite structures can be binary (only the presence/absence of a link is recorded) or quantitative (weighted), where links carry a variable importance or weight. Everything starts from an adjacency (incidence) matrix. Here we use matrices describing ecological interactions between animal frugivores and the plants whose fruits they eat. By convention in these examples, rows are plant species and columns are animal species.

library(ggbipart)
library(ggplot2)
library(network)
library(igraph)

The example data

The package ships two well-sampled plant-frugivore interaction matrices from southern Spain in inst/extdata/, retrievable with system.file(). Both are weighted adjacency matrices; the comment header lines are stripped with comment.char = "#".

# Nava de las Correhuelas (26 plants x 36 frugivores).
nch <- as.matrix(read.csv(
  system.file("extdata", "sdw01_adj_fru.csv", package = "ggbipart"),
  comment.char = "#", row.names = 1, check.names = FALSE))

# Hato Raton (16 plants x 17 frugivores).
hr <- as.matrix(read.csv(
  system.file("extdata", "sdw02_adj_fru.csv", package = "ggbipart"),
  comment.char = "#", row.names = 1, check.names = FALSE))

dim(nch)
#> [1] 26 36
dim(hr)
#> [1] 16 17

Initializing bipartite networks

Adjacency matrices are turned into the graph objects the plotting functions expect. bip_init_network() returns a network object; bip_init_igraph() returns an igraph object. Both encode the two modes and carry the edge weights.

hr.net <- bip_init_network(hr)   # network object
hr.ig  <- bip_init_igraph(hr)    # igraph object

hr.net
#>  Network attributes:
#>   vertices = 33 
#>   directed = FALSE 
#>   hyper = FALSE 
#>   loops = FALSE 
#>   multiple = FALSE 
#>   bipartite = 16 
#>   total edges= 121 
#>     missing edges= 0 
#>     non-missing edges= 121 
#> 
#>  Vertex attribute names: 
#>     mode vertex.names 
#> 
#>  Edge attribute names: 
#>     weights

Two small helpers underlie the plotting routines. vectorize() converts an adjacency matrix into a long, three-column edge table, and bip_edgewt() returns log-scaled edge weights suitable for mapping to line widths.

head(vectorize(hr))
#>                 Var2                 Var1 as.vector(mat)
#> 1 Pistacia.lentiscus   Sylvia.atricapilla           1370
#> 2 Pistacia.lentiscus         Sylvia.borin            336
#> 3 Pistacia.lentiscus   Erithacus.rubecula            317
#> 4 Pistacia.lentiscus Sylvia.melanocephala             49
#> 5 Pistacia.lentiscus        Turdus.merula            115
#> 6 Pistacia.lentiscus    Turdus.philomelos             14
summary(bip_edgewt(hr, x = 30))
#>    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
#>  0.3958  1.9520  4.5628  6.8343 11.2472 30.0000

Plotting with the network back end

bip_qtplot() draws a weighted (quantitative) bipartite network directly from the adjacency matrix, scaling edge widths by interaction strength.

bip_qtplot(hr)

bip_binplot() plots a network object; it is well suited to binary (presence/absence) webs.

bip_binplot(hr, hr.net)

Plotting with the igraph back end

bip_igplot() renders the same network through igraph, using the adjacency matrix (for node counts and edge scaling) and the igraph object.

bip_igplot(hr, hr.ig)

ggplot2 graphs with bip_ggnet

bip_ggnet() builds the plot within the ggplot2 framework via GGally::ggnet2. ggnet2 automatically detects two-mode graphs and understands arguments of the form [color, shape, size] = "mode", mapping the two modes to the ggnet2 classes. The returned object is a regular ggplot, so it can be extended with + in the usual way.

bip_ggnet(hr.net, hr)
#> Warning: Duplicated `override.aes` is ignored.

Colours for the two modes are controlled with the palette argument (its default is c(A = "grey", P = "gold")). You can override it and layer on extra ggplot2 geoms – here we colour by mode and add node labels.

col <- c("P" = "#FC9272", "A" = "#9ECAE1")
bip_ggnet(hr.net, hr,
          size = 7, shape = "mode", color = "mode",
          palette = col, layout.exp = 0.25) +
  geom_text(aes(label = network.vertex.names(hr.net)),
            color = "black", size = 3) +
  theme(legend.position = "none")

The railway layout

bip_railway() uses the classic two-parallel-columns layout familiar from the bipartite package, with each mode on its own axis.

bip_railway(hr, label = TRUE)

References

Bascompte, J. & Jordano, P. (2014) Mutualistic Networks. Princeton University Press, Princeton, NJ.

Pocock, M.J.O. et al. (2016) The visualisation of ecological networks, and their use as a tool for engagement, advocacy and management. Advances in Ecological Research 54, 41-85.

sessionInfo()
#> R version 4.5.3 (2026-03-11)
#> Platform: aarch64-apple-darwin20.0.0
#> Running under: macOS 27.0
#> 
#> Matrix products: default
#> BLAS/LAPACK: /Users/pedro/.claude-science/conda/envs/r-package-dev/lib/libopenblas.0.dylib;  LAPACK version 3.12.0
#> 
#> locale:
#> [1] C
#> 
#> time zone: Europe/Madrid
#> tzcode source: system (macOS)
#> 
#> attached base packages:
#> [1] stats     graphics  grDevices utils     datasets  methods   base     
#> 
#> other attached packages:
#> [1] igraph_2.3.3   network_1.20.0 ggbipart_0.1.3 ggplot2_4.0.3 
#> 
#> loaded via a namespace (and not attached):
#>  [1] Matrix_1.7-6          gtable_0.3.6          jsonlite_2.0.0       
#>  [4] dplyr_1.2.1           compiler_4.5.3        tidyselect_1.2.1     
#>  [7] ggstats_0.14.0        tidyr_1.3.2           jquerylib_0.1.4      
#> [10] scales_1.4.0          yaml_2.3.12           fastmap_1.2.0        
#> [13] GGally_2.4.0          lattice_0.23-1        coda_0.19-4.1        
#> [16] R6_2.6.1              generics_0.1.4        knitr_1.51           
#> [19] tibble_3.3.1          statnet.common_4.13.0 bslib_0.12.0         
#> [22] pillar_1.11.1         RColorBrewer_1.1-3    rlang_1.3.0          
#> [25] cachem_1.1.0          xfun_0.60             sass_0.4.10          
#> [28] S7_0.2.2              otel_0.2.0            cli_3.6.6            
#> [31] withr_3.0.3           magrittr_2.0.5        digest_0.6.39        
#> [34] grid_4.5.3            lifecycle_1.0.5       vctrs_0.7.3          
#> [37] sna_2.8               evaluate_1.0.5        glue_1.8.1           
#> [40] farver_2.1.2          purrr_1.2.2           rmarkdown_2.31       
#> [43] tools_4.5.3           pkgconfig_2.0.3       htmltools_0.5.9