├── .Rbuildignore ├── .gitattributes ├── .gitignore ├── .travis.yml ├── DESCRIPTION ├── LICENSE ├── LICENSE.md ├── NAMESPACE ├── NEWS.md ├── R ├── core.R ├── count_motifs.R ├── data.R ├── gaps_critical_edges.R ├── load_python.R ├── motif_zoo.R └── visualize_mnet.R ├── README.Rmd ├── README.md ├── _pkgdown.yml ├── cran-comments.md ├── data ├── directed_dummy_net.RData ├── dummy_net.RData ├── large_directed_dummy_net.RData ├── tidygraph_dummy_net.RData └── wetlands_mlnet.RData ├── inst ├── figures │ ├── motifr.png │ └── motifr.svg └── shiny_examples │ └── explore_zoo │ └── app.R ├── man ├── compare_to_baseline.Rd ├── count_motifs.Rd ├── critical_dyads.Rd ├── directed_dummy_net.Rd ├── dummy_net.Rd ├── edge_contribution.Rd ├── exemplify_motif.Rd ├── explore_motifs.Rd ├── figures │ ├── README-unnamed-chunk-11-1.png │ ├── README-unnamed-chunk-12-1.png │ ├── README-unnamed-chunk-13-1.png │ ├── README-unnamed-chunk-14-1.png │ ├── README-unnamed-chunk-5-1.png │ ├── README-unnamed-chunk-6-1.png │ ├── README-unnamed-chunk-7-1.png │ ├── README-unnamed-chunk-7-2.png │ ├── README-unnamed-chunk-8-1.png │ ├── README-unnamed-chunk-8-2.png │ ├── logo.png │ ├── logo.pngunnamed-chunk-4-1.svg │ ├── logo.pngunnamed-chunk-7-1.svg │ ├── logo.pngunnamed-chunk-8-1.svg │ └── logo.svg ├── identify_gaps.Rd ├── induced_level_subgraph.Rd ├── is.directed.Rd ├── large_directed_dummy_net.Rd ├── list_motifs.Rd ├── ml_net.Rd ├── motif_summary.Rd ├── motifs_distribution.Rd ├── plot_critical_dyads.Rd ├── plot_gaps.Rd ├── plot_gaps_or_critical_dyads.Rd ├── plot_mnet.Rd ├── show_motif.Rd ├── simulate_baseline.Rd ├── supported_classes.Rd ├── supported_signatures.Rd ├── tidygraph_dummy_net.Rd ├── to_py_graph.Rd └── update_motifr.Rd ├── motifr.Rproj ├── notes ├── baseline_example.png ├── demo.R ├── directed_dummy_net.R ├── gap_example.png ├── make_sticker.R ├── tidygraph_dummy_net.R └── viz_example.png ├── pkgdown └── favicon │ ├── apple-touch-icon-120x120.png │ ├── apple-touch-icon-152x152.png │ ├── apple-touch-icon-180x180.png │ ├── apple-touch-icon-60x60.png │ ├── apple-touch-icon-76x76.png │ ├── apple-touch-icon.png │ ├── favicon-16x16.png │ ├── favicon-32x32.png │ └── favicon.ico ├── tests ├── testthat.R └── testthat │ ├── test-core.R │ ├── test-count_motifs.R │ └── test-gaps_critical_edges.R └── vignettes ├── .gitignore ├── README.md ├── motif_zoo.Rmd ├── motif_zoo.Rmd.orig ├── motif_zoo_show_motif2120IID-1.svg ├── motif_zoo_show_motif_22IID-1.svg ├── motif_zoo_show_motif_IIC-1.svg ├── precompile.R ├── random_baselines.Rmd ├── random_baselines.Rmd.orig ├── random_baselines_chunk3-1.svg ├── random_baselines_chunk3-2.svg ├── random_baselines_chunk6-1.svg ├── random_baselines_chunk6-2.svg ├── random_baselines_chunk6-3.svg ├── random_baselines_chunk7-1.svg └── random_baselines_chunk8-1.svg /.Rbuildignore: -------------------------------------------------------------------------------- 1 | ^.*\.Rproj$ 2 | ^\.Rproj\.user$ 3 | ^LICENSE\.md$ 4 | ^README\.Rmd$ 5 | ^notes$ 6 | ^doc$ 7 | ^Meta$ 8 | ^_pkgdown\.yml$ 9 | ^docs$ 10 | ^pkgdown$ 11 | ^\.travis\.yml$ 12 | ^cran-comments\.md$ 13 | ^CRAN-RELEASE$ 14 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .Rproj.user 2 | inst/doc 3 | .Rhistory 4 | 5 | doc 6 | Meta 7 | docs/* 8 | 9 | .Rapp.history 10 | .RData 11 | .Ruserdata 12 | *-Ex.R 13 | /*.tar.gz 14 | /*.Rcheck/ 15 | .Rproj.user/ 16 | vignettes/*.html 17 | vignettes/*.pdf 18 | .httr-oauth 19 | *_cache/ 20 | /cache/ 21 | *.utf8.md 22 | *.knit.md 23 | .Renviron 24 | .DS_Store 25 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | dist: bionic 2 | language: r 3 | cache: packages 4 | sudo: required 5 | warnings_are_errors: false 6 | 7 | r_packages: 8 | - testthat 9 | - reticulate 10 | - pkgdown 11 | - knitr 12 | - devtools 13 | 14 | # https://www.jumpingrivers.com/blog/r-packages-travis-github-actions-rstudio/ 15 | before_install: 16 | - python3 -m pip install numpy networkx sma 17 | - R -e 'source("https://install-github.me/r-lib/remotes")' 18 | # - echo "options(repos = c(CRAN = 'https://packagemanager.rstudio.com/all/__linux__/bionic/latest'))" >> ~/.Rprofile.site 19 | # - echo "options(HTTPUserAgent = paste0('R/', getRversion(), ' R (', 20 | # paste(getRversion(), R.version['platform'], R.version['arch'], R.version['os']), 21 | # ')'))" >> ~/.Rprofile.site 22 | 23 | after_success: 24 | # re-build pre-build vignettes since they have not been build properly yet 25 | - echo "Pre-building vignette from ${PKG_TARBALL}" 26 | - R CMD INSTALL "${PKG_TARBALL}" 27 | - Rscript vignettes/precompile.R 28 | - Rscript -e 'devtools::build_vignettes()' 29 | # build website 30 | - Rscript -e 'pkgdown::build_site(run_dont_run = TRUE)' 31 | 32 | deploy: 33 | provider: pages 34 | skip_cleanup: true 35 | github_token: $GITHUB_PAT 36 | keep_history: true 37 | local-dir: docs 38 | on: 39 | branch: master 40 | 41 | addons: 42 | apt: 43 | update: true 44 | packages: 45 | - python3 46 | - python3-pip 47 | - python3-dev 48 | - python3-setuptools 49 | - libmagick++-dev 50 | - libharfbuzz-dev 51 | - libfribidi-dev 52 | - libgit2-dev 53 | -------------------------------------------------------------------------------- /DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: motifr 2 | Title: Motif Analysis in Multi-Level Networks 3 | Version: 1.0.0 4 | Date: 2020-12-01 5 | Authors@R: 6 | c(person(given = "Mario", 7 | family = "Angst", 8 | role = c("aut", "cre"), 9 | email = "mario.angst@gmail.com", 10 | comment = c(ORCID = "0000-0002-8297-9827")), 11 | person(given = "Tim", 12 | family = "Seppelt", 13 | role = "aut", 14 | email = "seppelt@cs.rwth-aachen.de", 15 | comment = c(ORCID = "0000-0002-6447-0568"))) 16 | Description: Tools for motif analysis in multi-level networks. 17 | Multi-level networks combine multiple networks in one, e.g. 18 | social-ecological networks. Motifs are small configurations of nodes 19 | and edges (subgraphs) occurring in networks. 'motifr' can visualize 20 | multi-level networks, count multi-level network motifs and compare 21 | motif occurrences to baseline models. It also identifies contributions 22 | of existing or potential edges to motifs to find critical or missing 23 | edges. The package is in many parts an R wrapper for the excellent 24 | 'SESMotifAnalyser' 'Python' package written by Tim Seppelt. 25 | License: MIT + file LICENSE 26 | URL: https://marioangst.github.io/motifr/ 27 | BugReports: https://github.com/marioangst/motifr/issues 28 | Depends: 29 | R (>= 3.5.0) 30 | Imports: 31 | dplyr, 32 | ggplot2 (>= 2.1.0), 33 | ggraph, 34 | igraph, 35 | intergraph, 36 | network, 37 | purrr, 38 | RColorBrewer, 39 | reshape2, 40 | reticulate, 41 | rlang, 42 | scales, 43 | tibble, 44 | tidygraph, 45 | shiny 46 | Suggests: 47 | ergm, 48 | knitr, 49 | pkgdown, 50 | rmarkdown, 51 | testthat (>= 2.1.0) 52 | VignetteBuilder: 53 | knitr 54 | Config/reticulate: 55 | list( 56 | packages = list( 57 | list(package = "sma", pip = TRUE), 58 | list(package = "networkx", pip = TRUE) 59 | ) 60 | ) 61 | Encoding: UTF-8 62 | Language: en-GB 63 | LazyData: true 64 | RoxygenNote: 7.1.1 65 | SystemRequirements: Python (>= 3.0.0), numpy, pandas 66 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | YEAR: 2020 2 | COPYRIGHT HOLDER: Mario Angst 3 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # MIT License 2 | 3 | Copyright (c) 2019 Mario Angst 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | Reference the author 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | -------------------------------------------------------------------------------- /NAMESPACE: -------------------------------------------------------------------------------- 1 | # Generated by roxygen2: do not edit by hand 2 | 3 | export(compare_to_baseline) 4 | export(count_motifs) 5 | export(critical_dyads) 6 | export(edge_contribution) 7 | export(exemplify_motif) 8 | export(explore_motifs) 9 | export(identify_gaps) 10 | export(induced_level_subgraph) 11 | export(is.directed) 12 | export(list_motifs) 13 | export(motif_summary) 14 | export(motifs_distribution) 15 | export(plot_critical_dyads) 16 | export(plot_gaps) 17 | export(plot_mnet) 18 | export(show_motif) 19 | export(simulate_baseline) 20 | export(supported_classes) 21 | export(supported_signatures) 22 | export(to_py_graph) 23 | export(update_motifr) 24 | importFrom(rlang,.data) 25 | -------------------------------------------------------------------------------- /NEWS.md: -------------------------------------------------------------------------------- 1 | # motifr 1.0.0 _2020-10-01_ 2 | 3 | * First release on CRAN 4 | 5 | --- 6 | 7 | # motifr 0.2.0 8 | 9 | * First stable release 10 | 11 | --- 12 | -------------------------------------------------------------------------------- /R/core.R: -------------------------------------------------------------------------------- 1 | #' Translate multi-level statnet or igraph network object to Python networkx 2 | #' object 3 | #' 4 | #' The function \code{motifr::is.directed} is used to determine whether the 5 | #' provided network is directed (if \code{directed = FALSE}). 6 | #' 7 | #' The nodal attribute specified by \code{lvl_attr} indicates the levels of the 8 | #' nodes. Values are automatically converted to integers. Levels must be 9 | #' numbered starting with 0, 1, …. 10 | #' 11 | #' @param g statnet or igraph network object 12 | #' @param lvl_attr character vector specifying the attribute name where level 13 | #' information is stored in \code{net}. 14 | #' @param relabel should nodes be relabelled with statnet \code{vertex.names} or 15 | #' igraph nodal attribute \code{name} 16 | #' @param directed whether the graph shall be treated as a directed graph. Per 17 | #' default (\code{NULL}), this is determined automatically using the structure 18 | #' of the provided network object 19 | #' 20 | #' @return Python networkx graph object 21 | #' @export 22 | #' 23 | #' @examples 24 | #' \dontrun{ 25 | #' to_py_graph(motifr::dummy_net, lvl_attr = "sesType") 26 | #' } 27 | to_py_graph <- function(g, lvl_attr, relabel = TRUE, directed = NULL) { 28 | if (network::is.network(g)) { 29 | # function for translating a statnet network object into a Python compatible 30 | # networkx object 31 | adjacency_matrix <- network::as.matrix.network(g) 32 | attribute_names <- as.list(network::list.vertex.attributes(g)) 33 | attribute_values <- lapply( 34 | network::list.vertex.attributes(g), 35 | function(x) network::get.vertex.attribute(g, x) 36 | ) 37 | vertex_names <- network::network.vertex.names(g) 38 | } else if (igraph::is.igraph(g)) { 39 | adjacency_matrix <- igraph::as_adjacency_matrix(g, sparse = FALSE) 40 | attributes <- as.data.frame(igraph::vertex.attributes(g)) 41 | attribute_names <- as.list(colnames(attributes)) 42 | attribute_values <- lapply( 43 | attribute_names, 44 | function(x) attributes[[x]] 45 | ) 46 | if (is.null(igraph::V(g)$name)) { 47 | # vertices don't carry names, uses igraph's internal numbering 48 | vertex_names <- 1:igraph::gorder(g) 49 | } else { 50 | vertex_names <- igraph::V(g)$name 51 | } 52 | } else { 53 | stop(paste("Provided network object is of unsupported format:", class(g))) 54 | } 55 | 56 | if (is.null(directed)) { 57 | directed <- motifr::is.directed(g) 58 | } else if (directed == TRUE && !motifr::is.directed(g)) { 59 | warning(paste( 60 | "Attempting to treat an undirected network as directed.", 61 | "This might lead to unintended results." 62 | )) 63 | } 64 | 65 | py_g <- pkg.env$sma$translateGraph(adjacency_matrix, 66 | attribute_names, 67 | attribute_values, 68 | lvl_attr, 69 | directed = directed 70 | ) 71 | 72 | if (relabel == TRUE) { 73 | # JS: renaming in here right now, 74 | # but will suggest update to rbridge.py to do in Python 75 | node_names <- 76 | reticulate::py_dict( 77 | keys = as.integer(0:(py_g$number_of_nodes() - 1)), 78 | values = vertex_names 79 | ) 80 | py_g <- pkg.env$nx$relabel_nodes(py_g, mapping = node_names) 81 | } 82 | return(py_g) 83 | } 84 | 85 | #' Checks whether the given network is directed 86 | #' 87 | #' Placeholder function for the corresponding functions of the various supported 88 | #' network formats. For example, this function calls 89 | #' \code{network::is.directed()} on \code{network} objects and 90 | #' \code{igraph::is.directed()} on \code{igraph} objects. 91 | #' 92 | #' @param net the network 93 | #' @return whether the given network is directed 94 | #' @export 95 | #' 96 | #' @examples 97 | #' is.directed(motifr::ml_net) 98 | is.directed <- function(net) { 99 | if (network::is.network(net)) { 100 | return(network::is.directed(net)) 101 | } else if (igraph::is.igraph(net)) { 102 | return(igraph::is.directed(net)) 103 | } else { 104 | stop(paste("Provided network object is of unsupported format:", class(net))) 105 | } 106 | } 107 | 108 | #' Returns subgraph induced by one level of the network 109 | #' 110 | #' This function is intended to be used together with \code{simulate_baseline()} 111 | #' for partial ERGM models. Currently, only \code{network} objects are supported 112 | #' as input. 113 | #' 114 | #' @param net the network 115 | #' @param level the (number of the) level 116 | #' @param lvl_attr name of the nodal attribute specifying the level 117 | #' @return induced subgraph as \code{network} object. 118 | #' @export 119 | #' @examples 120 | #' 121 | #' subgraph_actors <- induced_level_subgraph(motifr::ml_net, 1) 122 | #' plot_mnet(subgraph_actors, label = TRUE) 123 | induced_level_subgraph <- function(net, level, lvl_attr = "sesType") { 124 | if (!network::is.network(net)) { 125 | stop(paste( 126 | "motifr::induced_level_subgraph is only implemented for graph", 127 | "objects stemming from the network package." 128 | )) 129 | } 130 | levels <- network::get.vertex.attribute(net, lvl_attr) 131 | indices <- which(levels == level) 132 | subgraph <- network::get.inducedSubgraph(net, indices) 133 | return(subgraph) 134 | } 135 | -------------------------------------------------------------------------------- /R/count_motifs.R: -------------------------------------------------------------------------------- 1 | #' Count multi-level motifs 2 | #' 3 | #' @param net A network object with a node attribute specifying the 4 | #' level of each node 5 | #' @param motifs a list of motif identifiers which shall be counted, e.g. 6 | #' \code{list("1,2[I.C]")} 7 | #' @param lvl_attr character vector specifying the vertex attribute name where 8 | #' level information is stored in \code{net} 9 | #' @param assume_sparse whether the network shall be assumed to be sparse (for 10 | #' optimization), default TRUE 11 | #' @param omit_total_result whether total results shall be omitted, default 12 | #' FALSE 13 | #' @param directed whether the graph shall be treated as a directed graph. Per 14 | #' default (\code{NULL}), this is determined automatically using the structure 15 | #' of the provided network object 16 | #' 17 | #' @return data frame with a column containing motif identifier strings and one 18 | #' column containing motif counts 19 | #' @export 20 | #' 21 | #' @examples 22 | #' \dontrun{ 23 | #' count_motifs(ml_net, 24 | #' lvl_attr = c("sesType"), 25 | #' motifs = list("1,2[I.C]", "1,2[II.C]", "2,1[I.C]", "2,1[II.C]"), 26 | #' directed = FALSE 27 | #' ) 28 | #' } 29 | count_motifs <- function(net, 30 | motifs, 31 | lvl_attr = c("sesType"), 32 | assume_sparse = TRUE, 33 | omit_total_result = TRUE, 34 | directed = NULL) { 35 | # convert net to python object 36 | py_g <- motifr::to_py_graph(net, 37 | lvl_attr = lvl_attr, 38 | directed = directed 39 | ) 40 | 41 | # make sure motifs is list 42 | motifs <- as.list(motifs) 43 | 44 | # call counter 45 | counted <- pkg.env$sma$countMotifsAutoR(py_g, 46 | motifs, 47 | assume_sparse = assume_sparse, 48 | omit_total_result = omit_total_result 49 | ) 50 | df <- data.frame(motif = names(counted), count = unlist(counted)) 51 | return(df) 52 | } 53 | 54 | #' Compute statistical properties (expectation and variance) of the distribution 55 | #' of motifs in a baseline model 56 | #' 57 | #' This function supports the Erdős-Rényi Model (\code{erdos_renyi}) and the the 58 | #' Actor’s Choice Model (\code{actors_choice}). The model can be specified using 59 | #' the \code{model} parameter. The Erdős-Rényi Model can be used without 60 | #' providing further parameters. In case of the Actor’s Choice Model a level of 61 | #' the given network can be specified which is only level assumed to be 62 | #' variable. All other levels are assumed to be fixed. Per default, \code{level 63 | #' = -1}, the first level carrying two nodes in the signature of the motif is 64 | #' selected as variable level. Set the \code{level} parameter to the value of 65 | #' the \code{lvl_attr} of the nodes in the desired level to specify the level 66 | #' manually. 67 | #' 68 | #' @param net network object 69 | #' @param lvl_attr character vector specifying the attribute name where level 70 | #' information is stored in \code{net}. 71 | #' @param motifs list of motif identifiers describing the motifs whose 72 | #' distribution shall be analysed 73 | #' @param model baseline model to be used. options are "erdos_renyi" and "actors_choice". 74 | #' Defaults to "erdos_renyi". 75 | #' @param level Additional parameter to set the level to vary for the 76 | #' actors_choice model manually. All other levels are held fixed. 77 | #' @param omit_total_result whether total results shall be omitted 78 | #' @param directed whether the graph shall be treated as a directed graph. Per 79 | #' default (\code{NULL}), this is determined automatically using the structure 80 | #' of the provided network object 81 | #' 82 | #' @return data frame with one column giving names of motif identifers and two 83 | #' column giving expectation and variances per motif. For other motifs, 84 | #' expectations are computed but variances are returned as NaN. 85 | #' @export 86 | #' 87 | #' @examples 88 | #' \dontrun{ 89 | #' motifs_distribution(ml_net, motif = list("1,2[I.C]"), directed = FALSE) 90 | #' } 91 | motifs_distribution <- function(net, 92 | motifs, 93 | lvl_attr = "sesType", 94 | model = "erdos_renyi", 95 | level = -1, 96 | omit_total_result = TRUE, 97 | directed = NULL) { 98 | supported_models <- c("erdos_renyi", "actors_choice") 99 | if (!(model %in% supported_models)) { 100 | stop(paste( 101 | "Model", model, "is not supported. Choose one of", 102 | paste(supported_models, collapse = ", "), 103 | "or use simulate_baseline()." 104 | )) 105 | } 106 | 107 | # convert net to python object 108 | py_g <- motifr::to_py_graph(net, 109 | lvl_attr = lvl_attr, 110 | directed = directed 111 | ) 112 | 113 | # make sure motifs is list 114 | motifs <- as.list(motifs) 115 | 116 | # call counter 117 | result <- pkg.env$sma$distributionMotifsAutoR(py_g, 118 | motifs, 119 | model = model, 120 | level = level, 121 | omit_total_result = omit_total_result 122 | ) 123 | df <- data.frame( 124 | motif = names(result), 125 | expectation = unlist(result[1, ]), 126 | variance = unlist(result[2, ]) 127 | ) 128 | return(df) 129 | } 130 | 131 | #' Summary for motif counts and Erdős-Rényi distribution 132 | #' 133 | #' Returns a data frame with counts and statistical properties (expectation, 134 | #' variances) of six selected motifs in the given network. Note that this 135 | #' function implicitly assumes that the network is undirected, cf. 136 | #' \code{motifr::to_py_graph}. 137 | #' 138 | #' @param net network object 139 | #' @param lvl_attr character vector specifying the attribute name where level 140 | #' information is stored in \code{net}. 141 | #' 142 | #' @return dataframe with motif counts, expectations and variances for set of 143 | #' selected motifs 144 | #' @export 145 | #' 146 | #' @examples 147 | #' \dontrun{ 148 | #' motif_summary(ml_net) 149 | #' } 150 | motif_summary <- function(net, 151 | lvl_attr = c("sesType")) { 152 | # exquisite selection of motifs 153 | # all motifs are undirected, hence set directed = FALSE and suppose graph is 154 | # undirected 155 | motifs <- c( 156 | "1,2[I.C]", "1,2[II.C]", 157 | "2,1[I.C]", "2,1[II.C]", 158 | "2,2[III.C]", "2,2[III.D]" 159 | ) 160 | # count and compute distribution parameters 161 | counts <- motifr::count_motifs(net, 162 | lvl_attr, 163 | motifs = motifs, 164 | omit_total_result = TRUE, 165 | directed = FALSE 166 | ) 167 | distribution <- motifr::motifs_distribution(net, 168 | lvl_attr, 169 | motifs = motifs, 170 | omit_total_result = TRUE, 171 | directed = FALSE 172 | ) 173 | 174 | # reformat data 175 | result <- merge(counts, distribution) 176 | return(result) 177 | } 178 | 179 | #' Returns an example for a motif found in a given network 180 | #' 181 | #' @param net network object 182 | #' @param motif motif identifier string for the motif 183 | #' @param lvl_attr character vector specifying the attribute name where level 184 | #' information is stored in \code{net}. 185 | #' @param directed whether the graph shall be treated as a directed graph. Per 186 | #' default (\code{NULL}), this is determined automatically using the structure 187 | #' of the provided network object 188 | #' 189 | #' @return vector of nodes in the motif 190 | #' @seealso \code{motifr::show_motif} 191 | #' @export 192 | #' 193 | #' @examples 194 | #' \dontrun{ 195 | #' exemplify_motif(ml_net, motif = "1,2[I.C]", directed = FALSE) 196 | #' } 197 | exemplify_motif <- function(net, 198 | motif, 199 | lvl_attr = "sesType", 200 | directed = NULL) { 201 | # convert net to python object 202 | py_g <- motifr::to_py_graph(net, lvl_attr = lvl_attr, directed = directed) 203 | motif <- pkg.env$sma$exemplifyMotif(py_g, motif) 204 | return(purrr::simplify(motif)) 205 | } 206 | 207 | #' Plots an example for a motif with given motif identifier string taken from 208 | #' the given graph. 209 | #' 210 | #' If no network is provided, a motif in a dummy network 211 | #' (\code{motifr::dummy_net} or \code{motifr::large_directed_dummy_net}) will be 212 | #' shown. 213 | #' 214 | #' @param motif motif identifier string for the motif 215 | #' @param net network object 216 | #' @param lvl_attr character vector specifying the attribute name where level 217 | #' information is stored in \code{net}. 218 | #' @param directed whether the graph shall be treated as a directed graph. Per 219 | #' default (\code{NULL}), this is determined automatically using the structure 220 | #' of the provided network object 221 | #' @param ... additional arguments to be passed to plotting function (e.g. 222 | #' \code{label = TRUE}) 223 | #' @return plot 224 | #' @seealso \code{motifr::exemplify_motif} 225 | #' @export 226 | #' 227 | #' @examples 228 | #' \dontrun{ 229 | #' show_motif("1,2[I.C]", net = ml_net, directed = FALSE, label = TRUE) 230 | #' } 231 | show_motif <- function(motif, 232 | net = NULL, 233 | lvl_attr = c("sesType"), 234 | directed = NULL, 235 | ...) { 236 | if (is.null(net)) { 237 | if (is.null(directed) || directed == FALSE) { 238 | net <- motifr::dummy_net 239 | } else { 240 | net <- motifr::large_directed_dummy_net 241 | } 242 | } 243 | if (igraph::is.igraph(net)) { 244 | net <- intergraph::asNetwork(net) 245 | } 246 | motif_names <- motifr::exemplify_motif( 247 | net = net, motif = motif, 248 | lvl_attr = lvl_attr, 249 | directed = directed 250 | ) 251 | if (is.null(motif_names)) { 252 | stop(paste( 253 | "The chosen motif", motif, 254 | "does not exist in the supplied network." 255 | )) 256 | } 257 | vertices <- network::get.vertex.attribute(net, "vertex.names") 258 | indices <- match(motif_names, vertices) 259 | subgraph <- network::get.inducedSubgraph(net, indices) 260 | p <- motifr::plot_mnet(subgraph, 261 | lvl_attr = lvl_attr, directed = directed, 262 | ... 263 | ) 264 | return(p) 265 | } 266 | 267 | #' Simulate a baseline baseline model 268 | #' 269 | #' A baseline distribution of motif counts from a specified number of networks 270 | #' using a specified baseline model is computed. Options for the baseline model are 271 | #' - Erdős–Rényi 272 | #' - Actor's choice 273 | #' - Fixed density 274 | #' - Providing an ERGM fit for the whole network 275 | #' - Providing a partial ERGM fit (for only one level) 276 | #' 277 | #' Note that when using the Actor's Choice model this function does not choose 278 | #' the variable level automatically. Use the \code{level} parameter to provide a 279 | #' valid level. 280 | #' 281 | #' When using (partial) ERGM the parameter \code{net} is not used. Random 282 | #' networks are sampled in R using the \code{ergm_model} parameter. 283 | #' 284 | #' @param net network object 285 | #' @param motifs list of motif identifier strings 286 | #' @param n number of random graphs 287 | #' @param lvl_attr character string specifying the attribute name where level 288 | #' information is stored in \code{net}. 289 | #' @param assume_sparse whether the random graphs shall be assumed to be sparse. 290 | #' used to find ideal counting function. defaults to TRUE. 291 | #' @param model baseline model to be used. Options are 'erdos_renyi', 292 | #' 'fixed_densities', 'actors_choice', 'ergm' and 'partial_ergm'. See 293 | #' \code{vignette("random_baselines")} for more details. Defaults to 294 | #' 'erdos_renyi'. 295 | #' @param level lvl_attr of the variable level for the Actor's Choice model and 296 | #' for partial ERGM 297 | #' @param ergm_model ergm model as for example fitted by calling 298 | #' \code{ergm::ergm()}. Used when model is set to 'ergm' or 'partial_ergm' to 299 | #' sample random networks. 300 | #' @param directed whether the graph shall be treated as a directed graph. Per 301 | #' default (\code{NULL}), this is determined automatically using the structure 302 | #' of the provided network object 303 | #' 304 | #' @return data frame with one column for each motif identifier string and one 305 | #' row for every computed random graph 306 | #' @export 307 | #' 308 | #' @examples 309 | #' \dontrun{ 310 | #' simulate_baseline(ml_net, list("1,2[I.C]"), n = 10, directed = FALSE) 311 | #' } 312 | simulate_baseline <- function(net, 313 | motifs, 314 | n = 10, 315 | lvl_attr = "sesType", 316 | assume_sparse = TRUE, 317 | model = "erdos_renyi", 318 | level = -1, 319 | ergm_model = NULL, 320 | directed = NULL) { 321 | # make sure motifs is list 322 | motifs <- as.list(motifs) 323 | 324 | supported_models <- c( 325 | "erdos_renyi", "fixed_densities", "actors_choice", 326 | "ergm", "partial_ergm" 327 | ) 328 | if (!(model %in% supported_models)) { 329 | stop(paste( 330 | "Model", model, "is not supported. Choose one of", 331 | paste(supported_models, collapse = ", ") 332 | )) 333 | } 334 | if (model == "ergm" || model == "partial_ergm") { 335 | if (!requireNamespace("ergm", quietly = TRUE)) { 336 | stop("Package \"ergm\" needed for this function to work. Please install it with install.packages(ergm).", 337 | call. = FALSE 338 | ) 339 | } 340 | if (!ergm::is.ergm(ergm_model)) { 341 | stop("Please provde a valid ergm model when using (partial) ERGM.") 342 | } 343 | if (model == "partial_ergm") { 344 | if (level < 0) { 345 | stop("Please provde a valid level when using partial ERGM.") 346 | } 347 | if (!network::is.network(net)) { 348 | stop("Partial ERGM model needs valid network parameter.") 349 | } 350 | # preparing partially truncated network for re-adding edges from simulation 351 | truncated_net <- network::network.copy(net) 352 | levels <- network::get.vertex.attribute(truncated_net, lvl_attr) 353 | indices <- which(levels == level) 354 | for (i in indices) { 355 | dyads <- network::get.dyads.eids( 356 | truncated_net, 357 | replicate(length(indices), i), 358 | indices 359 | ) 360 | ids <- dyads[lapply(dyads > 0, is.na) == FALSE] 361 | network::delete.edges(truncated_net, unlist(ids)) 362 | } 363 | } 364 | # let's do the job ourselves 365 | result <- data.frame() 366 | for (i in 1:n) { 367 | sample <- stats::simulate(ergm_model) 368 | if (model == "partial_ergm") { 369 | # partial model provided: we'll copy the sample back into truncated_met 370 | total_sample <- network::network.copy(truncated_net) 371 | translations <- match( 372 | network::get.vertex.attribute(sample, "vertex.names"), 373 | network::get.vertex.attribute(total_sample, "vertex.names") 374 | ) 375 | edge_list_head <- network::as.edgelist(sample)[, 1] 376 | edge_list_tail <- network::as.edgelist(sample)[, 2] 377 | t_edge_list_head <- lapply(edge_list_head, function(x) translations[x]) 378 | t_edge_list_tail <- lapply(edge_list_tail, function(x) translations[x]) 379 | network::add.edges(total_sample, t_edge_list_head, t_edge_list_tail) 380 | 381 | sample <- total_sample 382 | } 383 | counts <- motifr::count_motifs(sample, 384 | motifs = motifs, 385 | lvl_attr = lvl_attr, 386 | assume_sparse = assume_sparse, 387 | directed = directed 388 | ) 389 | result <- rbind(result, counts$count) 390 | if (i == 1) { 391 | colnames(result) <- counts$motif 392 | } 393 | } 394 | return(result) 395 | } else { 396 | # let sma do the job 397 | if (model == "actors_choice") { 398 | if (level < 0) { 399 | stop("Please provide a valid level when using an Actor's Choice model") 400 | } 401 | } 402 | py_g <- motifr::to_py_graph(net, 403 | lvl_attr = lvl_attr, 404 | directed = directed 405 | ) 406 | 407 | result <- pkg.env$sma$simulateBaselineAutoR(py_g, 408 | motifs, 409 | n = n, 410 | assume_sparse = assume_sparse, 411 | model = model, 412 | level = level 413 | ) 414 | df <- data.frame(result, check.names = FALSE) 415 | return(df) 416 | } 417 | } 418 | 419 | #' Compare motif occurence in empirical network to occurence in a baseline model 420 | #' 421 | #' This function plots a comparison of the motif counts in a given network with the motif 422 | #' counts in a baseline model. 423 | #' 424 | #' Note that when using the Actor's Choice model this function does not choose 425 | #' the variable level automatically. Use the \code{level} parameter to provide a 426 | #' valid level. 427 | #' 428 | #' When using ERGM the parameter \code{net} is not used. Networks to create the 429 | #' baseline from are sampled in R using the \code{ergm_model} parameter. 430 | #' 431 | #' @param net network object 432 | #' @param motifs list of motif identifier strings 433 | #' @param n number of random graphs used in baseline model 434 | #' @param lvl_attr character vector specifying the attribute name where level 435 | #' information is stored in \code{net}. 436 | #' @param assume_sparse whether the random graphs shall be assumed to be sparse. 437 | #' used to find ideal counting function 438 | #' @param model baseline model to be used. Options are 'erdos_renyi', 'actors_choice', 439 | #' 'ergm', 'partial_ergm' and fixed_densities'. 440 | #' See \code{vignette("random_baselines")} for more details. 441 | #' Defaults to 'erdos_renyi'. 442 | #' @param level lvl_attr of the variable level for the Actor's Choice model 443 | #' @param ergm_model ergm model as for example fitted by calling 444 | #' \code{ergm::ergm()} on the empirically observed network. 445 | #' Needs to be supplied when model is set to ergm. 446 | #' @param directed whether the graph shall be treated as a directed graph. Per 447 | #' default (\code{NULL}), this is determined automatically using the structure 448 | #' of the provided network object 449 | #' 450 | #' @return data frame with one row for each motif identifier string and one row 451 | #' for every computed random graph 452 | #' @export 453 | #' 454 | #' @examples 455 | #' \dontrun{ 456 | #' compare_to_baseline(ml_net, list("1,2[I.C]", "1,2[II.C]"), directed = FALSE) 457 | #' } 458 | compare_to_baseline <- function(net, 459 | motifs, 460 | n = 10, 461 | lvl_attr = "sesType", 462 | assume_sparse = TRUE, 463 | model = "erdos_renyi", 464 | level = -1, 465 | ergm_model = NULL, 466 | directed = NULL) { 467 | simulation <- motifr::simulate_baseline(net, 468 | motifs, 469 | n = n, 470 | lvl_attr = lvl_attr, 471 | assume_sparse = assume_sparse, 472 | model = model, 473 | level = level, 474 | ergm_model = ergm_model, 475 | directed = directed 476 | ) 477 | count <- motifr::count_motifs(net, 478 | motifs, 479 | lvl_attr = lvl_attr, 480 | assume_sparse = assume_sparse, 481 | omit_total_result = TRUE, 482 | directed = directed 483 | ) 484 | 485 | plot_df <- suppressMessages(reshape2::melt(simulation, variable.name = "motif")) 486 | # plot_df_count <- suppressMessages(reshape2::melt(count)) 487 | 488 | p <- 489 | ggplot2::ggplot(plot_df, ggplot2::aes_(x = ~value)) + 490 | ggplot2::facet_wrap(~motif, scales = "free") + 491 | ggplot2::geom_histogram(fill = "gray", bins = 50) + 492 | ggplot2::geom_vline(data = count, ggplot2::aes_(xintercept = ~count)) + 493 | ggplot2::theme_minimal() + 494 | ggplot2::xlab(paste( 495 | "Simulated (gray histogram) versus \n actual (solid line) motif counts \n", 496 | sprintf("n = %d iterations, \n Baseline model: %s", n, model) 497 | )) 498 | 499 | return(p) 500 | } 501 | 502 | #' Lists motifs of a given class or all motifs with a given signature 503 | #' 504 | #' Returns a dataframe with one row for each instance of the motif specified by 505 | #' the given motif identifier string. If the identifier string specifies a motif 506 | #' class, e.g. \code{1,2[I.A]}, then only motifs of the given class are returned. 507 | #' If the identifier string specifies a signature, e.g. \code{1,2}, then a full 508 | #' list of all motifs of this signature is returned. In the latter case, the 509 | #' dataframe contains an additional column stating the classes of the motifs. 510 | 511 | #' The naming scheme of the columns is as follows: Each column is called 512 | #' \code{levelA_nodeB} where \code{A} is the \code{lvl_attr} of the nodes in the column 513 | #' and \code{B} the index of the nodes among the nodes on the same level. This 514 | #' index stems from the internal order of the nodes and does not carry any 515 | #' specific meaning. 516 | #' 517 | #' @param net network object 518 | #' @param identifier motif identifier string (with or without class, see above) 519 | #' @param lvl_attr character vector specifying the attribute name where level 520 | #' information is stored in \code{net}. 521 | #' @param directed whether the graph shall be treated as a directed graph. Per 522 | #' default (\code{NULL}), this is determined automatically using the structure 523 | #' of the provided network object 524 | #' 525 | #' @return data frame with one row for each motif 526 | #' @export 527 | #' 528 | #' @examples 529 | #' \dontrun{ 530 | #' head(list_motifs(ml_net, "1,2[I.C]", directed = FALSE)) 531 | #' } 532 | list_motifs <- function(net, 533 | identifier, 534 | lvl_attr = "sesType", 535 | directed = NULL) { 536 | py_g <- to_py_graph(net, lvl_attr = lvl_attr, directed = directed) 537 | df <- pkg.env$sma$motifTable(py_g, identifier) 538 | return(df) 539 | } 540 | -------------------------------------------------------------------------------- /R/data.R: -------------------------------------------------------------------------------- 1 | #' Two-level network example (wetlands management) 2 | #' 3 | #' A statnet network object based on empirical data about actors and their 4 | #' activities in a case study of Swiss wetlands management 5 | #' 6 | #' @format Statnet network object with 132 nodes and 566 edges on two levels. 7 | #' One network level contains actors, a second network level contains 8 | #' activities. Links between actors indicate collaboration among actors. Links 9 | #' between actors and activities indicate that an actor is active in a given 10 | #' activity. Links between activities indicate that the activities are 11 | #' causally interdependent. The network contains two variables to describe 12 | #' nodes/ vertices. \describe{ \item{vertex.names}{node labes} 13 | #' \item{sesType}{Binary variable specifying network levels for every node (1 14 | #' = node is a social node (actor) , 0 = node is a non-social node (an 15 | #' activity))} ... } 16 | #' @source Surveys and expert interviews in a Swiss wetland. Data is anonymized 17 | #' and should only be used for exemplary purposes. 18 | #' @examples 19 | #' plot_mnet(ml_net) 20 | "ml_net" 21 | 22 | #' Three-level network dummy example 23 | #' 24 | #' A simple statnet network object based on dummy data. 25 | #' 26 | #' @format Statnet network object with 60 nodes and 1035 edges on three levels. 27 | #' The network contains two variables to describe nodes/ vertices. 28 | #' \describe{ 29 | #' \item{vertex.names}{node labes} 30 | #' \item{sesType}{Categorical variable specifying network levels for every node (levels are 0,1 and 2)} 31 | #' ... 32 | #' } 33 | #' @source Dummy data 34 | #' \url{https://gitlab.com/t.seppelt/sesmotifanalyser/-/tree/master/test/data} 35 | #' @examples 36 | #' plot_mnet(dummy_net) 37 | "dummy_net" 38 | 39 | #' Two-level directed network dummy example 40 | #' 41 | #' Simple igraph network object based on dummy data 42 | #' 43 | #' @format igraph network object 44 | #' @source Dummy data 45 | #' \url{https://gitlab.com/t.seppelt/sesmotifanalyser/-/tree/master/test/data}, 46 | #' \url{https://github.com/marioangst/motifr/blob/master/notes/directed_dummy_net.R} 47 | #' @examples 48 | #' plot_mnet(directed_dummy_net) 49 | "directed_dummy_net" 50 | 51 | #' Two-level tidygraph network example 52 | #' 53 | #' Simple \code{tidygraph} network object for testing 54 | #' 55 | #' @format tidygraph network object 56 | #' @source Dummy data 57 | #' \url{https://github.com/marioangst/motifr/blob/master/notes/tidygraph_dummy_net.R} 58 | #' @examples 59 | #' plot_mnet(tidygraph_dummy_net) 60 | "tidygraph_dummy_net" 61 | 62 | #' Large two-level directed network dummy example 63 | #' 64 | #' 65 | #' @format network network object 66 | #' @source Dummy data 67 | #' \url{https://gitlab.com/t.seppelt/sesmotifanalyser/-/tree/master/test/data} 68 | #' @examples 69 | #' plot_mnet(large_directed_dummy_net) 70 | "large_directed_dummy_net" 71 | -------------------------------------------------------------------------------- /R/gaps_critical_edges.R: -------------------------------------------------------------------------------- 1 | #' List gaps 2 | #' 3 | #' List gaps ordered by contribution to a motif. This is a list of ties together 4 | #' with the number of motifs of a given class the dyad would generate by being 5 | #' added to the network. 6 | #' 7 | #' The level parameter determines on which level of the network gaps are 8 | #' analysed. Per default, when ``level = -1``, the first level in the motif 9 | #' which provides exactly two nodes is selected. Use this parameter to specify a 10 | #' level manually. The procedure for determining the level is the same as for 11 | #' the Actor's Choice Model, cf. vignette. 12 | #' 13 | #' Note that this only works for undirected graphs. Regardless of whether the 14 | #' input graph is directed it is treated as undirected graph. 15 | #' 16 | #' @param net network object 17 | #' @param motif motif identifier 18 | #' @param lvl_attr character vector specifying the attribute name where level 19 | #' information is stored in \code{net}. 20 | #' @param level level of the dyads which shall be considered, or -1 if the level 21 | #' shall be determined automatically. 22 | #' 23 | #' @return data frame with three columns, listing edges and their contribution 24 | #' to motifs described by the motif identifier in descending order 25 | #' @export 26 | #' 27 | #' @examples 28 | #' \dontrun{ 29 | #' head(identify_gaps(ml_net, motif = "1,2[II.C]")) 30 | #' } 31 | identify_gaps <- function(net, 32 | motif, 33 | lvl_attr = c("sesType"), 34 | level = -1) { 35 | if (pkg.env$sma$isClosedMotifR(motif, level) == FALSE) { 36 | stop("The specified motif is not closed. Look for critical_dyads instead.") 37 | } 38 | return(edge_contribution(net = net, motif = motif, lvl_attr = lvl_attr, level = level)) 39 | } 40 | 41 | #' List critical dyads 42 | #' 43 | #' Critical dyads are edges on a specified level which break motifs by being 44 | #' removed from the network. 45 | #' 46 | #' The level parameter determines on which level of the network critical dyads 47 | #' are analysed. Per default, when \code{level = -1}, the first level in the motif 48 | #' which provides exactly two nodes is selected. Use this parameter to specify a 49 | #' level manually. The procedure for determining the level is the same as for 50 | #' the Actor's Choice Model, cf. vignette. 51 | #' 52 | #' Note that this only works for undirected graphs. Regardless of whether the 53 | #' input graph is directed it is treated as undirected graph. 54 | #' 55 | #' @param net network object 56 | #' @param motif motif identifier 57 | #' @param lvl_attr character vector specifying the attribute name where level 58 | #' information is stored in \code{net} 59 | #' @param level level of the dyads which shall be considered, or -1 if the level 60 | #' shall be determined automatically. 61 | #' 62 | #' @return data frame with three columns, listing edges and their contribution 63 | #' to motifs described by the motif identifier in descending order 64 | #' @export 65 | #' 66 | #' @examples 67 | #' \dontrun{ 68 | #' head(critical_dyads(ml_net, motif = "1,2[I.C]")) 69 | #' } 70 | critical_dyads <- function(net, 71 | motif, 72 | lvl_attr = c("sesType"), 73 | level = -1) { 74 | if (pkg.env$sma$isClosedMotifR(motif, level) == TRUE) { 75 | stop("The specified motif is not open. Look for identify_gaps instead.") 76 | } 77 | return(edge_contribution(net = net, motif = motif, lvl_attr = lvl_attr, level = level)) 78 | } 79 | 80 | #' List edge contribution 81 | #' 82 | #' List gaps ordered by contribution to a motif. This is a list of ties together 83 | #' with the number of motifs of a given class the dyad would generate by being 84 | #' flipped. This is a generalisation of \code{motifr::identify_gaps()} and 85 | #' \code{motifr::criticial_dyads()}. 86 | #' 87 | #' The level parameter determines on which level of the network edge 88 | #' contributions are analysed. Per default, when \code{level = -1}, the first 89 | #' level in the motif which provides exactly two nodes is selected. Use this 90 | #' parameter to specify a level manually. The procedure for determining the 91 | #' level is the same as for the Actor's Choice Model, cf. vignette. 92 | #' 93 | #' Note that this only works for undirected graphs. Regardless of whether the 94 | #' input graph is directed it is treated as undirected graph. 95 | #' 96 | #' @param net network object 97 | #' @param motif motif identifier 98 | #' @param lvl_attr character vector specifying the attribute name where level 99 | #' information is stored in \code{net}. 100 | #' @param level level of the dyads which shall be considered, or -1 if the level 101 | #' shall be determined automatically. 102 | #' 103 | #' @return data frame with three columns, listing edges and their contribution 104 | #' to motifs described by the motif identifier in descending order 105 | #' @export 106 | #' 107 | #' @examples 108 | #' \dontrun{ 109 | #' head(edge_contribution(ml_net, "1,2[I.C]")) 110 | #' } 111 | edge_contribution <- function(net, 112 | motif, 113 | lvl_attr = c("sesType"), 114 | level = -1) { 115 | if (motifr::is.directed(net)) { 116 | warning(paste( 117 | "Edge contribution does only make sense for undirected networks.", 118 | "The given network is automatically treated as an undirected network." 119 | )) 120 | } 121 | py_g <- motifr::to_py_graph(g = net, lvl_attr = lvl_attr, directed = FALSE) 122 | result <- pkg.env$sma$identifyGapsR(py_g, motif, level = level) 123 | df <- data.frame(result) 124 | return(df) 125 | } 126 | 127 | #' Plot gaps in network visualisation 128 | #' 129 | #' Note that this only works for undirected graphs. Regardless of whether the 130 | #' input graph is directed it is treated as undirected graph. 131 | #' 132 | #' @param net Statnet network object 133 | #' @param motif Motif to explore gaps in for 134 | #' @param lvl_attr Node attribute specifying level information 135 | #' @param level Focal level for gap analysis 136 | #' @param cutoff Cut-off point in contributions of an edge to the number of 137 | #' motifs above which to analyse gaps 138 | #' @param ... list of additional parameters to be passed to plotting function 139 | #' (see \code{motifr::plot_mnet}), e.g. \code{label = TRUE} 140 | #' @param subset_graph Whether to subset the graph to only show nodes involved 141 | #' in gaps. One of "none" (no subset, default), "partial" (only focal level is 142 | #' subset) or "focal" (only focal level shown) 143 | #' 144 | #' @return A plot of gaps, sized by weight in a multilevel network 145 | #' @export 146 | #' 147 | #' @examples 148 | #' \dontrun{ 149 | #' plot_gaps(ml_net, "1,2[II.C]", level = -1) 150 | #' plot_gaps(ml_net, "1,2[II.C]", 151 | #' level = -1, 152 | #' subset_graph = "focal", cutoff = 4, label = TRUE 153 | #' ) 154 | #' plot_gaps(ml_net, "1,2[II.C]", 155 | #' level = -1, 156 | #' subset_graph = "partial", cutoff = 4, label = TRUE 157 | #' ) 158 | #' } 159 | plot_gaps <- function(net, 160 | motif, 161 | lvl_attr = c("sesType"), 162 | level = -1, 163 | cutoff = 2, 164 | subset_graph = "none", 165 | ...) { 166 | gaps <- motifr::identify_gaps( 167 | net = net, 168 | motif = motif, 169 | lvl_attr = lvl_attr, 170 | level = level 171 | ) 172 | 173 | plot_gaps_or_critical_dyads(net, 174 | gaps, 175 | "#f2790070", 176 | "Gap", 177 | lvl_attr = lvl_attr, 178 | cutoff = cutoff, 179 | subset_graph = subset_graph, 180 | ... 181 | ) 182 | } 183 | 184 | #' Plot critical dyads in network visualisation 185 | #' 186 | #' Note that this only works for undirected graphs. Regardless of whether the 187 | #' input graph is directed it is treated as undirected graph. 188 | #' 189 | #' @param net Statnet network object 190 | #' @param motif Motif to explore gaps in for 191 | #' @param lvl_attr Node attribute specifying level information 192 | #' @param level Focal level for gap analysis 193 | #' @param cutoff Cut-off point in contributions of an edge to the number of 194 | #' motifs above which to analyse gaps 195 | #' @param ... list of additional parameters to be passed to plotting function 196 | #' (see \code{motifr::plot_mnet}), e.g. \code{label = TRUE} 197 | #' @param subset_graph Whether to subset the graph to only show nodes involved 198 | #' in gaps. One of "none" (no subset, default), "partial" (only focal level is 199 | #' subset) or "focal" (only focal level shown) 200 | #' 201 | #' @return A plot of gaps, sized by weight in a multilevel network 202 | #' @export 203 | #' 204 | #' @examples 205 | #' \dontrun{ 206 | #' plot_critical_dyads(ml_net, "1,2[I.C]", level = -1) 207 | #' plot_critical_dyads(ml_net, "1,2[I.C]", 208 | #' level = -1, 209 | #' subset_graph = "focal", cutoff = 4, label = TRUE 210 | #' ) 211 | #' plot_critical_dyads(ml_net, "1,2[I.C]", 212 | #' level = -1, 213 | #' subset_graph = "partial", cutoff = 4, label = TRUE 214 | #' ) 215 | #' } 216 | plot_critical_dyads <- function(net, 217 | motif, 218 | lvl_attr = c("sesType"), 219 | level = -1, 220 | cutoff = 2, 221 | subset_graph = "none", 222 | ...) { 223 | gaps <- motifr::critical_dyads( 224 | net = net, 225 | motif = motif, 226 | lvl_attr = lvl_attr, 227 | level = level 228 | ) 229 | 230 | plot_gaps_or_critical_dyads(net, 231 | gaps, 232 | "#00f24070", 233 | "Critical dyad", 234 | lvl_attr = lvl_attr, 235 | cutoff = cutoff, 236 | subset_graph = subset_graph, 237 | ... 238 | ) 239 | } 240 | 241 | #' Helper function for plotting gaps and critical edges 242 | #' 243 | #' Note that this only works for undirected graphs. Regardless of whether the 244 | #' input graph is directed it is treated as undirected graph. 245 | #' 246 | #' @seealso \code{plot_gaps}, \code{plot_critical_dyads}. 247 | #' 248 | #' @param net network object 249 | #' @param edge_contribution data frame providing edge contribution data 250 | #' @param colour colour code for the weighted edges 251 | #' @param title title of the plot 252 | #' @param lvl_attr nodal attribute specifying level information 253 | #' @param cutoff Cut-off point in contributions of an edge to the number of 254 | #' motifs above which to analyse gaps 255 | #' @param ... list of additional parameters to be passed to plotting function 256 | #' (see \code{motifr::plot_mnet}), e.g. \code{label = TRUE} 257 | #' @param subset_graph Whether to subset the graph to only show nodes involved 258 | #' in gaps. One of "none" (no subset, default), "partial" (only focal level is 259 | #' subset) or "focal" (only focal level shown) 260 | #' 261 | #' @return A plot of gaps or critical edges, sized by weight in a multilevel 262 | #' network 263 | #' 264 | plot_gaps_or_critical_dyads <- function(net, 265 | edge_contribution, 266 | colour, 267 | title, 268 | lvl_attr = c("sesType"), 269 | cutoff = 2, 270 | subset_graph = "none", 271 | ...) { 272 | edge_contribution <- edge_contribution[edge_contribution$contribution >= cutoff, ] 273 | max_contribution <- max(edge_contribution$contribution) 274 | 275 | if (cutoff > max_contribution) { 276 | stop("Cutoff is higher than maximal contribution found") 277 | } 278 | 279 | gap_nodes <- unique(c(edge_contribution$vertex0, edge_contribution$vertex1)) 280 | 281 | if (network::is.network(net)) { 282 | net <- intergraph::asIgraph(net) 283 | net <- 284 | igraph::set.vertex.attribute(net, 285 | name = "name", value = 286 | igraph::get.vertex.attribute(net, "vertex.names") 287 | ) 288 | } 289 | 290 | t_g <- tidygraph::as_tbl_graph(net) 291 | nodes <- tibble::as_tibble(tidygraph::activate(t_g, nodes)) 292 | edges <- tibble::as_tibble(tidygraph::activate(t_g, edges)) 293 | 294 | edges$to_name <- nodes$name[edges$to] 295 | edges$from_name <- nodes$name[edges$from] 296 | 297 | colnames(nodes)[colnames(nodes) == lvl_attr] <- "sesType" 298 | 299 | gap_level <- unique(nodes$sesType[nodes$name %in% gap_nodes]) 300 | 301 | if (subset_graph == "partial") { 302 | non_focal <- unique( 303 | edges$to_name[edges$from_name %in% gap_nodes], 304 | edges$from_name[edges$to_name %in% gap_nodes] 305 | ) 306 | non_focal <- non_focal[non_focal %in% nodes$name[!(nodes$sesType %in% gap_level)]] 307 | nodes <- nodes[nodes$name %in% gap_nodes | nodes$name %in% non_focal, ] 308 | #' @importFrom rlang .data 309 | t_g <- tidygraph::to_subgraph(t_g, .data$name %in% nodes$name, subset_by = "nodes")$subgraph 310 | } 311 | 312 | if (subset_graph == "focal") { 313 | nodes <- nodes[nodes$name %in% gap_nodes, ] 314 | #' @importFrom rlang .data 315 | t_g <- tidygraph::to_subgraph(t_g, .data$name %in% nodes$name, subset_by = "nodes")$subgraph 316 | } 317 | 318 | netviz <- plot_mnet(net = t_g, lvl_attr = lvl_attr, ...) 319 | 320 | # get coordinates for all lines 321 | coord_gaps <- data.frame( 322 | x1 = 323 | unlist(lapply(edge_contribution$vertex0, function(vertex) { 324 | netviz$data$x[netviz$data$name == vertex] 325 | })), 326 | y1 = 327 | unlist(lapply(edge_contribution$vertex0, function(vertex) { 328 | netviz$data$y[netviz$data$name == vertex] 329 | })), 330 | x2 = 331 | unlist(lapply(edge_contribution$vertex1, function(vertex) { 332 | netviz$data$x[netviz$data$name == vertex] 333 | })), 334 | y2 = 335 | unlist(lapply(edge_contribution$vertex1, function(vertex) { 336 | netviz$data$y[netviz$data$name == vertex] 337 | })), 338 | weight = edge_contribution$contribution 339 | ) 340 | 341 | netviz <- netviz + 342 | ggplot2::geom_segment( 343 | data = coord_gaps, 344 | ggplot2::aes_(x = ~x1, y = ~y1, xend = ~x2, yend = ~y2, size = ~weight), 345 | colour = colour, alpha = 0.7 346 | ) 347 | 348 | netviz$layers <- c(netviz$layers[[1]], netviz$layers[[3]], netviz$layers[[2]]) 349 | 350 | break_range <- cutoff:max_contribution 351 | 352 | netviz + 353 | ggplot2::scale_size_continuous(sprintf("%s weight", title), 354 | range = c(1, 4), 355 | breaks = break_range 356 | ) 357 | } 358 | -------------------------------------------------------------------------------- /R/load_python.R: -------------------------------------------------------------------------------- 1 | 2 | # https://github.com/rstudio/reticulate/issues/233 (delay loading till use) 3 | 4 | # https://community.rstudio.com/t/build-package-namespace-dynamically-during-onload/4101 5 | # https://mran.microsoft.com/snapshot/2017-08-06/web/packages/reticulate/vignettes/package.html 6 | 7 | # global reference in order to prevent Python modules in user data space 8 | pkg.env <- new.env(parent = emptyenv()) 9 | pkg.env$nx <- NULL 10 | pkg.env$sma <- NULL 11 | 12 | .onLoad <- function(libname, pkgname) { 13 | # https://rstudio.github.io/reticulate/articles/python_dependencies.html#onload-configuration 14 | reticulate::configure_environment(pkgname) 15 | # delay load Python modules (will only be loaded when accessed via $) 16 | pkg.env$nx <- reticulate::import("networkx", delay_load = TRUE) 17 | pkg.env$sma <- reticulate::import("sma", delay_load = TRUE) 18 | } 19 | 20 | #' Checks for updates for motifr's Python core, the sma package 21 | #' 22 | #' It might be necessary to restart your R session after updating the sma package. 23 | #' 24 | #' @param method parameter for \code{reticulate::py_install} 25 | #' @param conda parameter for \code{reticulate::py_install} 26 | #' @export 27 | update_motifr <- function(method = "auto", conda = "auto") { 28 | reticulate::py_install("sma", pip = TRUE, method = method, conda = conda) 29 | } 30 | -------------------------------------------------------------------------------- /R/motif_zoo.R: -------------------------------------------------------------------------------- 1 | #' Lists all supported signatures 2 | #' 3 | #' Returns a data frame with three columns: signature, a Boolean value 4 | #' indicating whether the motifs are directed, the number of levels which the 5 | #' motif spans across 6 | #' 7 | #' @return data frame with all supported signatures 8 | #' @examples 9 | #' \dontrun{ 10 | #' supported_signatures() 11 | #' } 12 | #' @seealso \code{supported_classes()} 13 | #' @export 14 | #' 15 | supported_signatures <- function() { 16 | result <- data.frame() 17 | iter <- pkg.env$sma$supportedSignatures() 18 | while (TRUE) { 19 | item <- reticulate::iter_next(iter) 20 | if (is.null(item)) { 21 | break 22 | } 23 | result <- rbind( 24 | result, 25 | data.frame( 26 | signature = c(paste(item[[1]], collapse = ",")), 27 | directed = c(item[[2]]), 28 | n_levels = c(length(item[[1]])) 29 | ) 30 | ) 31 | } 32 | return(result) 33 | } 34 | 35 | #' Lists all supported motif classes for a given signature 36 | #' 37 | #' Returns a list with all supported motif classes for the given signature. 38 | #' Raises an error if the given signature is not supported. 39 | #' 40 | #' @return list of supported motif classes 41 | #' @param signature head of a motif identifier string, i.e. string with 42 | #' comma-separated list specifying the signature of the motif 43 | #' @param directed whether the motifs are directed. 44 | #' @export 45 | #' @seealso \code{supported_signatures()} 46 | #' @examples 47 | #' \dontrun{ 48 | #' supported_classes("1,2", FALSE) 49 | #' supported_classes("1,1", TRUE) 50 | #' } 51 | supported_classes <- function(signature, directed) { 52 | ls <- strsplit(signature, ",") 53 | lsi <- as.list(lapply(ls, as.integer)[[1]]) 54 | motif_info <- pkg.env$sma$motifInfo(lsi, directed) 55 | return(motif_info$classes) 56 | } 57 | 58 | #' Explore the motif zoo interactively in a shiny app 59 | #' 60 | #' Without any arguments, this launches a shiny app, where all available motifs 61 | #' in motifr can be graphically displayed by selecting signature-class combinations 62 | #' from a dropdown list. 63 | #' 64 | #' If arguments net and lvl_attr are provided, you can load you own network into 65 | #' the shiny app to explore what a given motif classifier looks like for your 66 | #' network. Be aware that if your network does not contain a specific motif, an 67 | #' example of the motif can also not be shown, because motifr illustrates motifs 68 | #' by actually finding an example within a given network. 69 | #' 70 | #' @param net optional: you may supply your own network object here 71 | #' (must be loaded as an R object in the global environment) 72 | #' @param lvl_attr if you supply your own network object, indicate the name of 73 | #' the network attribute where level information is stored for each node 74 | #' 75 | #' @return Launches a shiny app where all available motifs can be displayed or, alternatively, 76 | #' all available motifs for a user-supplied network 77 | #' @export 78 | explore_motifs <- function(net = NULL, 79 | lvl_attr = c("sesType")) { 80 | file_path <- system.file("shiny_examples", "explore_zoo", "app.R", package = "motifr") 81 | if (!nzchar(file_path)) { 82 | stop("Could not find example directory. Try re-installing `motifr`.", call. = FALSE) 83 | } 84 | 85 | ui <- server <- NULL # avoid NOTE about undefined globals 86 | source(file_path, local = TRUE) 87 | server_env <- environment(server) 88 | 89 | # add user supplied net variables for shiny server to find 90 | server_env$net <- net 91 | server_env$lvl_attr <- lvl_attr 92 | 93 | app <- shiny::shinyApp(ui, server) 94 | shiny::runApp(app, display.mode = "normal") 95 | } 96 | -------------------------------------------------------------------------------- /R/visualize_mnet.R: -------------------------------------------------------------------------------- 1 | 2 | #' Visualize a multi-level network (using ggraph) 3 | #' 4 | #' Visualize a multi-level network, with the possibility of specifying separate 5 | #' layouts for each level. This is a somewhat hacky wrapper for arranging 6 | #' separate ggraph calls for each network level in a circle. 7 | #' 8 | #' For more extensive visualization options, it is recommended to explore the 9 | #' \link[graphlayouts]{layout_as_multilevel} function included in 10 | #' the package graphlayouts. 11 | #' 12 | #' @param net A tidygraph, igraph or statnet network object 13 | #' @param lvl_attr The name of the categorical node attribute specifying at 14 | #' which level a node is situated 15 | #' @param layouts A list of layouts (see \code{ggraph::layout_ggraph}) for every level 16 | #' e.g. for two levels \code{list("auto","circle")} 17 | #' @param label logical - should nodes be labelled? (defaults to false) 18 | #' @param directed whether the network object shall be interpreted as directed 19 | #' network. Per default, \code{motifr::is.directed} is used to determine that. 20 | #' @param nodesize The size of node displays, if displayed as points (if label = false) 21 | #' @param edgewidth The width of lines illustrating edges 22 | #' 23 | #' @return A ggraph object 24 | #' @export 25 | #' 26 | #' @examples 27 | #' plot_mnet(net = motifr::ml_net, lvl_attr = "sesType", layouts = list("kk", "circle")) 28 | plot_mnet <- function(net, 29 | lvl_attr = c("sesType"), 30 | layouts = rep("kk", n_levels), 31 | label = FALSE, 32 | directed = NULL, 33 | nodesize = 3, 34 | edgewidth = 0.5) { 35 | if (network::is.network(net)) { 36 | net <- intergraph::asIgraph(net) 37 | net <- 38 | igraph::set.vertex.attribute(net, 39 | name = "name", value = 40 | igraph::get.vertex.attribute(net, "vertex.names") 41 | ) 42 | } 43 | 44 | t_g <- tidygraph::as_tbl_graph(net) 45 | nodes <- tibble::as_tibble(tidygraph::activate(t_g, nodes)) 46 | edges <- tibble::as_tibble(tidygraph::activate(t_g, edges)) 47 | 48 | colnames(nodes)[colnames(nodes) == lvl_attr] <- "lvl" 49 | # ensure numeric and starting at 1 50 | nodes$lvl_n <- as.numeric(factor(nodes$lvl)) 51 | 52 | n_levels <- length(unique(nodes$lvl_n)) 53 | 54 | edges$to_level <- nodes$lvl_n[edges$to] 55 | 56 | edges$from_level <- nodes$lvl_n[edges$from] 57 | 58 | edges$between <- ifelse(edges$from_level != edges$to_level, "between", "within") 59 | 60 | edges$level_pairs <- apply(data.frame(t(apply(cbind(edges$from_level, edges$to_level), 1, sort))), 61 | 1, paste, 62 | collapse = "_" 63 | ) 64 | 65 | # set edge colors here already 66 | edges$edgecol <- "gray" 67 | within_cols <- RColorBrewer::brewer.pal(n_levels + 2, "Paired") #+2 to avoid R brewer warnings if n < 3 68 | edges$edgecol[edges$between == "within"] <- within_cols[edges$from_level[edges$between == "within"]] 69 | 70 | t_g <- tidygraph::tbl_graph(nodes = nodes, edges = edges) 71 | 72 | # separate subgraphs 73 | sub_g_list <- vector(mode = "list", length = length(unique(nodes$lvl_n))) 74 | 75 | for (level in 1:n_levels) { 76 | #' @importFrom rlang .data 77 | t_g_sub <- tidygraph::to_subgraph(t_g, .data$lvl_n == level, subset_by = "nodes")$subgraph 78 | # if graph has no edges (bug in igraph), create self_loop to have one (hacky) 79 | if (nrow(tibble::as_tibble(tidygraph::activate(t_g_sub, edges))) == 0) { 80 | disc_edges <- tibble::as_tibble(tidygraph::activate(t_g_sub, edges)) 81 | disc_nodes <- tibble::as_tibble(tidygraph::activate(t_g_sub, nodes)) 82 | disc_edges <- dplyr::bind_rows(disc_edges, tibble::tibble(to = 1, from = 1, between = NA)) 83 | t_g_sub <- tidygraph::tbl_graph(nodes = disc_nodes, edges = disc_edges) 84 | } 85 | sub_g_list[[level]] <- ggraph::ggraph(graph = t_g_sub, layout = layouts[[level]]) + 86 | ggraph::geom_edge_loop() 87 | } 88 | 89 | # compute x and y offsets 90 | # When arranging n points on a circle, the k-th point, k = 0, …, n-1, has coordinates 91 | # x = cos(2 * pi * k /n) and y = sin(2 * pi * k/n). This gives coordinates ranging from -1 to 1. 92 | coord_offset <- lapply(c(1:n_levels), function(level) { 93 | x <- cos(2 * pi * level / n_levels) 94 | y <- sin(2 * pi * level / n_levels) 95 | return(list(x = x, y = y)) 96 | }) 97 | 98 | for (level in 1:n_levels) { 99 | sub_g_list[[level]][["data"]][["x"]] <- scales::rescale(sub_g_list[[level]][["data"]][["x"]], 100 | to = c(0, 1) 101 | ) + 102 | coord_offset[[level]][["x"]] 103 | sub_g_list[[level]][["data"]][["y"]] <- scales::rescale(sub_g_list[[level]][["data"]][["y"]], 104 | to = c(0, 1) 105 | ) + 106 | coord_offset[[level]][["y"]] 107 | } 108 | 109 | p_comb <- ggraph::ggraph(t_g, layout = "kk") 110 | 111 | for (level in 1:n_levels) { 112 | p_comb[["data"]][["x"]][ 113 | p_comb[["data"]][["lvl_n"]] == level 114 | ] <- 115 | sub_g_list[[level]][["data"]][["x"]] 116 | 117 | p_comb[["data"]][["y"]][ 118 | p_comb[["data"]][["lvl_n"]] == level 119 | ] <- 120 | sub_g_list[[level]][["data"]][["y"]] 121 | } 122 | 123 | # render edges (first, because of overplotting nodes later) 124 | 125 | # handle directed networks 126 | if ((is.null(directed) && motifr::is.directed(net)) || 127 | (!is.null(directed) && directed == TRUE)) { 128 | p_comb <- p_comb + 129 | ggraph::geom_edge_link(ggplot2::aes_( 130 | colour = ~edgecol 131 | ), 132 | end_cap = ggraph::circle(3, "mm"), 133 | start_cap = ggraph::circle(3, "mm"), 134 | arrow = grid::arrow( 135 | angle = 30, 136 | length = ggplot2::unit(.3, "cm"), 137 | type = "closed" 138 | ), 139 | width = edgewidth 140 | ) 141 | # + 142 | # ggraph::scale_edge_color_grey(guide = FALSE) 143 | } 144 | else { 145 | p_comb <- p_comb + 146 | ggraph::geom_edge_link(ggplot2::aes_( 147 | colour = ~edgecol 148 | ), 149 | width = edgewidth 150 | ) 151 | # + 152 | # ggraph::scale_edge_color_grey(guide = FALSE) 153 | } 154 | 155 | # render nodes 156 | 157 | if (label == FALSE) { 158 | p_comb <- 159 | p_comb + 160 | ggraph::geom_node_point(ggplot2::aes_(color = ~ factor(lvl)), size = nodesize) + 161 | ggplot2::scale_color_brewer("Level", 162 | breaks = levels(factor(nodes$lvl)), 163 | palette = "Paired" 164 | ) 165 | } 166 | 167 | if (label == TRUE) { 168 | p_comb <- 169 | p_comb + ggraph::geom_node_label(ggplot2::aes_(label = ~name, fill = ~ factor(lvl)), 170 | alpha = 0.5 171 | ) + 172 | ggplot2::scale_fill_brewer("Level", 173 | breaks = levels(factor(nodes$lvl)), 174 | palette = "Paired" 175 | ) 176 | } 177 | 178 | p_comb <- p_comb + 179 | ggplot2::theme_void() + 180 | ggplot2::coord_cartesian(clip = "off") + 181 | ggplot2::theme(legend.position = "bottom") 182 | 183 | return(p_comb + ggplot2::theme(plot.margin = ggplot2::unit(c(1, 1, 1, 1), "cm"))) 184 | } 185 | -------------------------------------------------------------------------------- /README.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: 3 | output: 4 | github_document 5 | --- 6 | 7 | # motifr 8 | 9 | 10 | 11 | ```{r, include = FALSE} 12 | knitr::opts_chunk$set( 13 | collapse = TRUE, 14 | comment = "#>", 15 | fig.path = "man/figures/README-", 16 | out.width = "100%", 17 | dev = "png" 18 | ) 19 | ``` 20 | 21 | 22 | [![Build Status](https://travis-ci.org/marioangst/motifr.svg?branch=master)](https://travis-ci.org/marioangst/motifr) 23 | [![CRAN\_Release\_Badge](https://www.r-pkg.org/badges/version-ago/motifr)](https://CRAN.R-project.org/package=motifr) 24 | [![Total downloads badge](https://cranlogs.r-pkg.org/badges/grand-total/motifr?color=blue)](https://CRAN.R-project.org/package=motifr) 25 | 26 | 27 | This package provides tools to analyse multi-level networks in terms of _motifs_. 28 | 29 | Multi-level networks combine multiple networks in one representation, e.g. social-ecological networks, which connect a social network (e.g. interactions among fishermen) with an ecological network (e.g. interactions between fish species) and the ties in between (e.g. fishers who fish specific species). 30 | 31 | [Motifs](https://en.wikipedia.org/wiki/Network_motif) are small configurations of nodes and edges (subgraphs) within an overall network. 32 | 33 | Package features include: 34 | 35 | - Visualization: The package provides functions to visualize multi-level networks, based on [ggraph](https://CRAN.R-project.org/package=ggraph). 36 | 37 | - Motif counts: The package is in many parts a R wrapper for the excellent [SESMotifAnalyser](https://gitlab.com/t.seppelt/sesmotifanalyser) Python framework written by Tim Seppelt to count multi-level network motifs, compare them to a baseline and much more. Only parts of of SESMotifAnalyser are yet wrapped, so consult the python framework for additional functionality. 38 | 39 | - Contributions of edges to motifs: motifr further identifies and visualizes functional gaps and critical edges in multi-level networks based on contributions of existing or potential edges to given motifs (this is theoretically motivated by network theories of functional fit and misfit). 40 | 41 | 42 | ## Installation 43 | 44 | Due to the package’s tight integration with the Python framework SESMotifAnalyser, we recommend explicitly installing the associated sma module through reticulate. 45 | 46 | ```{r eval=FALSE} 47 | reticulate::py_install("sma", pip = TRUE) 48 | ``` 49 | 50 | You can then install motifr from CRAN: 51 | 52 | ```{r eval=FALSE} 53 | install.packages("motifr") 54 | ``` 55 | 56 | To install the development version from github, using devtools: 57 | 58 | ```{r eval=FALSE} 59 | devtools::install_github("marioangst/motifr") 60 | ``` 61 | 62 | Please report any issues that occur when using the package by creating an issue in the [issue tracker on github](https://github.com/marioangst/motifr/issues). 63 | 64 | If you use motifr, please cite it when publishing results. To check how, use: 65 | 66 | ```{r} 67 | citation("motifr") 68 | ``` 69 | 70 | 71 | ## Input 72 | 73 | motifr currently can handle unweighted directed and undirected networks. The package supports motifs distributed across a maximum of three levels currently, while the total number of levels in the network is theoretically unrestricted. 74 | 75 | Network data should be prepared as statnet network objects or igraph/ tidygraph graph objects with a numeric vertex attribute to specify a level for each node (named e.g. "lvl") for best results. 76 | 77 | ## Introduction and key functionality 78 | 79 | First, we load the package. 80 | 81 | ```{r} 82 | library(motifr) 83 | ``` 84 | 85 | ### Visualize a multi-level network 86 | 87 | The following network is an example network from an empirical analysis of wetlands management in Switzerland. It consists of two levels - one level specifies a network of relations between actors. A second level specifies a network of relations between different activities occurring in the wetland, based on causal interdependence among activities. Links between the levels specify which actors carry out which activities. 88 | 89 | It is possible to specify layouts for every network level separately. Below, one level is plotted based on a circle layout, the second one based on Kamada-Kawai. 90 | 91 | ```{r} 92 | plot_mnet( 93 | net = ml_net, 94 | lvl_attr = "sesType", 95 | layouts = list("kk", "circle"), 96 | directed = FALSE 97 | ) 98 | ``` 99 | 100 | motifr provides a reliable starting point for multi-level network visualization but is focused on motif analyis at its core. For advanced visualization of multi-level networks we recommend pairing [ggraph](https://CRAN.R-project.org/package=ggraph) and [graphlayouts](https://CRAN.R-project.org/package=graphlayouts). [This blog post](http://blog.schochastics.net/post/visualizing-multilevel-networks-with-graphlayouts/) provides an excellent introduction. 101 | 102 | ### Selecting motifs 103 | 104 | See the vignette on the motif zoo (``vignette("motif_zoo")``) for details on nomenclature for motifs (motif identifier strings). We highly recommend the use of two helper functions implemented in motifr to ensure that the software interprets the motif identifier provided as intended by the analyst. 105 | 106 | - use ``explore_motifs()`` to launch a shiny app where all motifs implemented for analysis with motifr can be displayed. You can pass your own network to ``explore_motifs()`` to see what motifs mean exactly for your data. For example, if your network is stored in a object named ``my_net`` with a level attribute ``lvl`` you can explore motifs within it interactively using ``explore_motifs(net = my_net, lvl_attr = "lvl")``. Be aware that if your network does not contain a specific motif, it cannot be displayed. 107 | 108 | - check a specific motif of interest using ``show_motif()``, which will either illustrate the motif in a dummy example network or, if you pass a network object to the function, in your network. ``show_motif()`` is specifically helpful to explore the impact of position matching (see ``vignette("motif_zoo")`` for more details). 109 | 110 | ### Count motifs 111 | 112 | Motifs can be counted using the versatile function ``count_motifs()``. It takes as parameters a statnet network or igraph graph object (use ``ml_net`` or ``dummy_net`` provided by this package as examples) and a list of motif identifiers (see below) specifying the motifs. 113 | 114 | Let's quickly check out two classic examples of three-node, two-level motifs (open and closed triangles) in the wetlands management network introduced above: 115 | 116 | ```{r out.width="300px"} 117 | show_motif(motif = "1,2[I.C]", net = ml_net, label = TRUE, directed = FALSE) # open ('1,2[I.C]') triangle 118 | show_motif(motif = "1,2[II.C]", net = ml_net, label = TRUE, directed = FALSE) # closed ('1,2[II.C]') triangle 119 | ``` 120 | 121 | 122 | Let's count the number of of these motifs in the entire network. 123 | 124 | ```{r} 125 | motifs <- list("1,2[I.C]", "1,2[II.C]") # open and closed triangle 126 | 127 | count_motifs(ml_net, motifs, directed = FALSE) 128 | ``` 129 | 130 | An exploratory approach can be taken by calling ``motif_summary()``. This function counts the occurrences of a couple of basic motifs. Furthermore it computes expectations and variances for the occurrence of these motifs in a modified Erdős-Rényi or so-called "Actor's choice" model. See the package ``vignette("random_baselines")`` for details. 131 | 132 | ```{r} 133 | motif_summary(ml_net) 134 | ``` 135 | 136 | ### Identify gaps and critical edges 137 | 138 | motifr makes it possible to identify gaps and critical edges in multi-level networks. This is motivated by theories of functional fit and misfit in networks, which posit that certain motifs are especially valuable for network outcomes (depending on the context). 139 | 140 | In relation to gaps, we can therefore try to identify potential edges that would create a large number of a given closed motif if they were to exist ("activated" or "flipped"). The number of such motifs created by an edge is their contribution. For example, we can get all edges that would create closed triangles (``"1,2[II.C]"``), including the information about how many such triangles they would create for the wetlands case study network: 141 | 142 | ```{r} 143 | gaps <- identify_gaps(ml_net, motif = "1,2[II.C]") 144 | head(gaps) 145 | ``` 146 | 147 | We can also plot these gaps in various ways in our network, including the option to only look at gaps above a certain weight (contribution) and different levels of focus to only show nodes involved in such gaps. Here again for the wetlands management network, only showing gaps with a weight above 5 and subsetting the level where we analyze gaps to only contain nodes involved in gaps. 148 | 149 | ```{r} 150 | plot_gaps(ml_net, 151 | "1,2[II.C]", 152 | level = -1, 153 | subset_graph = "partial", 154 | cutoff = 5, label = TRUE 155 | ) 156 | ``` 157 | 158 | ``identify_gaps`` has a sibling in ``critical_dyads``. Critical_dyads works in reverse to identifying gaps - it analyses for every existing edge how many instances of a given open motif would appear if the edge was to be removed. Below an example showing critical dyads in a plot of the full wetlands management example network. 159 | 160 | ```{r} 161 | plot_critical_dyads(ml_net, 162 | "1,2[I.C]", 163 | level = -1, 164 | subset_graph = "none", 165 | cutoff = 3, label = FALSE 166 | ) 167 | ``` 168 | 169 | ### Comparing motif occurrence to a baseline model 170 | 171 | Motifr can be used to simulate a baseline of networks to compare against. Motif counts in an empirical network can then be compared to the distribution of motif counts in the networks simulated from the baseline model. Four different ways of specifying models for baseline distributions are implemented in motifr, from a basic Erdős–Rényi model to the possiblity of supplying an exponential random graph model (ERGM) fit to draw simulations from. See the ``vignette("random_baselines")`` for details. 172 | 173 | As an illustration, we simulate networks from a "Actor's choice" baseline model here as a baseline to compare counts of open and closed triangles in the wetland management network against. This model keeps all ties fixed except ties on a specifc level. On this level (here set by setting level to 1, which is the actor level in this network), ties are allowed to vary based on a fixed probability (Erdős-Rényi) model. 174 | 175 | We find that open triangles occur much less frequently and closed triangles much more often than in the baseline model. 176 | 177 | This is an unsurprising result - everything else would have been concerning. It indicates that actors tend to close triangles across levels to other actors working on the same wetland management tasks much more often compared to what would be expected if they just chose random collaboration partners. We would expect such "fit to task" in a network of professional organizations working in wetland management. We highlight this interpretation because we want to stress that baseline models need to be judged very carefully for what they represent substantially. This is why motifr allows for a variety of baseline model configurations, (including fitted ergm objects). 178 | 179 | ```{r} 180 | motifs <- list("1,2[I.C]", "1,2[II.C]") # open ('1,2[I.C]') and closed ('1,2[II.C]') triangles 181 | 182 | compare_to_baseline(ml_net, 183 | model = "actors_choice", 184 | level = 1, 185 | motifs = motifs, 186 | n = 50, 187 | directed = FALSE) 188 | ``` 189 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # motifr 3 | 4 | 5 | 6 | 7 | 8 | [![Build 9 | Status](https://travis-ci.org/marioangst/motifr.svg?branch=master)](https://travis-ci.org/marioangst/motifr) 10 | [![CRAN\_Release\_Badge](https://www.r-pkg.org/badges/version-ago/motifr)](https://CRAN.R-project.org/package=motifr) 11 | [![Total downloads 12 | badge](https://cranlogs.r-pkg.org/badges/grand-total/motifr?color=blue)](https://CRAN.R-project.org/package=motifr) 13 | 14 | 15 | This package provides tools to analyse multi-level networks in terms of 16 | *motifs*. 17 | 18 | Multi-level networks combine multiple networks in one representation, 19 | e.g. social-ecological networks, which connect a social network 20 | (e.g. interactions among fishermen) with an ecological network 21 | (e.g. interactions between fish species) and the ties in between 22 | (e.g. fishers who fish specific species). 23 | 24 | [Motifs](https://en.wikipedia.org/wiki/Network_motif) are small 25 | configurations of nodes and edges (subgraphs) within an overall network. 26 | 27 | Package features include: 28 | 29 | - Visualization: The package provides functions to visualize 30 | multi-level networks, based on 31 | [ggraph](https://CRAN.R-project.org/package=ggraph). 32 | 33 | - Motif counts: The package is in many parts a R wrapper for the 34 | excellent 35 | [SESMotifAnalyser](https://gitlab.com/t.seppelt/sesmotifanalyser) 36 | Python framework written by Tim Seppelt to count multi-level network 37 | motifs, compare them to a baseline and much more. Only parts of of 38 | SESMotifAnalyser are yet wrapped, so consult the python framework 39 | for additional functionality. 40 | 41 | - Contributions of edges to motifs: motifr further identifies and 42 | visualizes functional gaps and critical edges in multi-level 43 | networks based on contributions of existing or potential edges to 44 | given motifs (this is theoretically motivated by network theories of 45 | functional fit and misfit). 46 | 47 | ## Installation 48 | 49 | Due to the package’s tight integration with the Python framework 50 | SESMotifAnalyser, we recommend explicitly installing the associated sma 51 | module through reticulate. 52 | 53 | ``` r 54 | reticulate::py_install("sma", pip = TRUE) 55 | ``` 56 | 57 | You can then install motifr from CRAN: 58 | 59 | ``` r 60 | install.packages("motifr") 61 | ``` 62 | 63 | To install the development version from github, using devtools: 64 | 65 | ``` r 66 | devtools::install_github("marioangst/motifr") 67 | ``` 68 | 69 | Please report any issues that occur when using the package by creating 70 | an issue in the [issue tracker on 71 | github](https://github.com/marioangst/motifr/issues). 72 | 73 | If you use motifr, please cite it when publishing results. To check how, 74 | use: 75 | 76 | ``` r 77 | citation("motifr") 78 | #> 79 | #> To cite package 'motifr' in publications use: 80 | #> 81 | #> Mario Angst and Tim Seppelt (2020). motifr: Motif Analysis in 82 | #> Multi-Level Networks. R package version 1.0.0. 83 | #> https://marioangst.github.io/motifr/ 84 | #> 85 | #> A BibTeX entry for LaTeX users is 86 | #> 87 | #> @Manual{, 88 | #> title = {motifr: Motif Analysis in Multi-Level Networks}, 89 | #> author = {Mario Angst and Tim Seppelt}, 90 | #> year = {2020}, 91 | #> note = {R package version 1.0.0}, 92 | #> url = {https://marioangst.github.io/motifr/}, 93 | #> } 94 | ``` 95 | 96 | ## Input 97 | 98 | motifr currently can handle unweighted directed and undirected networks. 99 | The package supports motifs distributed across a maximum of three levels 100 | currently, while the total number of levels in the network is 101 | theoretically unrestricted. 102 | 103 | Network data should be prepared as statnet network objects or igraph/ 104 | tidygraph graph objects with a numeric vertex attribute to specify a 105 | level for each node (named e.g. “lvl”) for best results. 106 | 107 | ## Introduction and key functionality 108 | 109 | First, we load the package. 110 | 111 | ``` r 112 | library(motifr) 113 | ``` 114 | 115 | ### Visualize a multi-level network 116 | 117 | The following network is an example network from an empirical analysis 118 | of wetlands management in Switzerland. It consists of two levels - one 119 | level specifies a network of relations between actors. A second level 120 | specifies a network of relations between different activities occurring 121 | in the wetland, based on causal interdependence among activities. Links 122 | between the levels specify which actors carry out which activities. 123 | 124 | It is possible to specify layouts for every network level separately. 125 | Below, one level is plotted based on a circle layout, the second one 126 | based on Kamada-Kawai. 127 | 128 | ``` r 129 | plot_mnet( 130 | net = ml_net, 131 | lvl_attr = "sesType", 132 | layouts = list("kk", "circle"), 133 | directed = FALSE 134 | ) 135 | ``` 136 | 137 | 138 | 139 | motifr provides a reliable starting point for multi-level network 140 | visualization but is focused on motif analyis at its core. For advanced 141 | visualization of multi-level networks we recommend pairing 142 | [ggraph](https://CRAN.R-project.org/package=ggraph) and 143 | [graphlayouts](https://CRAN.R-project.org/package=graphlayouts). [This 144 | blog 145 | post](http://blog.schochastics.net/post/visualizing-multilevel-networks-with-graphlayouts/) 146 | provides an excellent introduction. 147 | 148 | ### Selecting motifs 149 | 150 | See the vignette on the motif zoo (`vignette("motif_zoo")`) for details 151 | on nomenclature for motifs (motif identifier strings). We highly 152 | recommend the use of two helper functions implemented in motifr to 153 | ensure that the software interprets the motif identifier provided as 154 | intended by the analyst. 155 | 156 | - use `explore_motifs()` to launch a shiny app where all motifs 157 | implemented for analysis with motifr can be displayed. You can pass 158 | your own network to `explore_motifs()` to see what motifs mean 159 | exactly for your data. For example, if your network is stored in a 160 | object named `my_net` with a level attribute `lvl` you can explore 161 | motifs within it interactively using `explore_motifs(net = my_net, 162 | lvl_attr = "lvl")`. Be aware that if your network does not contain a 163 | specific motif, it cannot be displayed. 164 | 165 | - check a specific motif of interest using `show_motif()`, which will 166 | either illustrate the motif in a dummy example network or, if you 167 | pass a network object to the function, in your network. 168 | `show_motif()` is specifically helpful to explore the impact of 169 | position matching (see `vignette("motif_zoo")` for more details). 170 | 171 | ### Count motifs 172 | 173 | Motifs can be counted using the versatile function `count_motifs()`. It 174 | takes as parameters a statnet network or igraph graph object (use 175 | `ml_net` or `dummy_net` provided by this package as examples) and a list 176 | of motif identifiers (see below) specifying the motifs. 177 | 178 | Let’s quickly check out two classic examples of three-node, two-level 179 | motifs (open and closed triangles) in the wetlands management network 180 | introduced above: 181 | 182 | ``` r 183 | show_motif(motif = "1,2[I.C]", net = ml_net, label = TRUE, directed = FALSE) # open ('1,2[I.C]') triangle 184 | ``` 185 | 186 | 187 | 188 | ``` r 189 | show_motif(motif = "1,2[II.C]", net = ml_net, label = TRUE, directed = FALSE) # closed ('1,2[II.C]') triangle 190 | ``` 191 | 192 | 193 | 194 | Let’s count the number of of these motifs in the entire network. 195 | 196 | ``` r 197 | motifs <- list("1,2[I.C]", "1,2[II.C]") # open and closed triangle 198 | 199 | count_motifs(ml_net, motifs, directed = FALSE) 200 | #> motif count 201 | #> 1,2[I.C] 1,2[I.C] 543 202 | #> 1,2[II.C] 1,2[II.C] 167 203 | ``` 204 | 205 | An exploratory approach can be taken by calling `motif_summary()`. This 206 | function counts the occurrences of a couple of basic motifs. Furthermore 207 | it computes expectations and variances for the occurrence of these 208 | motifs in a modified Erdős-Rényi or so-called “Actor’s choice” model. 209 | See the package `vignette("random_baselines")` for details. 210 | 211 | ``` r 212 | motif_summary(ml_net) 213 | #> motif count expectation variance 214 | #> 1 1,2[I.C] 543 169.14423077 949.77428949 215 | #> 2 1,2[II.C] 167 16.96153846 25.69286965 216 | #> 3 2,1[I.C] 217 109.90569527 437.59757816 217 | #> 4 2,1[II.C] 7 10.23853550 13.91309018 218 | #> 5 2,2[III.C] 73 0.44811771 0.52381527 219 | #> 6 2,2[III.D] 1 0.04174551 0.04302532 220 | ``` 221 | 222 | ### Identify gaps and critical edges 223 | 224 | motifr makes it possible to identify gaps and critical edges in 225 | multi-level networks. This is motivated by theories of functional fit 226 | and misfit in networks, which posit that certain motifs are especially 227 | valuable for network outcomes (depending on the context). 228 | 229 | In relation to gaps, we can therefore try to identify potential edges 230 | that would create a large number of a given motif if they were to exist 231 | (“activated” or “flipped”). The number of such motifs created by an edge 232 | is their contribution. For example, we can get all edges that would 233 | create closed triangles (`"1,2[II.C]"`), including the information about 234 | how many such triangles they would create for the wetlands case study 235 | network: 236 | 237 | ``` r 238 | gaps <- identify_gaps(ml_net, motif = "1,2[II.C]") 239 | head(gaps) 240 | #> vertex0 vertex1 contribution 241 | #> 1 actor10 actor27 5 242 | #> 2 actor18 actor44 5 243 | #> 3 actor6 actor24 4 244 | #> 4 actor16 actor55 4 245 | #> 5 actor18 actor27 4 246 | #> 6 actor18 actor31 4 247 | ``` 248 | 249 | We can also plot these gaps in various ways in our network, including 250 | the option to only look at gaps above a certain weight (contribution) 251 | and different levels of focus to only show nodes involved in such gaps. 252 | Here again for the wetlands management network, only showing gaps with a 253 | weight above 5 and subsetting the level where we analyze gaps to only 254 | contain nodes involved in gaps. 255 | 256 | ``` r 257 | plot_gaps(ml_net, 258 | "1,2[II.C]", 259 | level = -1, 260 | subset_graph = "partial", 261 | cutoff = 5, label = TRUE 262 | ) 263 | ``` 264 | 265 | 266 | 267 | `identify_gaps` has a sibling in `critical_dyads`. Critical\_dyads works 268 | in reverse to identifying gaps - it analyses for every existing edge how 269 | many instances of a given motif would disappear if the edge was to be 270 | removed. Below an example showing critical dyads in a plot of the full 271 | wetlands management example network. 272 | 273 | ``` r 274 | plot_critical_dyads(ml_net, 275 | "1,2[I.C]", 276 | level = -1, 277 | subset_graph = "none", 278 | cutoff = 3, label = FALSE 279 | ) 280 | ``` 281 | 282 | 283 | 284 | ### Comparing motif occurrence to a baseline model 285 | 286 | Motifr can be used to simulate a baseline of networks to compare 287 | against. Motif counts in an empirical network can then be compared to 288 | the distribution of motif counts in the networks simulated from the 289 | baseline model. Four different ways of specifying models for baseline 290 | distributions are implemented in motifr, from a basic Erdős–Rényi model 291 | to the possiblity of supplying an exponential random graph model (ERGM) 292 | fit to draw simulations from. See the `vignette("random_baselines")` for 293 | details. 294 | 295 | As an illustration, we simulate networks from a “Actor’s choice” 296 | baseline model here as a baseline to compare counts of open and closed 297 | triangles in the wetland management network against. This model keeps 298 | all ties fixed except ties on a specifc level. On this level (here set 299 | by setting level to 1, which is the actor level in this network), ties 300 | are allowed to vary based on a fixed probability (Erdős-Rényi) model. 301 | 302 | We find that open triangles occur much less frequently and closed 303 | triangles much more often than in the baseline model. 304 | 305 | This is an unsurprising result - everything else would have been 306 | concerning. It indicates that actors tend to close triangles across 307 | levels to other actors working on the same wetland management tasks much 308 | more often compared to what would be expected if they just chose random 309 | collaboration partners. We would expect such “fit to task” in a network 310 | of professional organizations working in wetland management. We 311 | highlight this interpretation because we want to stress that baseline 312 | models need to be judged very carefully for what they represent 313 | substantially. This is why motifr allows for a variety of baseline model 314 | configurations, (including fitted ergm objects). 315 | 316 | ``` r 317 | motifs <- list("1,2[I.C]", "1,2[II.C]") # open ('1,2[I.C]') and closed ('1,2[II.C]') triangles 318 | 319 | compare_to_baseline(ml_net, 320 | model = "actors_choice", 321 | level = 1, 322 | motifs = motifs, 323 | n = 50, 324 | directed = FALSE) 325 | ``` 326 | 327 | 328 | -------------------------------------------------------------------------------- /_pkgdown.yml: -------------------------------------------------------------------------------- 1 | destination: docs 2 | url: https://marioangst.github.io/motifr 3 | -------------------------------------------------------------------------------- /cran-comments.md: -------------------------------------------------------------------------------- 1 | For a CRAN submission we recommend that you fix all NOTEs, WARNINGs and ERRORs. 2 | ## Test environments 3 | - R-hub windows-x86_64-devel (r-devel) 4 | - R-hub ubuntu-gcc-release (r-release) 5 | - R-hub fedora-clang-devel (r-devel) 6 | 7 | ## R CMD check results 8 | > On windows-x86_64-devel (r-devel), ubuntu-gcc-release (r-release), fedora-clang-devel (r-devel) 9 | checking CRAN incoming feasibility ... NOTE 10 | Maintainer: 'Mario Angst ' 11 | New submission 12 | 13 | Possibly mis-spelled words in DESCRIPTION: 14 | Seppelt (24:54) 15 | motifr (19:50) 16 | subgraphs (19:16) 17 | 18 | > On ubuntu-gcc-release (r-release) 19 | checking examples ... NOTE 20 | Examples with CPU or elapsed time > 5s 21 | user system elapsed 22 | dummy_net 4.039 0.174 5.882 23 | 24 | 0 errors ✓ | 0 warnings ✓ | 2 notes x 25 | -------------------------------------------------------------------------------- /data/directed_dummy_net.RData: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marioangst/motifr/5c7b65acfc2322d0d38cee91653c96155db739e7/data/directed_dummy_net.RData -------------------------------------------------------------------------------- /data/dummy_net.RData: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marioangst/motifr/5c7b65acfc2322d0d38cee91653c96155db739e7/data/dummy_net.RData -------------------------------------------------------------------------------- /data/large_directed_dummy_net.RData: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marioangst/motifr/5c7b65acfc2322d0d38cee91653c96155db739e7/data/large_directed_dummy_net.RData -------------------------------------------------------------------------------- /data/tidygraph_dummy_net.RData: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marioangst/motifr/5c7b65acfc2322d0d38cee91653c96155db739e7/data/tidygraph_dummy_net.RData -------------------------------------------------------------------------------- /data/wetlands_mlnet.RData: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marioangst/motifr/5c7b65acfc2322d0d38cee91653c96155db739e7/data/wetlands_mlnet.RData -------------------------------------------------------------------------------- /inst/figures/motifr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marioangst/motifr/5c7b65acfc2322d0d38cee91653c96155db739e7/inst/figures/motifr.png -------------------------------------------------------------------------------- /inst/shiny_examples/explore_zoo/app.R: -------------------------------------------------------------------------------- 1 | library(shiny) 2 | library(motifr) 3 | 4 | # Define UI for application that draws a histogram 5 | ui <- fluidPage( 6 | 7 | # Application title 8 | titlePanel("Explore the motif zoo"), 9 | 10 | # Sidebar with UI 11 | sidebarLayout( 12 | sidebarPanel( 13 | uiOutput("directionControl"), 14 | uiOutput("n_levelsControl"), 15 | uiOutput("signatureControl"), 16 | uiOutput("classControl"), 17 | radioButtons("show_label","Show labels?",c(TRUE,FALSE), selected = FALSE) 18 | 19 | ), 20 | 21 | # Show a plot of the generated distribution 22 | mainPanel( 23 | h2(textOutput("motif_text")), 24 | plotOutput("motif_plot") 25 | ) 26 | ) 27 | ) 28 | 29 | # Define server logic required to draw a histogram 30 | server <- function(input, output) { 31 | 32 | output$directionControl <- renderUI({ 33 | if(!is.null(net)){ 34 | if (network::is.network(net)) { 35 | detected_direction <- ifelse(network::is.directed(net), 36 | "Directed","Undirected") 37 | } 38 | if (igraph::is.igraph(net)) { 39 | detected_direction <- ifelse(igraph::is.directed(net), 40 | "Directed","Undirected") 41 | } 42 | text_prompt <- paste("Type of network. 43 | Hint: The currently supplied network seems to be ", 44 | detected_direction) 45 | } 46 | else{ 47 | text_prompt <- "Type of network" 48 | } 49 | shiny::radioButtons(inputId = "directed",label = text_prompt, 50 | choices = c("Directed","Undirected"), selected = "Undirected", ) 51 | }) 52 | 53 | direction <- reactive({ 54 | if(input$directed == "Directed"){ 55 | return(TRUE) 56 | } 57 | else{ 58 | return(FALSE) 59 | } 60 | }) 61 | 62 | n_levels_set <- reactive({ 63 | input$n_levels 64 | }) 65 | 66 | output$n_levelsControl <- renderUI({ 67 | if(!is.null(net)){ 68 | if (network::is.network(net)) { 69 | detected_levels <- length( 70 | unique(network::get.vertex.attribute(net, attrname = lvl_attr))) 71 | } 72 | if (igraph::is.igraph(net)) { 73 | detected_levels <- length( 74 | unique(igraph::get.vertex.attribute(net, name = lvl_attr))) 75 | } 76 | text_prompt <- paste("Number of levels in network. 77 | Hint: The currently supplied network seems to have ", 78 | detected_levels," levels, based on the attribute ",lvl_attr) 79 | } 80 | else{ 81 | text_prompt <- "Number of levels in network" 82 | } 83 | 84 | shiny::numericInput("n_levels", text_prompt, 85 | value = 2, 86 | min = 1, 87 | max = max(supported_signatures()$n_levels)) 88 | }) 89 | 90 | output$classControl <- renderUI({ 91 | available_classes <- supported_classes(input$signature, 92 | directed = direction()) 93 | selectInput("class", label = "Motif class", 94 | choices = available_classes, 95 | multiple = FALSE) 96 | }) 97 | 98 | output$signatureControl <- renderUI({ 99 | available_signatures <- supported_signatures()[supported_signatures()$directed == direction() & 100 | supported_signatures()$n_levels <= n_levels_set(), 101 | "signature"] 102 | shiny::selectInput(inputId = "signature",label = "Signature", 103 | choices = available_signatures, 104 | multiple = FALSE) 105 | }) 106 | 107 | output$motif_plot <- renderPlot({ 108 | show_motif(paste(input$signature,"[",input$class,"]",sep = ""), 109 | nodesize = 7, 110 | net = net, 111 | lvl_attr = lvl_attr, 112 | label = input$show_label, 113 | edgewidth = 1, 114 | directed = direction()) 115 | }) 116 | 117 | output$motif_text <- renderText({ 118 | paste(input$signature,"[",input$class,"]",sep = "") 119 | }) 120 | } 121 | 122 | # # Run the application (do not because called from external) 123 | # shinyApp(ui = ui, server = server) 124 | -------------------------------------------------------------------------------- /man/compare_to_baseline.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/count_motifs.R 3 | \name{compare_to_baseline} 4 | \alias{compare_to_baseline} 5 | \title{Compare motif occurence in empirical network to occurence in a baseline model} 6 | \usage{ 7 | compare_to_baseline( 8 | net, 9 | motifs, 10 | n = 10, 11 | lvl_attr = "sesType", 12 | assume_sparse = TRUE, 13 | model = "erdos_renyi", 14 | level = -1, 15 | ergm_model = NULL, 16 | directed = NULL 17 | ) 18 | } 19 | \arguments{ 20 | \item{net}{network object} 21 | 22 | \item{motifs}{list of motif identifier strings} 23 | 24 | \item{n}{number of random graphs used in baseline model} 25 | 26 | \item{lvl_attr}{character vector specifying the attribute name where level 27 | information is stored in \code{net}.} 28 | 29 | \item{assume_sparse}{whether the random graphs shall be assumed to be sparse. 30 | used to find ideal counting function} 31 | 32 | \item{model}{baseline model to be used. Options are 'erdos_renyi', 'actors_choice', 33 | 'ergm', 'partial_ergm' and fixed_densities'. 34 | See \code{vignette("random_baselines")} for more details. 35 | Defaults to 'erdos_renyi'.} 36 | 37 | \item{level}{lvl_attr of the variable level for the Actor's Choice model} 38 | 39 | \item{ergm_model}{ergm model as for example fitted by calling 40 | \code{ergm::ergm()} on the empirically observed network. 41 | Needs to be supplied when model is set to ergm.} 42 | 43 | \item{directed}{whether the graph shall be treated as a directed graph. Per 44 | default (\code{NULL}), this is determined automatically using the structure 45 | of the provided network object} 46 | } 47 | \value{ 48 | data frame with one row for each motif identifier string and one row 49 | for every computed random graph 50 | } 51 | \description{ 52 | This function plots a comparison of the motif counts in a given network with the motif 53 | counts in a baseline model. 54 | } 55 | \details{ 56 | Note that when using the Actor's Choice model this function does not choose 57 | the variable level automatically. Use the \code{level} parameter to provide a 58 | valid level. 59 | 60 | When using ERGM the parameter \code{net} is not used. Networks to create the 61 | baseline from are sampled in R using the \code{ergm_model} parameter. 62 | } 63 | \examples{ 64 | \dontrun{ 65 | compare_to_baseline(ml_net, list("1,2[I.C]", "1,2[II.C]"), directed = FALSE) 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /man/count_motifs.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/count_motifs.R 3 | \name{count_motifs} 4 | \alias{count_motifs} 5 | \title{Count multi-level motifs} 6 | \usage{ 7 | count_motifs( 8 | net, 9 | motifs, 10 | lvl_attr = c("sesType"), 11 | assume_sparse = TRUE, 12 | omit_total_result = TRUE, 13 | directed = NULL 14 | ) 15 | } 16 | \arguments{ 17 | \item{net}{A network object with a node attribute specifying the 18 | level of each node} 19 | 20 | \item{motifs}{a list of motif identifiers which shall be counted, e.g. 21 | \code{list("1,2[I.C]")}} 22 | 23 | \item{lvl_attr}{character vector specifying the vertex attribute name where 24 | level information is stored in \code{net}} 25 | 26 | \item{assume_sparse}{whether the network shall be assumed to be sparse (for 27 | optimization), default TRUE} 28 | 29 | \item{omit_total_result}{whether total results shall be omitted, default 30 | FALSE} 31 | 32 | \item{directed}{whether the graph shall be treated as a directed graph. Per 33 | default (\code{NULL}), this is determined automatically using the structure 34 | of the provided network object} 35 | } 36 | \value{ 37 | data frame with a column containing motif identifier strings and one 38 | column containing motif counts 39 | } 40 | \description{ 41 | Count multi-level motifs 42 | } 43 | \examples{ 44 | \dontrun{ 45 | count_motifs(ml_net, 46 | lvl_attr = c("sesType"), 47 | motifs = list("1,2[I.C]", "1,2[II.C]", "2,1[I.C]", "2,1[II.C]"), 48 | directed = FALSE 49 | ) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /man/critical_dyads.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/gaps_critical_edges.R 3 | \name{critical_dyads} 4 | \alias{critical_dyads} 5 | \title{List critical dyads} 6 | \usage{ 7 | critical_dyads(net, motif, lvl_attr = c("sesType"), level = -1) 8 | } 9 | \arguments{ 10 | \item{net}{network object} 11 | 12 | \item{motif}{motif identifier} 13 | 14 | \item{lvl_attr}{character vector specifying the attribute name where level 15 | information is stored in \code{net}} 16 | 17 | \item{level}{level of the dyads which shall be considered, or -1 if the level 18 | shall be determined automatically.} 19 | } 20 | \value{ 21 | data frame with three columns, listing edges and their contribution 22 | to motifs described by the motif identifier in descending order 23 | } 24 | \description{ 25 | Critical dyads are edges on a specified level which break motifs by being 26 | removed from the network. 27 | } 28 | \details{ 29 | The level parameter determines on which level of the network critical dyads 30 | are analysed. Per default, when \code{level = -1}, the first level in the motif 31 | which provides exactly two nodes is selected. Use this parameter to specify a 32 | level manually. The procedure for determining the level is the same as for 33 | the Actor's Choice Model, cf. vignette. 34 | 35 | Note that this only works for undirected graphs. Regardless of whether the 36 | input graph is directed it is treated as undirected graph. 37 | } 38 | \examples{ 39 | \dontrun{ 40 | head(critical_dyads(ml_net, motif = "1,2[I.C]")) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /man/directed_dummy_net.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/data.R 3 | \docType{data} 4 | \name{directed_dummy_net} 5 | \alias{directed_dummy_net} 6 | \title{Two-level directed network dummy example} 7 | \format{ 8 | igraph network object 9 | } 10 | \source{ 11 | Dummy data 12 | \url{https://gitlab.com/t.seppelt/sesmotifanalyser/-/tree/master/test/data}, 13 | \url{https://github.com/marioangst/motifr/blob/master/notes/directed_dummy_net.R} 14 | } 15 | \usage{ 16 | directed_dummy_net 17 | } 18 | \description{ 19 | Simple igraph network object based on dummy data 20 | } 21 | \examples{ 22 | plot_mnet(directed_dummy_net) 23 | } 24 | \keyword{datasets} 25 | -------------------------------------------------------------------------------- /man/dummy_net.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/data.R 3 | \docType{data} 4 | \name{dummy_net} 5 | \alias{dummy_net} 6 | \title{Three-level network dummy example} 7 | \format{ 8 | Statnet network object with 60 nodes and 1035 edges on three levels. 9 | The network contains two variables to describe nodes/ vertices. 10 | \describe{ 11 | \item{vertex.names}{node labes} 12 | \item{sesType}{Categorical variable specifying network levels for every node (levels are 0,1 and 2)} 13 | ... 14 | } 15 | } 16 | \source{ 17 | Dummy data 18 | \url{https://gitlab.com/t.seppelt/sesmotifanalyser/-/tree/master/test/data} 19 | } 20 | \usage{ 21 | dummy_net 22 | } 23 | \description{ 24 | A simple statnet network object based on dummy data. 25 | } 26 | \examples{ 27 | plot_mnet(dummy_net) 28 | } 29 | \keyword{datasets} 30 | -------------------------------------------------------------------------------- /man/edge_contribution.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/gaps_critical_edges.R 3 | \name{edge_contribution} 4 | \alias{edge_contribution} 5 | \title{List edge contribution} 6 | \usage{ 7 | edge_contribution(net, motif, lvl_attr = c("sesType"), level = -1) 8 | } 9 | \arguments{ 10 | \item{net}{network object} 11 | 12 | \item{motif}{motif identifier} 13 | 14 | \item{lvl_attr}{character vector specifying the attribute name where level 15 | information is stored in \code{net}.} 16 | 17 | \item{level}{level of the dyads which shall be considered, or -1 if the level 18 | shall be determined automatically.} 19 | } 20 | \value{ 21 | data frame with three columns, listing edges and their contribution 22 | to motifs described by the motif identifier in descending order 23 | } 24 | \description{ 25 | List gaps ordered by contribution to a motif. This is a list of ties together 26 | with the number of motifs of a given class the dyad would generate by being 27 | flipped. This is a generalisation of \code{motifr::identify_gaps()} and 28 | \code{motifr::criticial_dyads()}. 29 | } 30 | \details{ 31 | The level parameter determines on which level of the network edge 32 | contributions are analysed. Per default, when \code{level = -1}, the first 33 | level in the motif which provides exactly two nodes is selected. Use this 34 | parameter to specify a level manually. The procedure for determining the 35 | level is the same as for the Actor's Choice Model, cf. vignette. 36 | 37 | Note that this only works for undirected graphs. Regardless of whether the 38 | input graph is directed it is treated as undirected graph. 39 | } 40 | \examples{ 41 | \dontrun{ 42 | head(edge_contribution(ml_net, "1,2[I.C]")) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /man/exemplify_motif.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/count_motifs.R 3 | \name{exemplify_motif} 4 | \alias{exemplify_motif} 5 | \title{Returns an example for a motif found in a given network} 6 | \usage{ 7 | exemplify_motif(net, motif, lvl_attr = "sesType", directed = NULL) 8 | } 9 | \arguments{ 10 | \item{net}{network object} 11 | 12 | \item{motif}{motif identifier string for the motif} 13 | 14 | \item{lvl_attr}{character vector specifying the attribute name where level 15 | information is stored in \code{net}.} 16 | 17 | \item{directed}{whether the graph shall be treated as a directed graph. Per 18 | default (\code{NULL}), this is determined automatically using the structure 19 | of the provided network object} 20 | } 21 | \value{ 22 | vector of nodes in the motif 23 | } 24 | \description{ 25 | Returns an example for a motif found in a given network 26 | } 27 | \examples{ 28 | \dontrun{ 29 | exemplify_motif(ml_net, motif = "1,2[I.C]", directed = FALSE) 30 | } 31 | } 32 | \seealso{ 33 | \code{motifr::show_motif} 34 | } 35 | -------------------------------------------------------------------------------- /man/explore_motifs.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/motif_zoo.R 3 | \name{explore_motifs} 4 | \alias{explore_motifs} 5 | \title{Explore the motif zoo interactively in a shiny app} 6 | \usage{ 7 | explore_motifs(net = NULL, lvl_attr = c("sesType")) 8 | } 9 | \arguments{ 10 | \item{net}{optional: you may supply your own network object here 11 | (must be loaded as an R object in the global environment)} 12 | 13 | \item{lvl_attr}{if you supply your own network object, indicate the name of 14 | the network attribute where level information is stored for each node} 15 | } 16 | \value{ 17 | Launches a shiny app where all available motifs can be displayed or, alternatively, 18 | all available motifs for a user-supplied network 19 | } 20 | \description{ 21 | Without any arguments, this launches a shiny app, where all available motifs 22 | in motifr can be graphically displayed by selecting signature-class combinations 23 | from a dropdown list. 24 | } 25 | \details{ 26 | If arguments net and lvl_attr are provided, you can load you own network into 27 | the shiny app to explore what a given motif classifier looks like for your 28 | network. Be aware that if your network does not contain a specific motif, an 29 | example of the motif can also not be shown, because motifr illustrates motifs 30 | by actually finding an example within a given network. 31 | } 32 | -------------------------------------------------------------------------------- /man/figures/README-unnamed-chunk-11-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marioangst/motifr/5c7b65acfc2322d0d38cee91653c96155db739e7/man/figures/README-unnamed-chunk-11-1.png -------------------------------------------------------------------------------- /man/figures/README-unnamed-chunk-12-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marioangst/motifr/5c7b65acfc2322d0d38cee91653c96155db739e7/man/figures/README-unnamed-chunk-12-1.png -------------------------------------------------------------------------------- /man/figures/README-unnamed-chunk-13-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marioangst/motifr/5c7b65acfc2322d0d38cee91653c96155db739e7/man/figures/README-unnamed-chunk-13-1.png -------------------------------------------------------------------------------- /man/figures/README-unnamed-chunk-14-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marioangst/motifr/5c7b65acfc2322d0d38cee91653c96155db739e7/man/figures/README-unnamed-chunk-14-1.png -------------------------------------------------------------------------------- /man/figures/README-unnamed-chunk-5-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marioangst/motifr/5c7b65acfc2322d0d38cee91653c96155db739e7/man/figures/README-unnamed-chunk-5-1.png -------------------------------------------------------------------------------- /man/figures/README-unnamed-chunk-6-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marioangst/motifr/5c7b65acfc2322d0d38cee91653c96155db739e7/man/figures/README-unnamed-chunk-6-1.png -------------------------------------------------------------------------------- /man/figures/README-unnamed-chunk-7-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marioangst/motifr/5c7b65acfc2322d0d38cee91653c96155db739e7/man/figures/README-unnamed-chunk-7-1.png -------------------------------------------------------------------------------- /man/figures/README-unnamed-chunk-7-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marioangst/motifr/5c7b65acfc2322d0d38cee91653c96155db739e7/man/figures/README-unnamed-chunk-7-2.png -------------------------------------------------------------------------------- /man/figures/README-unnamed-chunk-8-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marioangst/motifr/5c7b65acfc2322d0d38cee91653c96155db739e7/man/figures/README-unnamed-chunk-8-1.png -------------------------------------------------------------------------------- /man/figures/README-unnamed-chunk-8-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marioangst/motifr/5c7b65acfc2322d0d38cee91653c96155db739e7/man/figures/README-unnamed-chunk-8-2.png -------------------------------------------------------------------------------- /man/figures/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marioangst/motifr/5c7b65acfc2322d0d38cee91653c96155db739e7/man/figures/logo.png -------------------------------------------------------------------------------- /man/identify_gaps.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/gaps_critical_edges.R 3 | \name{identify_gaps} 4 | \alias{identify_gaps} 5 | \title{List gaps} 6 | \usage{ 7 | identify_gaps(net, motif, lvl_attr = c("sesType"), level = -1) 8 | } 9 | \arguments{ 10 | \item{net}{network object} 11 | 12 | \item{motif}{motif identifier} 13 | 14 | \item{lvl_attr}{character vector specifying the attribute name where level 15 | information is stored in \code{net}.} 16 | 17 | \item{level}{level of the dyads which shall be considered, or -1 if the level 18 | shall be determined automatically.} 19 | } 20 | \value{ 21 | data frame with three columns, listing edges and their contribution 22 | to motifs described by the motif identifier in descending order 23 | } 24 | \description{ 25 | List gaps ordered by contribution to a motif. This is a list of ties together 26 | with the number of motifs of a given class the dyad would generate by being 27 | added to the network. 28 | } 29 | \details{ 30 | The level parameter determines on which level of the network gaps are 31 | analysed. Per default, when ``level = -1``, the first level in the motif 32 | which provides exactly two nodes is selected. Use this parameter to specify a 33 | level manually. The procedure for determining the level is the same as for 34 | the Actor's Choice Model, cf. vignette. 35 | 36 | Note that this only works for undirected graphs. Regardless of whether the 37 | input graph is directed it is treated as undirected graph. 38 | } 39 | \examples{ 40 | \dontrun{ 41 | head(identify_gaps(ml_net, motif = "1,2[II.C]")) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /man/induced_level_subgraph.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/core.R 3 | \name{induced_level_subgraph} 4 | \alias{induced_level_subgraph} 5 | \title{Returns subgraph induced by one level of the network} 6 | \usage{ 7 | induced_level_subgraph(net, level, lvl_attr = "sesType") 8 | } 9 | \arguments{ 10 | \item{net}{the network} 11 | 12 | \item{level}{the (number of the) level} 13 | 14 | \item{lvl_attr}{name of the nodal attribute specifying the level} 15 | } 16 | \value{ 17 | induced subgraph as \code{network} object. 18 | } 19 | \description{ 20 | This function is intended to be used together with \code{simulate_baseline()} 21 | for partial ERGM models. Currently, only \code{network} objects are supported 22 | as input. 23 | } 24 | \examples{ 25 | 26 | subgraph_actors <- induced_level_subgraph(motifr::ml_net, 1) 27 | plot_mnet(subgraph_actors, label = TRUE) 28 | } 29 | -------------------------------------------------------------------------------- /man/is.directed.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/core.R 3 | \name{is.directed} 4 | \alias{is.directed} 5 | \title{Checks whether the given network is directed} 6 | \usage{ 7 | is.directed(net) 8 | } 9 | \arguments{ 10 | \item{net}{the network} 11 | } 12 | \value{ 13 | whether the given network is directed 14 | } 15 | \description{ 16 | Placeholder function for the corresponding functions of the various supported 17 | network formats. For example, this function calls 18 | \code{network::is.directed()} on \code{network} objects and 19 | \code{igraph::is.directed()} on \code{igraph} objects. 20 | } 21 | \examples{ 22 | is.directed(motifr::ml_net) 23 | } 24 | -------------------------------------------------------------------------------- /man/large_directed_dummy_net.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/data.R 3 | \docType{data} 4 | \name{large_directed_dummy_net} 5 | \alias{large_directed_dummy_net} 6 | \title{Large two-level directed network dummy example} 7 | \format{ 8 | network network object 9 | } 10 | \source{ 11 | Dummy data 12 | \url{https://gitlab.com/t.seppelt/sesmotifanalyser/-/tree/master/test/data} 13 | } 14 | \usage{ 15 | large_directed_dummy_net 16 | } 17 | \description{ 18 | Large two-level directed network dummy example 19 | } 20 | \examples{ 21 | plot_mnet(large_directed_dummy_net) 22 | } 23 | \keyword{datasets} 24 | -------------------------------------------------------------------------------- /man/list_motifs.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/count_motifs.R 3 | \name{list_motifs} 4 | \alias{list_motifs} 5 | \title{Lists motifs of a given class or all motifs with a given signature} 6 | \usage{ 7 | list_motifs(net, identifier, lvl_attr = "sesType", directed = NULL) 8 | } 9 | \arguments{ 10 | \item{net}{network object} 11 | 12 | \item{identifier}{motif identifier string (with or without class, see above)} 13 | 14 | \item{lvl_attr}{character vector specifying the attribute name where level 15 | information is stored in \code{net}.} 16 | 17 | \item{directed}{whether the graph shall be treated as a directed graph. Per 18 | default (\code{NULL}), this is determined automatically using the structure 19 | of the provided network object} 20 | } 21 | \value{ 22 | data frame with one row for each motif 23 | } 24 | \description{ 25 | Returns a dataframe with one row for each instance of the motif specified by 26 | the given motif identifier string. If the identifier string specifies a motif 27 | class, e.g. \code{1,2[I.A]}, then only motifs of the given class are returned. 28 | If the identifier string specifies a signature, e.g. \code{1,2}, then a full 29 | list of all motifs of this signature is returned. In the latter case, the 30 | dataframe contains an additional column stating the classes of the motifs. 31 | The naming scheme of the columns is as follows: Each column is called 32 | \code{levelA_nodeB} where \code{A} is the \code{lvl_attr} of the nodes in the column 33 | and \code{B} the index of the nodes among the nodes on the same level. This 34 | index stems from the internal order of the nodes and does not carry any 35 | specific meaning. 36 | } 37 | \examples{ 38 | \dontrun{ 39 | head(list_motifs(ml_net, "1,2[I.C]", directed = FALSE)) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /man/ml_net.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/data.R 3 | \docType{data} 4 | \name{ml_net} 5 | \alias{ml_net} 6 | \title{Two-level network example (wetlands management)} 7 | \format{ 8 | Statnet network object with 132 nodes and 566 edges on two levels. 9 | One network level contains actors, a second network level contains 10 | activities. Links between actors indicate collaboration among actors. Links 11 | between actors and activities indicate that an actor is active in a given 12 | activity. Links between activities indicate that the activities are 13 | causally interdependent. The network contains two variables to describe 14 | nodes/ vertices. \describe{ \item{vertex.names}{node labes} 15 | \item{sesType}{Binary variable specifying network levels for every node (1 16 | = node is a social node (actor) , 0 = node is a non-social node (an 17 | activity))} ... } 18 | } 19 | \source{ 20 | Surveys and expert interviews in a Swiss wetland. Data is anonymized 21 | and should only be used for exemplary purposes. 22 | } 23 | \usage{ 24 | ml_net 25 | } 26 | \description{ 27 | A statnet network object based on empirical data about actors and their 28 | activities in a case study of Swiss wetlands management 29 | } 30 | \examples{ 31 | plot_mnet(ml_net) 32 | } 33 | \keyword{datasets} 34 | -------------------------------------------------------------------------------- /man/motif_summary.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/count_motifs.R 3 | \name{motif_summary} 4 | \alias{motif_summary} 5 | \title{Summary for motif counts and Erdős-Rényi distribution} 6 | \usage{ 7 | motif_summary(net, lvl_attr = c("sesType")) 8 | } 9 | \arguments{ 10 | \item{net}{network object} 11 | 12 | \item{lvl_attr}{character vector specifying the attribute name where level 13 | information is stored in \code{net}.} 14 | } 15 | \value{ 16 | dataframe with motif counts, expectations and variances for set of 17 | selected motifs 18 | } 19 | \description{ 20 | Returns a data frame with counts and statistical properties (expectation, 21 | variances) of six selected motifs in the given network. Note that this 22 | function implicitly assumes that the network is undirected, cf. 23 | \code{motifr::to_py_graph}. 24 | } 25 | \examples{ 26 | \dontrun{ 27 | motif_summary(ml_net) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /man/motifs_distribution.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/count_motifs.R 3 | \name{motifs_distribution} 4 | \alias{motifs_distribution} 5 | \title{Compute statistical properties (expectation and variance) of the distribution 6 | of motifs in a baseline model} 7 | \usage{ 8 | motifs_distribution( 9 | net, 10 | motifs, 11 | lvl_attr = "sesType", 12 | model = "erdos_renyi", 13 | level = -1, 14 | omit_total_result = TRUE, 15 | directed = NULL 16 | ) 17 | } 18 | \arguments{ 19 | \item{net}{network object} 20 | 21 | \item{motifs}{list of motif identifiers describing the motifs whose 22 | distribution shall be analysed} 23 | 24 | \item{lvl_attr}{character vector specifying the attribute name where level 25 | information is stored in \code{net}.} 26 | 27 | \item{model}{baseline model to be used. options are "erdos_renyi" and "actors_choice". 28 | Defaults to "erdos_renyi".} 29 | 30 | \item{level}{Additional parameter to set the level to vary for the 31 | actors_choice model manually. All other levels are held fixed.} 32 | 33 | \item{omit_total_result}{whether total results shall be omitted} 34 | 35 | \item{directed}{whether the graph shall be treated as a directed graph. Per 36 | default (\code{NULL}), this is determined automatically using the structure 37 | of the provided network object} 38 | } 39 | \value{ 40 | data frame with one column giving names of motif identifers and two 41 | column giving expectation and variances per motif. For other motifs, 42 | expectations are computed but variances are returned as NaN. 43 | } 44 | \description{ 45 | This function supports the Erdős-Rényi Model (\code{erdos_renyi}) and the the 46 | Actor’s Choice Model (\code{actors_choice}). The model can be specified using 47 | the \code{model} parameter. The Erdős-Rényi Model can be used without 48 | providing further parameters. In case of the Actor’s Choice Model a level of 49 | the given network can be specified which is only level assumed to be 50 | variable. All other levels are assumed to be fixed. Per default, \code{level 51 | = -1}, the first level carrying two nodes in the signature of the motif is 52 | selected as variable level. Set the \code{level} parameter to the value of 53 | the \code{lvl_attr} of the nodes in the desired level to specify the level 54 | manually. 55 | } 56 | \examples{ 57 | \dontrun{ 58 | motifs_distribution(ml_net, motif = list("1,2[I.C]"), directed = FALSE) 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /man/plot_critical_dyads.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/gaps_critical_edges.R 3 | \name{plot_critical_dyads} 4 | \alias{plot_critical_dyads} 5 | \title{Plot critical dyads in network visualisation} 6 | \usage{ 7 | plot_critical_dyads( 8 | net, 9 | motif, 10 | lvl_attr = c("sesType"), 11 | level = -1, 12 | cutoff = 2, 13 | subset_graph = "none", 14 | ... 15 | ) 16 | } 17 | \arguments{ 18 | \item{net}{Statnet network object} 19 | 20 | \item{motif}{Motif to explore gaps in for} 21 | 22 | \item{lvl_attr}{Node attribute specifying level information} 23 | 24 | \item{level}{Focal level for gap analysis} 25 | 26 | \item{cutoff}{Cut-off point in contributions of an edge to the number of 27 | motifs above which to analyse gaps} 28 | 29 | \item{subset_graph}{Whether to subset the graph to only show nodes involved 30 | in gaps. One of "none" (no subset, default), "partial" (only focal level is 31 | subset) or "focal" (only focal level shown)} 32 | 33 | \item{...}{list of additional parameters to be passed to plotting function 34 | (see \code{motifr::plot_mnet}), e.g. \code{label = TRUE}} 35 | } 36 | \value{ 37 | A plot of gaps, sized by weight in a multilevel network 38 | } 39 | \description{ 40 | Note that this only works for undirected graphs. Regardless of whether the 41 | input graph is directed it is treated as undirected graph. 42 | } 43 | \examples{ 44 | \dontrun{ 45 | plot_critical_dyads(ml_net, "1,2[I.C]", level = -1) 46 | plot_critical_dyads(ml_net, "1,2[I.C]", 47 | level = -1, 48 | subset_graph = "focal", cutoff = 4, label = TRUE 49 | ) 50 | plot_critical_dyads(ml_net, "1,2[I.C]", 51 | level = -1, 52 | subset_graph = "partial", cutoff = 4, label = TRUE 53 | ) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /man/plot_gaps.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/gaps_critical_edges.R 3 | \name{plot_gaps} 4 | \alias{plot_gaps} 5 | \title{Plot gaps in network visualisation} 6 | \usage{ 7 | plot_gaps( 8 | net, 9 | motif, 10 | lvl_attr = c("sesType"), 11 | level = -1, 12 | cutoff = 2, 13 | subset_graph = "none", 14 | ... 15 | ) 16 | } 17 | \arguments{ 18 | \item{net}{Statnet network object} 19 | 20 | \item{motif}{Motif to explore gaps in for} 21 | 22 | \item{lvl_attr}{Node attribute specifying level information} 23 | 24 | \item{level}{Focal level for gap analysis} 25 | 26 | \item{cutoff}{Cut-off point in contributions of an edge to the number of 27 | motifs above which to analyse gaps} 28 | 29 | \item{subset_graph}{Whether to subset the graph to only show nodes involved 30 | in gaps. One of "none" (no subset, default), "partial" (only focal level is 31 | subset) or "focal" (only focal level shown)} 32 | 33 | \item{...}{list of additional parameters to be passed to plotting function 34 | (see \code{motifr::plot_mnet}), e.g. \code{label = TRUE}} 35 | } 36 | \value{ 37 | A plot of gaps, sized by weight in a multilevel network 38 | } 39 | \description{ 40 | Note that this only works for undirected graphs. Regardless of whether the 41 | input graph is directed it is treated as undirected graph. 42 | } 43 | \examples{ 44 | \dontrun{ 45 | plot_gaps(ml_net, "1,2[II.C]", level = -1) 46 | plot_gaps(ml_net, "1,2[II.C]", 47 | level = -1, 48 | subset_graph = "focal", cutoff = 4, label = TRUE 49 | ) 50 | plot_gaps(ml_net, "1,2[II.C]", 51 | level = -1, 52 | subset_graph = "partial", cutoff = 4, label = TRUE 53 | ) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /man/plot_gaps_or_critical_dyads.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/gaps_critical_edges.R 3 | \name{plot_gaps_or_critical_dyads} 4 | \alias{plot_gaps_or_critical_dyads} 5 | \title{Helper function for plotting gaps and critical edges} 6 | \usage{ 7 | plot_gaps_or_critical_dyads( 8 | net, 9 | edge_contribution, 10 | colour, 11 | title, 12 | lvl_attr = c("sesType"), 13 | cutoff = 2, 14 | subset_graph = "none", 15 | ... 16 | ) 17 | } 18 | \arguments{ 19 | \item{net}{network object} 20 | 21 | \item{edge_contribution}{data frame providing edge contribution data} 22 | 23 | \item{colour}{colour code for the weighted edges} 24 | 25 | \item{title}{title of the plot} 26 | 27 | \item{lvl_attr}{nodal attribute specifying level information} 28 | 29 | \item{cutoff}{Cut-off point in contributions of an edge to the number of 30 | motifs above which to analyse gaps} 31 | 32 | \item{subset_graph}{Whether to subset the graph to only show nodes involved 33 | in gaps. One of "none" (no subset, default), "partial" (only focal level is 34 | subset) or "focal" (only focal level shown)} 35 | 36 | \item{...}{list of additional parameters to be passed to plotting function 37 | (see \code{motifr::plot_mnet}), e.g. \code{label = TRUE}} 38 | } 39 | \value{ 40 | A plot of gaps or critical edges, sized by weight in a multilevel 41 | network 42 | } 43 | \description{ 44 | Note that this only works for undirected graphs. Regardless of whether the 45 | input graph is directed it is treated as undirected graph. 46 | } 47 | \seealso{ 48 | \code{plot_gaps}, \code{plot_critical_dyads}. 49 | } 50 | -------------------------------------------------------------------------------- /man/plot_mnet.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/visualize_mnet.R 3 | \name{plot_mnet} 4 | \alias{plot_mnet} 5 | \title{Visualize a multi-level network (using ggraph)} 6 | \usage{ 7 | plot_mnet( 8 | net, 9 | lvl_attr = c("sesType"), 10 | layouts = rep("kk", n_levels), 11 | label = FALSE, 12 | directed = NULL, 13 | nodesize = 3, 14 | edgewidth = 0.5 15 | ) 16 | } 17 | \arguments{ 18 | \item{net}{A tidygraph, igraph or statnet network object} 19 | 20 | \item{lvl_attr}{The name of the categorical node attribute specifying at 21 | which level a node is situated} 22 | 23 | \item{layouts}{A list of layouts (see \code{ggraph::layout_ggraph}) for every level 24 | e.g. for two levels \code{list("auto","circle")}} 25 | 26 | \item{label}{logical - should nodes be labelled? (defaults to false)} 27 | 28 | \item{directed}{whether the network object shall be interpreted as directed 29 | network. Per default, \code{motifr::is.directed} is used to determine that.} 30 | 31 | \item{nodesize}{The size of node displays, if displayed as points (if label = false)} 32 | 33 | \item{edgewidth}{The width of lines illustrating edges} 34 | } 35 | \value{ 36 | A ggraph object 37 | } 38 | \description{ 39 | Visualize a multi-level network, with the possibility of specifying separate 40 | layouts for each level. This is a somewhat hacky wrapper for arranging 41 | separate ggraph calls for each network level in a circle. 42 | } 43 | \details{ 44 | For more extensive visualization options, it is recommended to explore the 45 | \link[graphlayouts]{layout_as_multilevel} function included in 46 | the package graphlayouts. 47 | } 48 | \examples{ 49 | plot_mnet(net = motifr::ml_net, lvl_attr = "sesType", layouts = list("kk", "circle")) 50 | } 51 | -------------------------------------------------------------------------------- /man/show_motif.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/count_motifs.R 3 | \name{show_motif} 4 | \alias{show_motif} 5 | \title{Plots an example for a motif with given motif identifier string taken from 6 | the given graph.} 7 | \usage{ 8 | show_motif(motif, net = NULL, lvl_attr = c("sesType"), directed = NULL, ...) 9 | } 10 | \arguments{ 11 | \item{motif}{motif identifier string for the motif} 12 | 13 | \item{net}{network object} 14 | 15 | \item{lvl_attr}{character vector specifying the attribute name where level 16 | information is stored in \code{net}.} 17 | 18 | \item{directed}{whether the graph shall be treated as a directed graph. Per 19 | default (\code{NULL}), this is determined automatically using the structure 20 | of the provided network object} 21 | 22 | \item{...}{additional arguments to be passed to plotting function (e.g. 23 | \code{label = TRUE})} 24 | } 25 | \value{ 26 | plot 27 | } 28 | \description{ 29 | If no network is provided, a motif in a dummy network 30 | (\code{motifr::dummy_net} or \code{motifr::large_directed_dummy_net}) will be 31 | shown. 32 | } 33 | \examples{ 34 | \dontrun{ 35 | show_motif("1,2[I.C]", net = ml_net, directed = FALSE, label = TRUE) 36 | } 37 | } 38 | \seealso{ 39 | \code{motifr::exemplify_motif} 40 | } 41 | -------------------------------------------------------------------------------- /man/simulate_baseline.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/count_motifs.R 3 | \name{simulate_baseline} 4 | \alias{simulate_baseline} 5 | \title{Simulate a baseline baseline model} 6 | \usage{ 7 | simulate_baseline( 8 | net, 9 | motifs, 10 | n = 10, 11 | lvl_attr = "sesType", 12 | assume_sparse = TRUE, 13 | model = "erdos_renyi", 14 | level = -1, 15 | ergm_model = NULL, 16 | directed = NULL 17 | ) 18 | } 19 | \arguments{ 20 | \item{net}{network object} 21 | 22 | \item{motifs}{list of motif identifier strings} 23 | 24 | \item{n}{number of random graphs} 25 | 26 | \item{lvl_attr}{character string specifying the attribute name where level 27 | information is stored in \code{net}.} 28 | 29 | \item{assume_sparse}{whether the random graphs shall be assumed to be sparse. 30 | used to find ideal counting function. defaults to TRUE.} 31 | 32 | \item{model}{baseline model to be used. Options are 'erdos_renyi', 33 | 'fixed_densities', 'actors_choice', 'ergm' and 'partial_ergm'. See 34 | \code{vignette("random_baselines")} for more details. Defaults to 35 | 'erdos_renyi'.} 36 | 37 | \item{level}{lvl_attr of the variable level for the Actor's Choice model and 38 | for partial ERGM} 39 | 40 | \item{ergm_model}{ergm model as for example fitted by calling 41 | \code{ergm::ergm()}. Used when model is set to 'ergm' or 'partial_ergm' to 42 | sample random networks.} 43 | 44 | \item{directed}{whether the graph shall be treated as a directed graph. Per 45 | default (\code{NULL}), this is determined automatically using the structure 46 | of the provided network object} 47 | } 48 | \value{ 49 | data frame with one column for each motif identifier string and one 50 | row for every computed random graph 51 | } 52 | \description{ 53 | A baseline distribution of motif counts from a specified number of networks 54 | using a specified baseline model is computed. Options for the baseline model are 55 | - Erdős–Rényi 56 | - Actor's choice 57 | - Fixed density 58 | - Providing an ERGM fit for the whole network 59 | - Providing a partial ERGM fit (for only one level) 60 | } 61 | \details{ 62 | Note that when using the Actor's Choice model this function does not choose 63 | the variable level automatically. Use the \code{level} parameter to provide a 64 | valid level. 65 | 66 | When using (partial) ERGM the parameter \code{net} is not used. Random 67 | networks are sampled in R using the \code{ergm_model} parameter. 68 | } 69 | \examples{ 70 | \dontrun{ 71 | simulate_baseline(ml_net, list("1,2[I.C]"), n = 10, directed = FALSE) 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /man/supported_classes.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/motif_zoo.R 3 | \name{supported_classes} 4 | \alias{supported_classes} 5 | \title{Lists all supported motif classes for a given signature} 6 | \usage{ 7 | supported_classes(signature, directed) 8 | } 9 | \arguments{ 10 | \item{signature}{head of a motif identifier string, i.e. string with 11 | comma-separated list specifying the signature of the motif} 12 | 13 | \item{directed}{whether the motifs are directed.} 14 | } 15 | \value{ 16 | list of supported motif classes 17 | } 18 | \description{ 19 | Returns a list with all supported motif classes for the given signature. 20 | Raises an error if the given signature is not supported. 21 | } 22 | \examples{ 23 | \dontrun{ 24 | supported_classes("1,2", FALSE) 25 | supported_classes("1,1", TRUE) 26 | } 27 | } 28 | \seealso{ 29 | \code{supported_signatures()} 30 | } 31 | -------------------------------------------------------------------------------- /man/supported_signatures.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/motif_zoo.R 3 | \name{supported_signatures} 4 | \alias{supported_signatures} 5 | \title{Lists all supported signatures} 6 | \usage{ 7 | supported_signatures() 8 | } 9 | \value{ 10 | data frame with all supported signatures 11 | } 12 | \description{ 13 | Returns a data frame with three columns: signature, a Boolean value 14 | indicating whether the motifs are directed, the number of levels which the 15 | motif spans across 16 | } 17 | \examples{ 18 | \dontrun{ 19 | supported_signatures() 20 | } 21 | } 22 | \seealso{ 23 | \code{supported_classes()} 24 | } 25 | -------------------------------------------------------------------------------- /man/tidygraph_dummy_net.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/data.R 3 | \docType{data} 4 | \name{tidygraph_dummy_net} 5 | \alias{tidygraph_dummy_net} 6 | \title{Two-level tidygraph network example} 7 | \format{ 8 | tidygraph network object 9 | } 10 | \source{ 11 | Dummy data 12 | \url{https://github.com/marioangst/motifr/blob/master/notes/tidygraph_dummy_net.R} 13 | } 14 | \usage{ 15 | tidygraph_dummy_net 16 | } 17 | \description{ 18 | Simple \code{tidygraph} network object for testing 19 | } 20 | \examples{ 21 | plot_mnet(tidygraph_dummy_net) 22 | } 23 | \keyword{datasets} 24 | -------------------------------------------------------------------------------- /man/to_py_graph.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/core.R 3 | \name{to_py_graph} 4 | \alias{to_py_graph} 5 | \title{Translate multi-level statnet or igraph network object to Python networkx 6 | object} 7 | \usage{ 8 | to_py_graph(g, lvl_attr, relabel = TRUE, directed = NULL) 9 | } 10 | \arguments{ 11 | \item{g}{statnet or igraph network object} 12 | 13 | \item{lvl_attr}{character vector specifying the attribute name where level 14 | information is stored in \code{net}.} 15 | 16 | \item{relabel}{should nodes be relabelled with statnet \code{vertex.names} or 17 | igraph nodal attribute \code{name}} 18 | 19 | \item{directed}{whether the graph shall be treated as a directed graph. Per 20 | default (\code{NULL}), this is determined automatically using the structure 21 | of the provided network object} 22 | } 23 | \value{ 24 | Python networkx graph object 25 | } 26 | \description{ 27 | The function \code{motifr::is.directed} is used to determine whether the 28 | provided network is directed (if \code{directed = FALSE}). 29 | } 30 | \details{ 31 | The nodal attribute specified by \code{lvl_attr} indicates the levels of the 32 | nodes. Values are automatically converted to integers. Levels must be 33 | numbered starting with 0, 1, …. 34 | } 35 | \examples{ 36 | \dontrun{ 37 | to_py_graph(motifr::dummy_net, lvl_attr = "sesType") 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /man/update_motifr.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/load_python.R 3 | \name{update_motifr} 4 | \alias{update_motifr} 5 | \title{Checks for updates for motifr's Python core, the sma package} 6 | \usage{ 7 | update_motifr(method = "auto", conda = "auto") 8 | } 9 | \arguments{ 10 | \item{method}{parameter for \code{reticulate::py_install}} 11 | 12 | \item{conda}{parameter for \code{reticulate::py_install}} 13 | } 14 | \description{ 15 | It might be necessary to restart your R session after updating the sma package. 16 | } 17 | -------------------------------------------------------------------------------- /motifr.Rproj: -------------------------------------------------------------------------------- 1 | Version: 1.0 2 | 3 | RestoreWorkspace: No 4 | SaveWorkspace: No 5 | AlwaysSaveHistory: Default 6 | 7 | EnableCodeIndexing: Yes 8 | UseSpacesForTab: Yes 9 | NumSpacesForTab: 2 10 | Encoding: UTF-8 11 | 12 | RnwWeave: Sweave 13 | LaTeX: pdfLaTeX 14 | 15 | AutoAppendNewline: Yes 16 | StripTrailingWhitespace: Yes 17 | 18 | BuildType: Package 19 | PackageUseDevtools: Yes 20 | PackageInstallArgs: --no-multiarch --with-keep.source 21 | PackageBuildBinaryArgs: --no-multiarch 22 | PackageCheckArgs: --as-cran 23 | PackageRoxygenize: rd,collate,namespace 24 | -------------------------------------------------------------------------------- /notes/baseline_example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marioangst/motifr/5c7b65acfc2322d0d38cee91653c96155db739e7/notes/baseline_example.png -------------------------------------------------------------------------------- /notes/demo.R: -------------------------------------------------------------------------------- 1 | 2 | library(motifr) 3 | 4 | # closed and open triangles, one node on level 0, two nodes on level 1 5 | 6 | motifs <- list("1,2[I.C]", "1,2[II.C]") 7 | motif <- c("1,2[II.C]") # closed triangle 8 | 9 | count_motifs(ml_net, "1,2[II.C]") 10 | 11 | count_motifs(ml_net, motifs = motifs, lvl_attr = "sesType") 12 | motifs_distribution(ml_net, motifs = motifs) 13 | motif_summary(ml_net) 14 | exemplify_motif(ml_net, motif = motif) 15 | show_motif(ml_net, motif = motif) 16 | show_motif(ml_net, motif = motif, label = TRUE) 17 | 18 | plot_mnet(net = dummy_net, lvl_attr = "sesType") 19 | g <- intergraph::asIgraph(dummy_net) 20 | plot_mnet(g, lvl_attr = "sesType") 21 | 22 | simulate_baseline(ml_net, motifs = motifs, n = 20, model = "actors_choice") 23 | compare_to_baseline(ml_net, motifs = motifs, n = 10) 24 | compare_to_baseline(ml_net, motifs = motifs, n = 500) 25 | 26 | compare_to_baseline(dummy_net, motifs = list("1,2[I.C]", "1,2[II.C]")) 27 | 28 | identify_gaps(ml_net, motif = motif) 29 | critical_dyads(ml_net, motif = motif) 30 | 31 | plot_gaps(ml_net, motif = motif, label = TRUE, cutoff = 5, subset_graph = "partial") 32 | -------------------------------------------------------------------------------- /notes/directed_dummy_net.R: -------------------------------------------------------------------------------- 1 | # Script for creating directed_dummy_net 2 | 3 | # https://gitlab.com/t.seppelt/sesmotifanalyser/-/blob/master/test/data/digraph.py 4 | directed_dummy_net <- igraph::make_empty_graph(directed = TRUE) %>% 5 | igraph::add_vertices(3, sesType = 0) %>% 6 | igraph::add_vertices(3, sesType = 1) %>% 7 | igraph::add_edges(c(1,4, 1,2, 1,5, 1,3, 1,6, 4,5, 4,3, 4,6, 3,6, 5,2, 8 | 3,4, 3,2, 3,1, 5,1)) 9 | 10 | save(directed_dummy_net, file = "data/directed_dummy_net.RData") 11 | -------------------------------------------------------------------------------- /notes/gap_example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marioangst/motifr/5c7b65acfc2322d0d38cee91653c96155db739e7/notes/gap_example.png -------------------------------------------------------------------------------- /notes/make_sticker.R: -------------------------------------------------------------------------------- 1 | 2 | library(motifr) 3 | library(ggplot2) 4 | library(hexSticker) 5 | library(magick) 6 | library(patchwork) 7 | library(svglite) 8 | 9 | library(showtext) 10 | ## Loading Google fonts (http://www.google.com/fonts) 11 | font_add_google("Roboto") 12 | ## Automatically use showtext to render text for future devices 13 | showtext_auto() 14 | 15 | p1 <- motifr::show_motif(motif = "2,2[III.C]") + 16 | theme_void() + theme_transparent() + guides(color = FALSE) 17 | 18 | p2 <- motifr::show_motif(motif = "1,2[I.C]") + 19 | theme_void() + theme_transparent() + guides(color = FALSE) 20 | p2 21 | 22 | p3 <- motifr::show_motif(motif = "2,2,1[III.D.3]") + 23 | theme_void() + theme_transparent() + guides(color = FALSE) 24 | p3 25 | 26 | p4 <- motifr::show_motif(motif = "2,2,1[III.D.1]") + 27 | theme_void() + theme_transparent() + guides(color = FALSE) 28 | p4 29 | 30 | sticker(p4, 31 | package = "motifr", p_size = 25, s_x = 1, s_y = .75, s_width = 1.3, s_height = 1, 32 | filename = "inst/figures/motifr.png", h_fill = "#ede9e8", dpi = 300, 33 | p_family = "Roboto", p_color = "black", h_color = "black" 34 | ) 35 | 36 | sticker(p4, 37 | package = "motifr", p_size = 9, s_x = 1, s_y = .75, s_width = 1.3, s_height = 1, 38 | filename = "inst/figures/motifr.svg", h_fill = "#ede9e8", dpi = 300, 39 | p_family = "Roboto", p_color = "black", h_color = "black" 40 | ) 41 | 42 | usethis::use_logo(img = "inst/figures/motifr.svg") 43 | -------------------------------------------------------------------------------- /notes/tidygraph_dummy_net.R: -------------------------------------------------------------------------------- 1 | # Script for creating dummy tidygraph object 2 | library(tidygraph) 3 | 4 | tidygraph_dummy_net <- tbl_graph(directed = FALSE) %>% 5 | bind_nodes(data.frame(new = 1:3, sesType = 0)) %>% 6 | bind_nodes(data.frame(new = 3:5, sesType = 1)) %>% 7 | bind_edges(data.frame(from = 1, to = 2:6)) %>% 8 | bind_edges(data.frame(from = 4, to = 5:6)) 9 | plot(tidygraph_dummy_net) 10 | 11 | save(tidygraph_dummy_net, file = "data/tidygraph_dummy_net.RData") 12 | -------------------------------------------------------------------------------- /notes/viz_example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marioangst/motifr/5c7b65acfc2322d0d38cee91653c96155db739e7/notes/viz_example.png -------------------------------------------------------------------------------- /pkgdown/favicon/apple-touch-icon-120x120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marioangst/motifr/5c7b65acfc2322d0d38cee91653c96155db739e7/pkgdown/favicon/apple-touch-icon-120x120.png -------------------------------------------------------------------------------- /pkgdown/favicon/apple-touch-icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marioangst/motifr/5c7b65acfc2322d0d38cee91653c96155db739e7/pkgdown/favicon/apple-touch-icon-152x152.png -------------------------------------------------------------------------------- /pkgdown/favicon/apple-touch-icon-180x180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marioangst/motifr/5c7b65acfc2322d0d38cee91653c96155db739e7/pkgdown/favicon/apple-touch-icon-180x180.png -------------------------------------------------------------------------------- /pkgdown/favicon/apple-touch-icon-60x60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marioangst/motifr/5c7b65acfc2322d0d38cee91653c96155db739e7/pkgdown/favicon/apple-touch-icon-60x60.png -------------------------------------------------------------------------------- /pkgdown/favicon/apple-touch-icon-76x76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marioangst/motifr/5c7b65acfc2322d0d38cee91653c96155db739e7/pkgdown/favicon/apple-touch-icon-76x76.png -------------------------------------------------------------------------------- /pkgdown/favicon/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marioangst/motifr/5c7b65acfc2322d0d38cee91653c96155db739e7/pkgdown/favicon/apple-touch-icon.png -------------------------------------------------------------------------------- /pkgdown/favicon/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marioangst/motifr/5c7b65acfc2322d0d38cee91653c96155db739e7/pkgdown/favicon/favicon-16x16.png -------------------------------------------------------------------------------- /pkgdown/favicon/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marioangst/motifr/5c7b65acfc2322d0d38cee91653c96155db739e7/pkgdown/favicon/favicon-32x32.png -------------------------------------------------------------------------------- /pkgdown/favicon/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marioangst/motifr/5c7b65acfc2322d0d38cee91653c96155db739e7/pkgdown/favicon/favicon.ico -------------------------------------------------------------------------------- /tests/testthat.R: -------------------------------------------------------------------------------- 1 | library(testthat) 2 | library(motifr) 3 | library(reticulate) 4 | 5 | test_check("motifr") 6 | -------------------------------------------------------------------------------- /tests/testthat/test-core.R: -------------------------------------------------------------------------------- 1 | skip_if_no_sma <- function() { 2 | if (!reticulate::py_module_available("sma")) { 3 | skip("sma not available for testing") 4 | } 5 | } 6 | test_that("io_undirected_dummy_net", { 7 | skip_if_no_sma() 8 | 9 | nx <- reticulate::import("networkx", delay_load = TRUE) 10 | sma <- reticulate::import("sma", delay_load = TRUE) 11 | 12 | testthat::expect_warning(motifr::to_py_graph(motifr::dummy_net, "sesType", directed = TRUE)) 13 | 14 | py_g <- to_py_graph(motifr::dummy_net, "sesType") 15 | testthat::expect_false(nx$is_directed(py_g)) 16 | 17 | testthat::expect_equal( 18 | network::network.size(dummy_net), 19 | nx$number_of_nodes(py_g) 20 | ) 21 | testthat::expect_equal( 22 | network::network.edgecount(dummy_net), 23 | nx$number_of_edges(py_g) 24 | ) 25 | nodes_count <- sma$nodesCount(py_g) 26 | types <- table(network::get.vertex.attribute(dummy_net, "sesType")) 27 | testthat::expect_equal(length(nodes_count), length(types)) 28 | testthat::expect_equal(nodes_count$`0`, types[[1]]) 29 | testthat::expect_equal(nodes_count$`1`, types[[2]]) 30 | testthat::expect_equal(nodes_count$`2`, types[[3]]) 31 | }) 32 | test_that("io_undirected_dummy_net_issue#27", { 33 | skip_if_no_sma() 34 | # this checks that the workaround for 35 | # the issue https://github.com/marioangst/motifr/issues/27 36 | # works correctly 37 | ml_net27 <- network::set.vertex.attribute( 38 | ml_net, 39 | "lvl", 40 | as.character(network::get.vertex.attribute(ml_net, "sesType")) 41 | ) 42 | # suppress UserWarning issued by sma.translateGraph() 43 | ms <- reticulate::py_suppress_warnings(motif_summary(ml_net27, lvl_attr = "lvl")) 44 | testthat::expect_equal( 45 | ms$count, 46 | c(543, 167, 217, 7, 73, 1) 47 | ) 48 | }) 49 | test_that("io_directed_ml_net", { 50 | skip_if_no_sma() 51 | 52 | nx <- reticulate::import("networkx", delay_load = TRUE) 53 | sma <- reticulate::import("sma", delay_load = TRUE) 54 | 55 | py_g <- to_py_graph(motifr::ml_net, "sesType") 56 | testthat::expect_false(nx$is_directed(py_g)) 57 | 58 | testthat::expect_equal( 59 | network::network.size(ml_net), 60 | nx$number_of_nodes(py_g) 61 | ) 62 | testthat::expect_equal( 63 | network::network.edgecount(ml_net), 64 | nx$number_of_edges(py_g) 65 | ) 66 | nodes_count <- sma$nodesCount(py_g) 67 | types <- table(network::get.vertex.attribute(ml_net, "sesType")) 68 | testthat::expect_equal(length(nodes_count), length(types)) 69 | testthat::expect_equal(nodes_count$`0`, types[[1]]) 70 | testthat::expect_equal(nodes_count$`1`, types[[2]]) 71 | }) 72 | 73 | test_that("io_directed_igraph", { 74 | skip_if_no_sma() 75 | 76 | nx <- reticulate::import("networkx", delay_load = TRUE) 77 | sma <- reticulate::import("sma", delay_load = TRUE) 78 | 79 | # https://gitlab.com/t.seppelt/sesmotifanalyser/-/blob/master/test/data/digraph.py 80 | py_g <- to_py_graph(motifr::directed_dummy_net, "sesType") 81 | testthat::expect_true(nx$is_directed(py_g)) 82 | 83 | testthat::expect_equal( 84 | igraph::gorder(motifr::directed_dummy_net), 85 | nx$number_of_nodes(py_g) 86 | ) 87 | testthat::expect_equal( 88 | igraph::gsize(motifr::directed_dummy_net), 89 | nx$number_of_edges(py_g) 90 | ) 91 | nodes_count <- sma$nodesCount(py_g) 92 | types <- table(igraph::V(motifr::directed_dummy_net)$sesType) 93 | testthat::expect_equal(length(nodes_count), length(types)) 94 | testthat::expect_equal(nodes_count$`0`, types[[1]]) 95 | testthat::expect_equal(nodes_count$`1`, types[[2]]) 96 | }) 97 | 98 | test_that("io_tidygraph", { 99 | skip_if_no_sma() 100 | 101 | nx <- reticulate::import("networkx", delay_load = TRUE) 102 | sma <- reticulate::import("sma", delay_load = TRUE) 103 | 104 | # tidygraph wraps around igraph so use igraph's functions here. Perhaps change 105 | # to tidygraph's network properties at a later stage. 106 | 107 | py_g <- to_py_graph(motifr::tidygraph_dummy_net, "sesType") 108 | testthat::expect_false(nx$is_directed(py_g)) 109 | 110 | testthat::expect_equal( 111 | igraph::gorder(motifr::tidygraph_dummy_net), 112 | nx$number_of_nodes(py_g) 113 | ) 114 | testthat::expect_equal( 115 | igraph::gsize(motifr::tidygraph_dummy_net), 116 | nx$number_of_edges(py_g) 117 | ) 118 | nodes_count <- sma$nodesCount(py_g) 119 | types <- table(igraph::V(motifr::tidygraph_dummy_net)$sesType) 120 | testthat::expect_equal(length(nodes_count), length(types)) 121 | testthat::expect_equal(nodes_count$`0`, types[[1]]) 122 | testthat::expect_equal(nodes_count$`1`, types[[2]]) 123 | }) 124 | 125 | 126 | test_that("is_directed", { 127 | skip_if_no_sma() 128 | testthat::expect_equal(motifr::is.directed(dummy_net), FALSE) 129 | testthat::expect_equal(motifr::is.directed(directed_dummy_net), TRUE) 130 | testthat::expect_equal(motifr::is.directed(tidygraph_dummy_net), FALSE) 131 | testthat::expect_equal(motifr::is.directed(ml_net), FALSE) 132 | }) 133 | 134 | test_that("induced_level_subgraph", { 135 | skip_if_no_sma() 136 | 137 | nx <- reticulate::import("networkx", delay_load = TRUE) 138 | sma <- reticulate::import("sma", delay_load = TRUE) 139 | 140 | net <- motifr::dummy_net 141 | py_g <- motifr::to_py_graph(net, "sesType") 142 | nodes_count <- sma$nodesCount(py_g) 143 | edges_count <- sma$edgesCountMatrix(py_g) 144 | for (level in 1:3) { 145 | # on every level 146 | subgraph <- motifr::induced_level_subgraph(net, level - 1) 147 | testthat::expect_equal(nodes_count[[level]], network::network.size(subgraph)) 148 | py_subgraph <- motifr::to_py_graph(subgraph, "sesType") 149 | edges_count_subgraph <- sma$edgesCountMatrix(py_subgraph, nTypes = 3L) 150 | testthat::expect_equal(sum(edges_count_subgraph > 0), 1) 151 | testthat::expect_equal( 152 | edges_count_subgraph[level, level], 153 | edges_count[level, level] 154 | ) 155 | } 156 | }) 157 | -------------------------------------------------------------------------------- /tests/testthat/test-count_motifs.R: -------------------------------------------------------------------------------- 1 | skip_if_no_sma <- function() { 2 | if (!reticulate::py_module_available("sma")) { 3 | skip("sma not available for testing") 4 | } 5 | } 6 | test_that("count_directed_motifs_2", { 7 | skip_if_no_sma() 8 | # see https://gitlab.com/t.seppelt/sesmotifanalyser/-/blob/master/test/sma_test.py 9 | df <- motifr::count_motifs(directed_dummy_net, 10 | motifs = "2[1]", 11 | omit_total_result = FALSE 12 | ) 13 | motifs <- c( 14 | "2[0]", "2[1]", "2[2]" 15 | ) 16 | counts <- c(0, 2, 1) 17 | expected <- data.frame(motif = motifs, count = counts, row.names = motifs) 18 | testthat::expect_identical(df, expected) 19 | }) 20 | test_that("count_directed_motifs_0_2", { 21 | skip_if_no_sma() 22 | # see https://gitlab.com/t.seppelt/sesmotifanalyser/-/blob/master/test/sma_test.py 23 | df <- motifr::count_motifs(directed_dummy_net, 24 | motifs = "0,2[1]", 25 | omit_total_result = FALSE 26 | ) 27 | motifs <- c( 28 | "0,2[0]", "0,2[1]", "0,2[2]" 29 | ) 30 | counts <- c(1, 2, 0) 31 | expected <- data.frame(motif = motifs, count = counts, row.names = motifs) 32 | testthat::expect_identical(df, expected) 33 | }) 34 | test_that("count_directed_motifs_1_1", { 35 | skip_if_no_sma() 36 | # see https://gitlab.com/t.seppelt/sesmotifanalyser/-/blob/master/test/sma_test.py 37 | df <- motifr::count_motifs(directed_dummy_net, 38 | motifs = "1,1[1]", 39 | omit_total_result = FALSE 40 | ) 41 | motifs <- c( 42 | "1,1[0]", "1,1[1]", "1,1[2]", "1,1[3]" 43 | ) 44 | counts <- c(3, 1, 3, 2) 45 | expected <- data.frame(motif = motifs, count = counts, row.names = motifs) 46 | testthat::expect_identical(df, expected) 47 | }) 48 | 49 | 50 | test_that("count_motifs_1_2", { 51 | skip_if_no_sma() 52 | # see https://gitlab.com/t.seppelt/sesmotifanalyser/-/blob/master/test/sma_test.py 53 | df <- motifr::count_motifs(dummy_net, 54 | motifs = list("1:0,2:2[I.A]"), 55 | omit_total_result = FALSE 56 | ) 57 | motifs <- c( 58 | "1:0,2:2[I.A]", "1:0,2:2[I.B]", "1:0,2:2[I.C]", "1:0,2:2[II.A]", 59 | "1:0,2:2[II.B]", "1:0,2:2[II.C]" 60 | ) 61 | counts <- c(49, 433, 1118, 13, 143, 344) 62 | expected <- data.frame(motif = motifs, count = counts, row.names = motifs) 63 | testthat::expect_identical(df, expected) 64 | }) 65 | test_that("count_motifs_multiple", { 66 | skip_if_no_sma() 67 | # see https://gitlab.com/t.seppelt/sesmotifanalyser/-/blob/master/test/sma_test.py 68 | motifs <- c("2:1,2:2[I.A]", "2:1,2:2[IV.D]", "2:2,2:0[V.B]", "2:2,2:0[VI.A]") 69 | df <- motifr::count_motifs(dummy_net, motifs = motifs) 70 | counts <- c(901, 16, 998, 35) 71 | expected <- data.frame(motif = motifs, count = counts, row.names = motifs) 72 | testthat::expect_identical(df, expected) 73 | }) 74 | test_that("count_motifs_tidygraph_dummy", { 75 | skip_if_no_sma() 76 | motifs <- c("1,2[I.A]", "1,2[I.B]", "1,2[I.C]", "1,2[II.A]", "1,2[II.B]", "1,2[II.C]") 77 | df <- motifr::count_motifs(motifr::tidygraph_dummy_net, motifs = motifs) 78 | counts <- c(2, 0, 1, 4, 0, 2) 79 | expected <- data.frame(motif = motifs, count = counts, row.names = motifs) 80 | testthat::expect_identical(df, expected) 81 | }) 82 | test_that("simulate_ergm", { 83 | skip_if_no_sma() 84 | library(ergm) 85 | ergm_model <- ergm(dummy_net ~ density) 86 | n <- 10 87 | df <- simulate_baseline(dummy_net, list("1[1]", "0,1[1]", "0,0,1[1]"), model = "ergm", ergm_model = ergm_model, n = n) 88 | testthat::expect_equal(dim(df)[[1]], n) 89 | testthat::expect_true(all(df$`1[1]` == 20)) 90 | testthat::expect_true(all(df$`0,1[1]` == 25)) 91 | testthat::expect_true(all(df$`0,0,1[1]` == 15)) 92 | }) 93 | test_that("exemplify_motif", { 94 | skip_if_no_sma() 95 | # use small networks that have only one motif of a certain type (or none) 96 | testthat::expect_equal( 97 | motifr::exemplify_motif(motifr::tidygraph_dummy_net, "1,2[I.C]"), 98 | c(1, 5, 6) 99 | ) 100 | # requires sma version 2.1.2 101 | testthat::expect_null(motifr::exemplify_motif(motifr::tidygraph_dummy_net, "1,2[I.B]")) 102 | }) 103 | test_that("simulate_ergm", { 104 | skip_if_no_sma() 105 | library(ergm) 106 | ergm_model <- ergm(dummy_net ~ density) 107 | n <- 10 108 | df <- simulate_baseline(dummy_net, list("1[1]", "0,1[1]", "0,0,1[1]"), model = "ergm", ergm_model = ergm_model, n = n) 109 | testthat::expect_equal(dim(df)[[1]], n) 110 | testthat::expect_true(all(df$`1[1]` == 20)) 111 | testthat::expect_true(all(df$`0,1[1]` == 25)) 112 | testthat::expect_true(all(df$`0,0,1[1]` == 15)) 113 | }) 114 | test_that("simulate_partial_ergm_level1", { 115 | skip_if_no_sma() 116 | library(ergm) 117 | net <- motifr::ml_net 118 | actors <- motifr::induced_level_subgraph(net, level = 1) 119 | # very simple model to keep testing costs low 120 | ergm_model <- ergm(actors ~ density) 121 | 122 | n <- 10 123 | df <- simulate_baseline(net, 124 | list( 125 | "1[1]", "0,1[1]", 126 | "2[1]", "1,1[1]", # "0,2[1]", is variable 127 | "1,2[I.A]", "1,2[II.A]", 128 | "1,2[I.B]", "1,2[II.B]", 129 | "1,2[I.C]", "1,2[II.C]" 130 | ), 131 | model = "partial_ergm", 132 | ergm_model = ergm_model, 133 | n = n, 134 | level = 1 135 | ) 136 | testthat::expect_equal(dim(df)[[1]], n) 137 | # test node counts 138 | testthat::expect_true(all(df$`1[1]` == 52)) 139 | testthat::expect_true(all(df$`0,1[1]` == 80)) 140 | # test edge counts of fixed segments 141 | testthat::expect_true(all(df$`2[1]` == 113)) 142 | testthat::expect_true(all(df$`1,1[1]` == 140)) 143 | # test edge contribution in triangles 144 | testthat::expect_true(all(df$`1,2[I.A]` + df$`1,2[II.A]` == 153970)) 145 | testthat::expect_true(all(df$`1,2[I.B]` + df$`1,2[II.B]` == 9640)) 146 | testthat::expect_true(all(df$`1,2[I.C]` + df$`1,2[II.C]` == 710)) 147 | }) 148 | test_that("simulate_partial_ergm_level0", { 149 | skip_if_no_sma() 150 | library(ergm) 151 | net <- motifr::ml_net 152 | actors <- motifr::induced_level_subgraph(net, level = 0) 153 | # very simple model to keep testing costs low 154 | ergm_model <- ergm(actors ~ density) 155 | 156 | n <- 10 157 | df <- simulate_baseline(net, 158 | list( 159 | "1[1]", "0,1[1]", 160 | "0,2[1]", "1,1[1]", # "2[1]", is variable 161 | "2,1[I.A]", "2,1[II.A]", 162 | "2,1[I.B]", "2,1[II.B]", 163 | "2,1[I.C]", "2,1[II.C]" 164 | ), 165 | model = "partial_ergm", 166 | ergm_model = ergm_model, 167 | n = n, 168 | level = 0 169 | ) 170 | testthat::expect_equal(dim(df)[[1]], n) 171 | # test node counts 172 | testthat::expect_true(all(df$`1[1]` == 52)) 173 | testthat::expect_true(all(df$`0,1[1]` == 80)) 174 | # test edge counts of fixed segments 175 | testthat::expect_true(all(df$`0,2[1]` == 288)) 176 | testthat::expect_true(all(df$`1,1[1]` == 140)) 177 | # test edge contribution in triangles 178 | testthat::expect_true(all(df$`2,1[I.A]` + df$`2,1[II.A]` == 99164)) 179 | testthat::expect_true(all(df$`2,1[I.B]` + df$`2,1[II.B]` == 6692)) 180 | testthat::expect_true(all(df$`2,1[I.C]` + df$`2,1[II.C]` == 224)) 181 | }) 182 | -------------------------------------------------------------------------------- /tests/testthat/test-gaps_critical_edges.R: -------------------------------------------------------------------------------- 1 | skip_if_no_sma <- function() { 2 | if (!reticulate::py_module_available("sma")) { 3 | skip("sma not available for testing") 4 | } 5 | } 6 | test_that("error is given for gap test in open motif", { 7 | skip_if_no_sma() 8 | expect_error(identify_gaps(ml_net, motif = "1,2[I.C]")) 9 | }) 10 | test_that("error is given for gap test in closed motif", { 11 | skip_if_no_sma() 12 | expect_error(critical_dyads(ml_net, motif = "1,2[II.C]")) 13 | }) 14 | -------------------------------------------------------------------------------- /vignettes/.gitignore: -------------------------------------------------------------------------------- 1 | *.html 2 | *.R 3 | -------------------------------------------------------------------------------- /vignettes/README.md: -------------------------------------------------------------------------------- 1 | # Note on Pre-Build Vignettes 2 | 3 | This directory contains the vignettes for motifr. Files ``*.Rmd.orig`` contain 4 | the "real" Rmd vignettes. Using [``precompile.R``](./precompile.R) they have to 5 | be pre-compiled to ``*.Rmd`` files. This is done to circumvene problems with Python 6 | on CRAN. 7 | 8 | Please edit **only** the ``*.Rmd.orig`` files. All other files in this directory 9 | are derivates of them. 10 | 11 | For details see https://www.r-bloggers.com/2020/06/optimal-workflows-for-package-vignettes/ 12 | -------------------------------------------------------------------------------- /vignettes/motif_zoo.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: The motif zoo 3 | output: rmarkdown::html_vignette 4 | vignette: > 5 | %\VignetteIndexEntry{The motif zoo} 6 | %\VignetteEngine{knitr::rmarkdown} 7 | %\VignetteEncoding{UTF-8} 8 | --- 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | This vignette introduces the nomenclature used for labeling motifs in multi-level networks as used in motifr. 17 | 18 | ## Motif classification – how to label motifs in the wild 19 | 20 | Motifs are small subgraphs whose occurrences can reveal interesting structures in empirical networks. Motifs can span several levels of a multilevel networks. This package uses *motif identifier strings* for identifying motifs. These must provide information about 21 | 22 | - the number of nodes contained by the motif on the various levels of the network (*signature* and *positions*), 23 | - the structure of the motif itself, i.e. which edges are present, (*motif class*). 24 | 25 | A motif identifier string contains these two pieces of information. It consists of a *head* and *class*, and is of the form ``HEAD[CLASS]``, e.g. ``1,2[II.C]``. The head specifies the signature and the positions of the motif while the class represents the motif class. 26 | 27 | Let's consider an example in a two-level network with levels 0 and 1. motifr generally counts levels starting at 0, due to its Python roots. The motif identifier string ``1,2[II.C]`` represents the closed triangle with one node on level 0 and two nodes on level 1. ``1,2`` signifies that the first level (0) provides one node and the second (1) two nodes. The expression ``II.C`` in brackets stands for closed triangle. Alternatively, ``1,2[I.C]`` represents the open triangle with the same number of nodes taken from the levels. 28 | 29 | You can explore all possible motifs interactively in a shiny app included in the package by running (after installing and loading the package): 30 | 31 | 32 | ```r 33 | explore_motifs() 34 | ``` 35 | 36 | You can pass your own network to ``explore_motifs()`` to see what motifs mean exactly for your data. For example, if your network is stored in a object named ``my_net`` with a level attribute ``lvl`` you can explore motifs within it interactively using ``explore_motifs(net = my_net, lvl_attr = "lvl")`` 37 | 38 | When in doubt about what a given identifier string implies for you specific network, you can let motifr show you the motif either in a dummy network or an example of the motif in a network you are analysing (the second will only work if the network you are analysing actually contains the motif) with ``show_motif(motif)``. 39 | 40 | The following code below uses show_motif to illustrate a "1,2[II.C]" motif as found in the wetland management network object ml_net. ml_net contains a level attribute (specifying which node belongs to which level) named "sesType". 41 | 42 | 43 | ```r 44 | show_motif(motif = "1,2[II.C]", net = ml_net, label = TRUE, lvl_attr = "sesType") 45 | ``` 46 | 47 | ![plot of chunk motif_zoo_show_motif_IIC](./motif_zoo_show_motif_IIC-1.svg) 48 | 49 | motifr supports both undirected and directed motifs, although the number of directed motifs currently supported is much more limited than the number of supported undirected motifs. If your favourite motif is not part of the zoo yet, reach out to us by [creating an issue on github](https://github.com/marioangst/motifr/issues) describing the motif you would like to have included and we will consider the request. 50 | 51 | A full list of all supported signatures and motif classes can also be found in the [documentation of the Python sma package](https://gitlab.com/t.seppelt/sesmotifanalyser/raw/master/doc/_build/latex/SESMotifAnalyser.pdf?inline=false) in section *Appendix: The Motif Zoo*. 52 | 53 | ## A word of caution on counting: Position matching 54 | 55 | Motifr uses an internal mechanism called *position matching* for translating the head of a motif identifier string, e.g. ``1,2``, to a sequence of levels corresponding to the signature of the motif. Usually, this procedure yields the desired matching and is of no interest to the user. However, sometimes it might be necessary two overwrite the position matching by providing custom positions. 56 | 57 | An example for this is the motif ``2,2[II.D]``. It contains two nodes on level 0 and two nodes on level 1. The two nodes on level 0 and the two nodes on level 1 are respectively adjacent. Furthermore the two nodes on level 0 are linked to one of the nodes on level 1. 58 | 59 | 60 | ```r 61 | show_motif(motif = "2,2[II.D]", label = TRUE) 62 | ``` 63 | 64 | ![plot of chunk motif_zoo_show_motif_22IID](./motif_zoo_show_motif_22IID-1.svg) 65 | 66 | 67 | This motif differs from the motif ``2:1,2:0[II.D]`` which contains the same number of nodes from the different levels. However, here the roles of the levels are swapped, i.e. in this motif, the two nodes on level 1 are adjacent to the two nodes on level 0. 68 | 69 | 70 | ```r 71 | show_motif(motif = "2:1,2:0[II.D]", label = TRUE) 72 | ``` 73 | 74 | ![plot of chunk motif_zoo_show_motif2120IID](./motif_zoo_show_motif2120IID-1.svg) 75 | 76 | The position matching can be overwritten by providing the levels corresponding to the entries in the motif signature, e.g. the head ``2:1,2:0`` signalises that the first level containing two nodes is level 1 ("2:1") while the second level containing two nodes is level 0 ("2:0"). 77 | 78 | See the [documentation of the Python sma package](https://gitlab.com/t.seppelt/sesmotifanalyser/raw/master/doc/_build/latex/SESMotifAnalyser.pdf?inline=false), subsection *Position matching* for a detailed description of the procedure. 79 | 80 | In general, it is recommended to always use ``show_motif(motif)`` to check whether the software interprets the provided motif identifier string as intended by the analyst. 81 | -------------------------------------------------------------------------------- /vignettes/motif_zoo.Rmd.orig: -------------------------------------------------------------------------------- 1 | --- 2 | title: The motif zoo 3 | output: rmarkdown::html_vignette 4 | vignette: > 5 | %\VignetteIndexEntry{The motif zoo} 6 | %\VignetteEngine{knitr::rmarkdown} 7 | %\VignetteEncoding{UTF-8} 8 | --- 9 | 10 | ```{r, include = FALSE} 11 | knitr::opts_chunk$set( 12 | collapse = TRUE, 13 | comment = "#>", 14 | fig.width = 7, 15 | dev = "svg", 16 | fig.path = "./" 17 | ) 18 | ``` 19 | 20 | 21 | 22 | ```{r setup, include=FALSE} 23 | library(motifr) 24 | ``` 25 | 26 | This vignette introduces the nomenclature used for labeling motifs in multi-level networks as used in motifr. 27 | 28 | ## Motif classification – how to label motifs in the wild 29 | 30 | Motifs are small subgraphs whose occurrences can reveal interesting structures in empirical networks. Motifs can span several levels of a multilevel networks. This package uses *motif identifier strings* for identifying motifs. These must provide information about 31 | 32 | - the number of nodes contained by the motif on the various levels of the network (*signature* and *positions*), 33 | - the structure of the motif itself, i.e. which edges are present, (*motif class*). 34 | 35 | A motif identifier string contains these two pieces of information. It consists of a *head* and *class*, and is of the form ``HEAD[CLASS]``, e.g. ``1,2[II.C]``. The head specifies the signature and the positions of the motif while the class represents the motif class. 36 | 37 | Let's consider an example in a two-level network with levels 0 and 1. motifr generally counts levels starting at 0, due to its Python roots. The motif identifier string ``1,2[II.C]`` represents the closed triangle with one node on level 0 and two nodes on level 1. ``1,2`` signifies that the first level (0) provides one node and the second (1) two nodes. The expression ``II.C`` in brackets stands for closed triangle. Alternatively, ``1,2[I.C]`` represents the open triangle with the same number of nodes taken from the levels. 38 | 39 | You can explore all possible motifs interactively in a shiny app included in the package by running (after installing and loading the package): 40 | 41 | ```{r, eval=FALSE} 42 | explore_motifs() 43 | ``` 44 | 45 | You can pass your own network to ``explore_motifs()`` to see what motifs mean exactly for your data. For example, if your network is stored in a object named ``my_net`` with a level attribute ``lvl`` you can explore motifs within it interactively using ``explore_motifs(net = my_net, lvl_attr = "lvl")`` 46 | 47 | When in doubt about what a given identifier string implies for you specific network, you can let motifr show you the motif either in a dummy network or an example of the motif in a network you are analysing (the second will only work if the network you are analysing actually contains the motif) with ``show_motif(motif)``. 48 | 49 | The following code below uses show_motif to illustrate a "1,2[II.C]" motif as found in the wetland management network object ml_net. ml_net contains a level attribute (specifying which node belongs to which level) named "sesType". 50 | 51 | ```{r motif_zoo_show_motif_IIC} 52 | show_motif(motif = "1,2[II.C]", net = ml_net, label = TRUE, lvl_attr = "sesType") 53 | ``` 54 | 55 | motifr supports both undirected and directed motifs, although the number of directed motifs currently supported is much more limited than the number of supported undirected motifs. If your favourite motif is not part of the zoo yet, reach out to us by [creating an issue on github](https://github.com/marioangst/motifr/issues) describing the motif you would like to have included and we will consider the request. 56 | 57 | A full list of all supported signatures and motif classes can also be found in the [documentation of the Python sma package](https://gitlab.com/t.seppelt/sesmotifanalyser/raw/master/doc/_build/latex/SESMotifAnalyser.pdf?inline=false) in section *Appendix: The Motif Zoo*. 58 | 59 | ## A word of caution on counting: Position matching 60 | 61 | Motifr uses an internal mechanism called *position matching* for translating the head of a motif identifier string, e.g. ``1,2``, to a sequence of levels corresponding to the signature of the motif. Usually, this procedure yields the desired matching and is of no interest to the user. However, sometimes it might be necessary two overwrite the position matching by providing custom positions. 62 | 63 | An example for this is the motif ``2,2[II.D]``. It contains two nodes on level 0 and two nodes on level 1. The two nodes on level 0 and the two nodes on level 1 are respectively adjacent. Furthermore the two nodes on level 0 are linked to one of the nodes on level 1. 64 | 65 | ```{r motif_zoo_show_motif_22IID} 66 | show_motif(motif = "2,2[II.D]", label = TRUE) 67 | ``` 68 | 69 | 70 | This motif differs from the motif ``2:1,2:0[II.D]`` which contains the same number of nodes from the different levels. However, here the roles of the levels are swapped, i.e. in this motif, the two nodes on level 1 are adjacent to the two nodes on level 0. 71 | 72 | ```{r motif_zoo_show_motif2120IID} 73 | show_motif(motif = "2:1,2:0[II.D]", label = TRUE) 74 | ``` 75 | 76 | The position matching can be overwritten by providing the levels corresponding to the entries in the motif signature, e.g. the head ``2:1,2:0`` signalises that the first level containing two nodes is level 1 ("2:1") while the second level containing two nodes is level 0 ("2:0"). 77 | 78 | See the [documentation of the Python sma package](https://gitlab.com/t.seppelt/sesmotifanalyser/raw/master/doc/_build/latex/SESMotifAnalyser.pdf?inline=false), subsection *Position matching* for a detailed description of the procedure. 79 | 80 | In general, it is recommended to always use ``show_motif(motif)`` to check whether the software interprets the provided motif identifier string as intended by the analyst. 81 | -------------------------------------------------------------------------------- /vignettes/motif_zoo_show_motif_IIC-1.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | -------------------------------------------------------------------------------- /vignettes/precompile.R: -------------------------------------------------------------------------------- 1 | # Pre-Build Vignettes 2 | 3 | # In order to circumvene problems with Python dependencies on CRAN, vignettes are 4 | # pre-build following https://ropensci.org/technotes/2019/12/08/precompute-vignettes/. 5 | # The following commands have to be run: 6 | 7 | knitr::knit("vignettes/motif_zoo.Rmd.orig", output = "vignettes/motif_zoo.Rmd") 8 | knitr::knit("vignettes/random_baselines.Rmd.orig", output = "vignettes/random_baselines.Rmd") 9 | 10 | # copy figures from ./ to vignettes/ 11 | current_folder <- "./" 12 | new_folder <- "vignettes/" 13 | list_of_files <- list.files(current_folder, "(motif_zoo|random_baselines)_.*.svg$") 14 | file.rename(from = file.path(current_folder,list_of_files), 15 | to = file.path(new_folder,list_of_files)) 16 | -------------------------------------------------------------------------------- /vignettes/random_baselines.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: Baseline model comparisons 3 | output: rmarkdown::html_vignette 4 | vignette: > 5 | %\VignetteIndexEntry{Baseline model comparisons} 6 | %\VignetteEngine{knitr::rmarkdown} 7 | %\VignetteEncoding{UTF-8} 8 | --- 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | This vignette describes the models used to specify baseline motif distributions to compare empirically observed networks against in motifr. Baseline models have to be specified when requesting random networks to compare empirically observed networks against (e.g. using ``compare_to_baseline()``), and when computing properties of motif distributions analytically (e.g, ``motifs_distribution()``). 17 | 18 | ## Baseline models 19 | 20 | Currently, four baseline models are implemented. The parameters of the models are extracted from a given (empirical) network. 21 | 22 | - *Fixed Densities Model* (``fixed_densities``) Given two levels A and B, in this model it is assumed that the number of edges between level A and level B is fixed for all A-B ties. 23 | The specific number of edges is drawn randomly from the set of all possible edges. 24 | - *Erdős-Rényi Model* (``erdos_renyi``) In this model edges _between_ the various levels are drawn independently at random with respect to a fixed probability depending on the levels. 25 | The (refined) Erdős-Rényi model accounts for the various levels by allowing 26 | to specify different probabilities ties occurring _within_ different levels. 27 | - *Actor's Choice Model* (``actors_choice``) In this model all edges but the edges on one specific level are fixed. This is motivated by theoretical considerations about the application of multi-level network models in situations where one level is composed of actors, which are more readily able to change their connections, and other levels considered more stable, e.g. specific instances of ecological networks. The edges on this non-fixed "actor level" are chosen independently with fixed probability as in Erdős-Rényi. 28 | - *Exponential Random Graph Model* (``ergm``) Sophisticated model which accounts for various network parameters. Please consult the documentation of ``ergm::ergm()`` for details. 29 | - *Partial Exponential Random Graph Model* (``partial_ergm``) It tends to be hard to fit an ERGM to a multi-level network. However, this can be often avoided by using ERGM only for modelling the behaviour of one level while keeping the other levels fixed, motivated by the same theoretical considerations as for the Actor's choice model. 30 | 31 | For further details see the [documentation of the Python sma package](https://gitlab.com/t.seppelt/sesmotifanalyser/raw/master/doc/_build/latex/SESMotifAnalyser.pdf?inline=false). 32 | 33 | ## Motifs in networks simulated based on a baseline model 34 | 35 | Motifs in random networks can be counted using ``simulate_baseline()``. The supported models are 36 | ``erdos_renyi``, ``fixed_densities``, ``actors_choice`` and ``ergm``. The function ``compare_to_baseline()`` can be used for 37 | plotting the results: 38 | 39 | 40 | ```r 41 | simulate_baseline(ml_net, motifs = list("1,2[I.C]", "1,2[II.C]"), n = 5, directed = FALSE) 42 | #> 1,2[I.C] 1,2[II.C] 43 | #> 1 179 19 44 | #> 2 183 21 45 | #> 3 247 22 46 | #> 4 147 19 47 | #> 5 122 12 48 | ``` 49 | 50 | ## Motif distributions without simulation 51 | 52 | Expectations and variances in ``erdos_renyi`` and ``actors_choice`` can be computed analytically, i.e. without simulating hundreds of random networks. Until now variances can only be computed for 1,2-motifs. 53 | 54 | 55 | ```r 56 | motifs_distribution(ml_net, motifs = list("1,2[I.C]", "2,2[II.D]"), directed = FALSE) 57 | #> motif expectation variance 58 | #> 1,2[I.C] 1,2[I.C] 169.14423 949.7743 59 | #> 2,2[II.D] 2,2[II.D] 68.83919 968.5823 60 | ``` 61 | 62 | ## Difference between Erdős-Rényi and Fixed Densities 63 | 64 | There is a crucial difference between the Erdős-Rényi model and Fixed Densities model. In the former, the expected number of edges on each level equals the number of edges in the empirical network, while in the latter the exact number in each random network equals this number. This difference becomes obvious when counting edges in random networks. For a network with two levels A and B ``1,1[1]`` is the motif consisting of one node from level A and one from level B, linked by an edge. 65 | 66 | 67 | ```r 68 | compare_to_baseline(ml_net, motifs = list("1,1[1]"), model = "erdos_renyi", n = 50, directed = FALSE) 69 | ``` 70 | 71 | ![plot of chunk random_baselines_chunk3](./random_baselines_chunk3-1.svg) 72 | 73 | ```r 74 | compare_to_baseline(ml_net, motifs = list("1,1[1]"), model = "fixed_densities", n = 50, directed = FALSE) 75 | ``` 76 | 77 | ![plot of chunk random_baselines_chunk3](./random_baselines_chunk3-2.svg) 78 | 79 | ## Level selection in Actor's Choice Model 80 | 81 | In the Actor's Choice Model all levels in the network except for one are assumed to be fixed. The edges in the remaining variable (or actor's) level are drawn randomly according to the Erdős-Rényi Model. The variable level can be selected using the ``level`` parameter, e.g. in ``motifs_distribution()``, or determined automatically. The default level is the first level providing exactly two nodes in the motif. Note that ``simulate_baseline()`` and ``compare_to_baseline()`` do not support automatic level selection. 82 | 83 | Let's consider an example. Assume that the variable level is level zero. The edges in the other levels are fixed. The expected number of closed triangles with two nodes on the variable level can be computed as follows. Here ``level`` is automatically determined to be 0. 84 | 85 | 86 | ```r 87 | motifs_distribution(ml_net, motifs = list("2,1[II.C]"), model = "actors_choice", directed = FALSE) 88 | #> motif expectation variance 89 | #> 1 2,1[II.C] 19.08899 64.54796 90 | ``` 91 | 92 | More interesting is the case of motifs with signature ``2,2``, i.e. with two nodes on two levels. Per default, the variable level is the first level providing two nodes, so level 0 in the first line. Manually, level 1 can be selected as variable level, see second line. 93 | 94 | 95 | ```r 96 | motifs_distribution(ml_net, motifs = list("2,2[I.A]", "2,2[I.B]", "2,2[I.C]", "2,2[I.D]"), model = "actors_choice", directed = FALSE) 97 | #> motif expectation variance 98 | #> 2,2[I.A] 2,2[I.A] 3988.44646 20908.0827 99 | #> 2,2[I.B] 2,2[I.B] 371.55354 20908.0827 100 | #> 2,2[I.C] 2,2[I.C] 648.57994 614.8427 101 | #> 2,2[I.D] 2,2[I.D] 60.42006 614.8427 102 | motifs_distribution(ml_net, motifs = list("2,2[I.A]", "2,2[I.B]", "2,2[I.C]", "2,2[I.D]"), model = "actors_choice", level = 1, directed = FALSE) 103 | #> motif expectation variance 104 | #> 2,2[I.A] 2,2[I.A] 4506.13165 4146.282609 105 | #> 2,2[I.B] 2,2[I.B] 100.88354 9.525781 106 | #> 2,2[I.C] 2,2[I.C] 451.86835 4146.282609 107 | #> 2,2[I.D] 2,2[I.D] 10.11646 9.525781 108 | ``` 109 | Note that in the first line, when level 0 is the variable level, motifs I.A and I.B have the same variance. This is because they only differ in the dyad on the variable level. Similarly, in the second line motifs I.A and I.C have the same variance as they only differ in the dyad on the variable level. 110 | 111 | ```r 112 | show_motif("2,2[I.A]", net = ml_net, label = TRUE, directed = FALSE) 113 | ``` 114 | 115 | plot of chunk random_baselines_chunk6 116 | 117 | ```r 118 | show_motif("2,2[I.B]", net = ml_net, label = TRUE, directed = FALSE) 119 | ``` 120 | 121 | plot of chunk random_baselines_chunk6 122 | 123 | ```r 124 | show_motif("2,2[I.C]", net = ml_net, label = TRUE, directed = FALSE) 125 | ``` 126 | 127 | plot of chunk random_baselines_chunk6 128 | 129 | ## Exponential Random Graph Models (ERGM) 130 | 131 | ERGMs are a powerful tool to create more advanced, parameterized baseline models. The R library ``ergm`` provides various facililties for fitting models based on a multitude of network parameters. motifr can be used to analyse random networks sampled from a given fitted ERGM. In the example below, we fit a basic ergm model to ``dummy_net`` including a triangle statistic, capturing a propensity for triangles to occur. We then use the fitted model in a generative way to simulate networks from it and collect to arrive at a baseline distribution based on this model. 132 | 133 | 134 | ```r 135 | model <- ergm(dummy_net ~ edges + triangle) 136 | #> Starting maximum pseudolikelihood estimation (MPLE): 137 | #> Evaluating the predictor and response matrix. 138 | #> Maximizing the pseudolikelihood. 139 | #> Finished MPLE. 140 | #> Starting Monte Carlo maximum likelihood estimation (MCMLE): 141 | #> Iteration 1 of at most 20: 142 | #> Optimizing with step length 1. 143 | #> The log-likelihood improved by 0.02841. 144 | #> Step length converged once. Increasing MCMC sample size. 145 | #> Iteration 2 of at most 20: 146 | #> Optimizing with step length 1. 147 | #> The log-likelihood improved by 0.001053. 148 | #> Step length converged twice. Stopping. 149 | #> Finished MCMLE. 150 | #> Evaluating log-likelihood at the estimate. Using 20 bridges: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 . 151 | #> This model was fit using MCMC. To examine model diagnostics and check for degeneracy, use the mcmc.diagnostics() function. 152 | compare_to_baseline(dummy_net, list("1,2[I.A]", "1,2[I.C]", "1,2[II.C]"), n = 50, directed = FALSE, model = "ergm", ergm_model = model) 153 | ``` 154 | 155 | ![plot of chunk random_baselines_chunk7](./random_baselines_chunk7-1.svg) 156 | 157 | ## Workflow for Partial ERGM 158 | 159 | The example below illustrates the use of a partial ergm model fit on one level of the network to be used as a baseline model. In this way, all ties except ties on one level of interest are kept fixed. On the level of interest, networks are simulated from an ergm fit to this level and used to create a baseline motif distribution in the multi-level network. 160 | 161 | To do this, the subgraph containing only one level of the network is first extracted using ``induced_level_subgraph()``, setting the level to extract (here the actor network on level ``1``). This yields a network object only containing this level to fit an ergm to. 162 | 163 | As this example is illustrative, we only fit the most basic ergm model possible, using only a density term. As such, what we do amounts to fitting a Erdős-Rényi model on one level of the multi-level network to be used to simulate a baseline from. But the actual power of using an ERGM lies in the fact that it allows the analyst to account for more subtle processes such as clustering, centralization or homophily. 164 | 165 | The resulting model fit (here captured in the object named ``actor_ergm_model``) is then used in ``compare_to_baseline()`` by passing it to the parameter ergm_model and setting model to partial_ergm. The level parameter further needs to be set to the correct level where the ergm fit is to be used (here ``1`` again). 166 | 167 | 168 | ```r 169 | net <- motifr::ml_net 170 | actor_net <- motifr::induced_level_subgraph(net, level = 1) 171 | 172 | # very simple ergm model 173 | actor_ergm_model <- ergm(actor_net ~ density) 174 | #> Starting maximum pseudolikelihood estimation (MPLE): 175 | #> Evaluating the predictor and response matrix. 176 | #> Maximizing the pseudolikelihood. 177 | #> Finished MPLE. 178 | #> Stopping at the initial estimate. 179 | #> Evaluating log-likelihood at the estimate. 180 | 181 | n <- 10 182 | compare_to_baseline(net, 183 | list("1[1]", "0,1[1]", 184 | "2[1]", "1,1[1]", # "0,2[1]", is variable 185 | "1,2[I.A]", "1,2[II.A]", 186 | "1,2[I.B]", "1,2[II.B]", 187 | "1,2[I.C]", "1,2[II.C]"), 188 | model = "partial_ergm", 189 | ergm_model = actor_ergm_model, 190 | n = n, 191 | level = 1) 192 | ``` 193 | 194 | ![plot of chunk random_baselines_chunk8](./random_baselines_chunk8-1.svg) 195 | -------------------------------------------------------------------------------- /vignettes/random_baselines.Rmd.orig: -------------------------------------------------------------------------------- 1 | --- 2 | title: Baseline model comparisons 3 | output: rmarkdown::html_vignette 4 | vignette: > 5 | %\VignetteIndexEntry{Baseline model comparisons} 6 | %\VignetteEngine{knitr::rmarkdown} 7 | %\VignetteEncoding{UTF-8} 8 | --- 9 | 10 | 11 | 12 | ```{r, include = FALSE} 13 | knitr::opts_chunk$set( 14 | collapse = TRUE, 15 | comment = "#>", 16 | fig.width = 7, 17 | dev = "svg", 18 | fig.path = "./" 19 | ) 20 | ``` 21 | 22 | ```{r setup, include=FALSE} 23 | library(motifr) 24 | library(ergm) 25 | ``` 26 | 27 | This vignette describes the models used to specify baseline motif distributions to compare empirically observed networks against in motifr. Baseline models have to be specified when requesting random networks to compare empirically observed networks against (e.g. using ``compare_to_baseline()``), and when computing properties of motif distributions analytically (e.g, ``motifs_distribution()``). 28 | 29 | ## Baseline models 30 | 31 | Currently, four baseline models are implemented. The parameters of the models are extracted from a given (empirical) network. 32 | 33 | - *Fixed Densities Model* (``fixed_densities``) Given two levels A and B, in this model it is assumed that the number of edges between level A and level B is fixed for all A-B ties. 34 | The specific number of edges is drawn randomly from the set of all possible edges. 35 | - *Erdős-Rényi Model* (``erdos_renyi``) In this model edges _between_ the various levels are drawn independently at random with respect to a fixed probability depending on the levels. 36 | The (refined) Erdős-Rényi model accounts for the various levels by allowing 37 | to specify different probabilities ties occurring _within_ different levels. 38 | - *Actor's Choice Model* (``actors_choice``) In this model all edges but the edges on one specific level are fixed. This is motivated by theoretical considerations about the application of multi-level network models in situations where one level is composed of actors, which are more readily able to change their connections, and other levels considered more stable, e.g. specific instances of ecological networks. The edges on this non-fixed "actor level" are chosen independently with fixed probability as in Erdős-Rényi. 39 | - *Exponential Random Graph Model* (``ergm``) Sophisticated model which accounts for various network parameters. Please consult the documentation of ``ergm::ergm()`` for details. 40 | - *Partial Exponential Random Graph Model* (``partial_ergm``) It tends to be hard to fit an ERGM to a multi-level network. However, this can be often avoided by using ERGM only for modelling the behaviour of one level while keeping the other levels fixed, motivated by the same theoretical considerations as for the Actor's choice model. 41 | 42 | For further details see the [documentation of the Python sma package](https://gitlab.com/t.seppelt/sesmotifanalyser/raw/master/doc/_build/latex/SESMotifAnalyser.pdf?inline=false). 43 | 44 | ## Motifs in networks simulated based on a baseline model 45 | 46 | Motifs in random networks can be counted using ``simulate_baseline()``. The supported models are 47 | ``erdos_renyi``, ``fixed_densities``, ``actors_choice`` and ``ergm``. The function ``compare_to_baseline()`` can be used for 48 | plotting the results: 49 | 50 | ```{r random_baselines_chunk1} 51 | simulate_baseline(ml_net, motifs = list("1,2[I.C]", "1,2[II.C]"), n = 5, directed = FALSE) 52 | ``` 53 | 54 | ## Motif distributions without simulation 55 | 56 | Expectations and variances in ``erdos_renyi`` and ``actors_choice`` can be computed analytically, i.e. without simulating hundreds of random networks. Until now variances can only be computed for 1,2-motifs. 57 | 58 | ```{r random_baselines_chunk2} 59 | motifs_distribution(ml_net, motifs = list("1,2[I.C]", "2,2[II.D]"), directed = FALSE) 60 | ``` 61 | 62 | ## Difference between Erdős-Rényi and Fixed Densities 63 | 64 | There is a crucial difference between the Erdős-Rényi model and Fixed Densities model. In the former, the expected number of edges on each level equals the number of edges in the empirical network, while in the latter the exact number in each random network equals this number. This difference becomes obvious when counting edges in random networks. For a network with two levels A and B ``1,1[1]`` is the motif consisting of one node from level A and one from level B, linked by an edge. 65 | 66 | ```{r random_baselines_chunk3} 67 | compare_to_baseline(ml_net, motifs = list("1,1[1]"), model = "erdos_renyi", n = 50, directed = FALSE) 68 | compare_to_baseline(ml_net, motifs = list("1,1[1]"), model = "fixed_densities", n = 50, directed = FALSE) 69 | ``` 70 | 71 | ## Level selection in Actor's Choice Model 72 | 73 | In the Actor's Choice Model all levels in the network except for one are assumed to be fixed. The edges in the remaining variable (or actor's) level are drawn randomly according to the Erdős-Rényi Model. The variable level can be selected using the ``level`` parameter, e.g. in ``motifs_distribution()``, or determined automatically. The default level is the first level providing exactly two nodes in the motif. Note that ``simulate_baseline()`` and ``compare_to_baseline()`` do not support automatic level selection. 74 | 75 | Let's consider an example. Assume that the variable level is level zero. The edges in the other levels are fixed. The expected number of closed triangles with two nodes on the variable level can be computed as follows. Here ``level`` is automatically determined to be 0. 76 | 77 | ```{r random_baselines_chunk4} 78 | motifs_distribution(ml_net, motifs = list("2,1[II.C]"), model = "actors_choice", directed = FALSE) 79 | ``` 80 | 81 | More interesting is the case of motifs with signature ``2,2``, i.e. with two nodes on two levels. Per default, the variable level is the first level providing two nodes, so level 0 in the first line. Manually, level 1 can be selected as variable level, see second line. 82 | 83 | ```{r random_baselines_chunk5} 84 | motifs_distribution(ml_net, motifs = list("2,2[I.A]", "2,2[I.B]", "2,2[I.C]", "2,2[I.D]"), model = "actors_choice", directed = FALSE) 85 | motifs_distribution(ml_net, motifs = list("2,2[I.A]", "2,2[I.B]", "2,2[I.C]", "2,2[I.D]"), model = "actors_choice", level = 1, directed = FALSE) 86 | ``` 87 | Note that in the first line, when level 0 is the variable level, motifs I.A and I.B have the same variance. This is because they only differ in the dyad on the variable level. Similarly, in the second line motifs I.A and I.C have the same variance as they only differ in the dyad on the variable level. 88 | ```{r random_baselines_chunk6, out.width="300px"} 89 | show_motif("2,2[I.A]", net = ml_net, label = TRUE, directed = FALSE) 90 | show_motif("2,2[I.B]", net = ml_net, label = TRUE, directed = FALSE) 91 | show_motif("2,2[I.C]", net = ml_net, label = TRUE, directed = FALSE) 92 | ``` 93 | 94 | ## Exponential Random Graph Models (ERGM) 95 | 96 | ERGMs are a powerful tool to create more advanced, parameterized baseline models. The R library ``ergm`` provides various facililties for fitting models based on a multitude of network parameters. motifr can be used to analyse random networks sampled from a given fitted ERGM. In the example below, we fit a basic ergm model to ``dummy_net`` including a triangle statistic, capturing a propensity for triangles to occur. We then use the fitted model in a generative way to simulate networks from it and collect to arrive at a baseline distribution based on this model. 97 | 98 | ```{r random_baselines_chunk7} 99 | model <- ergm(dummy_net ~ edges + triangle) 100 | compare_to_baseline(dummy_net, list("1,2[I.A]", "1,2[I.C]", "1,2[II.C]"), n = 50, directed = FALSE, model = "ergm", ergm_model = model) 101 | ``` 102 | 103 | ## Workflow for Partial ERGM 104 | 105 | The example below illustrates the use of a partial ergm model fit on one level of the network to be used as a baseline model. In this way, all ties except ties on one level of interest are kept fixed. On the level of interest, networks are simulated from an ergm fit to this level and used to create a baseline motif distribution in the multi-level network. 106 | 107 | To do this, the subgraph containing only one level of the network is first extracted using ``induced_level_subgraph()``, setting the level to extract (here the actor network on level ``1``). This yields a network object only containing this level to fit an ergm to. 108 | 109 | As this example is illustrative, we only fit the most basic ergm model possible, using only a density term. As such, what we do amounts to fitting a Erdős-Rényi model on one level of the multi-level network to be used to simulate a baseline from. But the actual power of using an ERGM lies in the fact that it allows the analyst to account for more subtle processes such as clustering, centralization or homophily. 110 | 111 | The resulting model fit (here captured in the object named ``actor_ergm_model``) is then used in ``compare_to_baseline()`` by passing it to the parameter ergm_model and setting model to partial_ergm. The level parameter further needs to be set to the correct level where the ergm fit is to be used (here ``1`` again). 112 | 113 | ```{r random_baselines_chunk8} 114 | net <- motifr::ml_net 115 | actor_net <- motifr::induced_level_subgraph(net, level = 1) 116 | 117 | # very simple ergm model 118 | actor_ergm_model <- ergm(actor_net ~ density) 119 | 120 | n <- 10 121 | compare_to_baseline(net, 122 | list("1[1]", "0,1[1]", 123 | "2[1]", "1,1[1]", # "0,2[1]", is variable 124 | "1,2[I.A]", "1,2[II.A]", 125 | "1,2[I.B]", "1,2[II.B]", 126 | "1,2[I.C]", "1,2[II.C]"), 127 | model = "partial_ergm", 128 | ergm_model = actor_ergm_model, 129 | n = n, 130 | level = 1) 131 | ``` 132 | --------------------------------------------------------------------------------