├── .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 | [](https://travis-ci.org/marioangst/motifr)
23 | [](https://CRAN.R-project.org/package=motifr)
24 | [](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 | [](https://travis-ci.org/marioangst/motifr)
10 | [](https://CRAN.R-project.org/package=motifr)
11 | [](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 | 
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 | 
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 | 
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 |
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 | 
72 |
73 | ```r
74 | compare_to_baseline(ml_net, motifs = list("1,1[1]"), model = "fixed_densities", n = 50, directed = FALSE)
75 | ```
76 |
77 | 
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 |
116 |
117 | ```r
118 | show_motif("2,2[I.B]", net = ml_net, label = TRUE, directed = FALSE)
119 | ```
120 |
121 |
122 |
123 | ```r
124 | show_motif("2,2[I.C]", net = ml_net, label = TRUE, directed = FALSE)
125 | ```
126 |
127 |
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 | 
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 | 
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 |
--------------------------------------------------------------------------------