From e9653023b7f9eeb0c4911d7247ad7ce1c958a323 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sat, 28 Feb 2026 06:36:03 +0000
Subject: [PATCH 01/98] Initial plan
From 08c9ca4b49c5ff549bf923a33aad738a62ae2db0 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sat, 28 Feb 2026 06:42:17 +0000
Subject: [PATCH 02/98] Fix documentation: replace manynet/migraph references
with netrics, create _pkgdown.yml, update DESCRIPTION
Co-authored-by: jhollway <5595229+jhollway@users.noreply.github.com>
---
DESCRIPTION | 2 +-
R/mark_nodes.R | 4 +-
R/mark_ties.R | 4 +-
R/measure_features.R | 2 +-
R/motif_census.R | 2 +-
R/zzz.R | 2 +-
README.Rmd | 80 +++++++++++-------------------
_pkgdown.yml | 114 +++++++++++++++++++++++++++++++++++++++++++
8 files changed, 151 insertions(+), 59 deletions(-)
create mode 100644 _pkgdown.yml
diff --git a/DESCRIPTION b/DESCRIPTION
index ced8554..f3fdc5c 100644
--- a/DESCRIPTION
+++ b/DESCRIPTION
@@ -4,7 +4,7 @@ Version: 0.0.1
Date: 2025-12-05
Description: Many tools for marking, measuring, motifs and memberships of
many different types of networks.
- All functions operate with matrices, edge lists, and 'igraph', 'network', and 'tidygraph' objects,
+ All functions operate with matrices, edge lists, and 'igraph', 'network', 'tidygraph', and 'mnet' objects,
on directed, multiplex, multimodal, signed, and other networks.
URL: https://stocnet.github.io/netrics/
BugReports: https://github.com/stocnet/netrics/issues
diff --git a/R/mark_nodes.R b/R/mark_nodes.R
index d9c0d65..b65d3c7 100644
--- a/R/mark_nodes.R
+++ b/R/mark_nodes.R
@@ -403,7 +403,7 @@ node_is_random <- function(.data, size = 1){
#' three scores.
#' By default, `ranks = 1`.
#' @examples
-#' #node_is_max(migraph::node_degree(ison_brandes))
+#' #node_is_max(node_by_degree(manynet::ison_brandes))
#' @export
node_is_max <- function(node_measure, ranks = 1){
if(!inherits(node_measure, "node_measure"))
@@ -429,7 +429,7 @@ node_is_max <- function(node_measure, ranks = 1){
#' @rdname mark_select
#' @examples
-#' #node_is_min(migraph::node_degree(ison_brandes))
+#' #node_is_min(node_by_degree(manynet::ison_brandes))
#' @export
node_is_min <- function(node_measure, ranks = 1){
if(!inherits(node_measure, "node_measure"))
diff --git a/R/mark_ties.R b/R/mark_ties.R
index 80db090..947a026 100644
--- a/R/mark_ties.R
+++ b/R/mark_ties.R
@@ -341,7 +341,7 @@ tie_is_random <- function(.data, size = 1){
#' @rdname mark_tie_select
#' @param tie_measure An object created by a `tie_` measure.
#' @examples
-#' # tie_is_max(migraph::tie_betweenness(ison_brandes))
+#' # tie_is_max(tie_by_betweenness(manynet::ison_brandes))
#' @export
tie_is_max <- function(tie_measure){
out <- as.numeric(tie_measure) == max(as.numeric(tie_measure))
@@ -351,7 +351,7 @@ tie_is_max <- function(tie_measure){
#' @rdname mark_tie_select
#' @examples
-#' #tie_is_min(migraph::tie_betweenness(ison_brandes))
+#' #tie_is_min(tie_by_betweenness(manynet::ison_brandes))
#' @export
tie_is_min <- function(tie_measure){
out <- as.numeric(tie_measure) == min(as.numeric(tie_measure))
diff --git a/R/measure_features.R b/R/measure_features.R
index 7990b4e..b29120e 100644
--- a/R/measure_features.R
+++ b/R/measure_features.R
@@ -248,7 +248,7 @@ net_by_modularity <- function(.data,
#' with the same dimensions.
#' \eqn{SWI} also ranges between 0 and 1 with the same interpretation,
#' but where there may not be a network for which \eqn{SWI = 1}.
-#' @seealso [net_transitivity()] and [net_equivalency()]
+#' @seealso [net_by_transitivity()] and [net_by_equivalency()]
#' for how clustering is calculated
#' @references
#' ## On small-worldliness
diff --git a/R/motif_census.R b/R/motif_census.R
index 9bdd64d..c31862b 100644
--- a/R/motif_census.R
+++ b/R/motif_census.R
@@ -301,7 +301,7 @@ node_x_path <- function(.data){
#' @name motif_net
#' @family motifs
#' @inheritParams motif_node
-#' @param object2 A second, two-mode migraph-consistent object.
+#' @param object2 A second, two-mode network object.
NULL
#' @rdname motif_net
diff --git a/R/zzz.R b/R/zzz.R
index 8d3f887..6d5b5b0 100644
--- a/R/zzz.R
+++ b/R/zzz.R
@@ -18,7 +18,7 @@
greet_startup_cli <- function() {
tips <- c(
- "i" = "There are lots of ways to contribute to {.pkg manynet} at {.url https://github.com/stocnet/netrics/}.",
+ "i" = "There are lots of ways to contribute to {.pkg netrics} at {.url https://github.com/stocnet/netrics/}.",
"i" = "Please share bugs, issues, or feature requests at {.url https://github.com/stocnet/netrics/issues}. It's really helpful!",
# "i" = "To suppress package startup messages, use: `suppressPackageStartupMessages(library({.pkg netrics}))`.",
# "i" = "Changing the theme of all your graphs is straightforward with `set_manynet_theme()`",
diff --git a/README.Rmd b/README.Rmd
index cd41700..fafe669 100644
--- a/README.Rmd
+++ b/README.Rmd
@@ -29,11 +29,6 @@ list_data <- function(string){


[](https://app.codecov.io/gh/stocnet/netrics?branch=main)
-
-
-
-
-
## About the package
@@ -51,7 +46,8 @@ which can mess up your pretty presentation or paper.
This can make learning and using network analysis tools in R challenging.
By contrast, `{netrics}` offers _many_ analytic tools that work on _many_ (if not most) types and kinds of networks.
-It helps researchers make, modify, mark, measure, and identify nodes' motifs and memberships in networks.
+It helps researchers mark, measure, and identify nodes' motifs and memberships in networks.
+All functions operate on matrices, `{igraph}`, `{network}`, `{tidygraph}`, and `mnet` class objects.
For graph drawing see [`{autograph}`](https://stocnet.github.io/autograph/),
and for further testing and modelling capabilities
see [`{migraph}`](https://stocnet.github.io/migraph/) and the other [stocnet](https://github.com/stocnet) packages.
@@ -60,7 +56,6 @@ see [`{migraph}`](https://stocnet.github.io/migraph/) and the other [stocnet](ht
- [Motifs](#motifs)
- [Memberships](#memberships)
- [Measuring](#measuring)
-- [Tutorials](#tutorials)
- [Installation](#installation)
- [Stable](#stable)
- [Development](#development)
@@ -75,7 +70,7 @@ marks, measures, motifs, and memberships.
Marks are logical scalars or vectors, measures are numeric,
memberships categorical, and motifs result in tabular outputs.
-`{manynet}`'s `*is_*()` functions offer fast logical tests of various properties.
+`{netrics}`'s `*is_*()` functions offer fast logical tests of various properties.
Whereas `is_*()` returns a single logical value for the network,
`node_is_*()` returns a logical vector the length of the number of nodes in the network,
and `tie_is_*()` returns a logical vector the length of the number of ties in the network.
@@ -88,14 +83,14 @@ the maximum or minimum, respectively, node or tie according to some measure (see
## Motifs
-`{manynet}`'s `*by_*()` functions tabulate nodes' frequency in various motifs.
+`{netrics}`'s `*by_*()` functions tabulate nodes' frequency in various motifs.
These include:
- `r list_functions("_by_")`
## Memberships
-`{manynet}`'s `*in_*()` functions identify nodes' membership in some grouping,
+`{netrics}`'s `*in_*()` functions identify nodes' membership in some grouping,
such as a community or component.
These functions always return a character vector,
indicating e.g. that the first node is a member of group "A", the second in group "B", etc.
@@ -108,18 +103,18 @@ Gould-Fernandez brokerage roles for a one-mode network,
and the Jasny-Lubell brokerage roles for a two-mode network.
These can be analysed alone, or used as a profile for establishing equivalence.
-`{manynet}` offers both HCA and CONCOR algorithms,
+`{netrics}` offers both HCA and CONCOR algorithms,
as well as elbow, silhouette, and strict methods for _k_-cluster selection.
-`{manynet}` also includes functions for establishing membership on other bases,
+`{netrics}` also includes functions for establishing membership on other bases,
such as typical community detection algorithms,
as well as component and core-periphery partitioning algorithms.
## Measuring
-`{manynet}` also offers a large and growing smorgasbord of measures that
+`{netrics}` also offers a large and growing smorgasbord of measures that
can be used at the node, tie, and network level
to measure some feature, property, or quantity of the network.
Each recognises whether the network is directed or undirected,
@@ -140,31 +135,18 @@ Here are some examples:
- _Diffusion_: e.g. `net_reproduction()`, `net_immunity()`, `node_thresholds()`
There is a lot here,
-so we recommend you explore [the list of functions](https://stocnet.github.io/migraph/reference/index.html) to find out more.
-
-## Tutorials
-
-This package includes tutorials to help new and experienced users
-learn how they can conduct social network analysis using the package.
-These tutorials leverage the additional package `{learnr}` (see [here](https://rstudio.github.io/learnr/)),
-but we have made it easy to use `{manynet}` or `{migraph}` tutorials
-right out of the box:
-
-```{r learnr-tutes}
-run_tute()
-# run_tute("tutorial1")
-```
+so we recommend you explore [the list of functions](https://stocnet.github.io/netrics/reference/index.html) to find out more.
## Installation
### Stable
-The easiest way to install the latest stable version of `{manynet}` is via CRAN.
+The easiest way to install the latest stable version of `{netrics}` is via CRAN.
Simply open the R console and enter:
-`install.packages('manynet')`
+`install.packages('netrics')`
-`library(manynet)` will then load the package and make the data and tutorials (see below) contained within the package available.
+`library(netrics)` will then load the package and make the functions contained within the package available.
### Development
@@ -173,46 +155,42 @@ for slightly earlier access to new features or for testing,
you may wish to download and install the binaries from Github
or install from source locally.
The latest binary releases for all major OSes -- Windows, Mac, and Linux --
-can be found [here](https://github.com/stocnet/manynet/releases/latest).
+can be found [here](https://github.com/stocnet/netrics/releases/latest).
Download the appropriate binary for your operating system,
and install using an adapted version of the following commands:
-- For Windows: `install.packages("~/Downloads/manynet_winOS.zip", repos = NULL)`
-- For Mac: `install.packages("~/Downloads/manynet_macOS.tgz", repos = NULL)`
-- For Unix: `install.packages("~/Downloads/manynet_linuxOS.tar.gz", repos = NULL)`
+- For Windows: `install.packages("~/Downloads/netrics_winOS.zip", repos = NULL)`
+- For Mac: `install.packages("~/Downloads/netrics_macOS.tgz", repos = NULL)`
+- For Unix: `install.packages("~/Downloads/netrics_linuxOS.tar.gz", repos = NULL)`
-To install from source the latest main version of `{manynet}` from Github,
+To install from source the latest main version of `{netrics}` from Github,
please install the `{remotes}` package from CRAN and then:
- For latest stable version:
-`remotes::install_github("stocnet/manynet")`
+`remotes::install_github("stocnet/netrics")`
- For latest development version:
-`remotes::install_github("stocnet/manynet@develop")`
+`remotes::install_github("stocnet/netrics@develop")`
### Other sources
Those using Mac computers may also install using Macports:
-`sudo port install R-manynet`
+`sudo port install R-netrics`
## Relationship to other packages
This package stands on the shoulders of several incredible packages.
In terms of the objects it works with,
-this package aims to provide an updated, more comprehensive replacement for `{intergraph}`.
-As such it works with objects in `{igraph}` and `{network}` formats,
-but also equally well with base matrices and edgelists (data frames),
-and formats from several other packages.
-
-The user interface is inspired in some ways by Thomas Lin Pedersen's excellent `{tidygraph}` package,
-though makes some different decisions,
-and uses the quickest `{igraph}` or `{network}` routines where available.
-
-`{manynet}` has inherited most of its core functionality from its maternal package, `{migraph}`.
-`{migraph}` continues to offer more analytic and modelling functions that builds upon
-the architecture provided by `{manynet}`.
-For more, please check out `{migraph}` directly.
+`{netrics}` operates on matrices, `{igraph}`, `{network}`, `{tidygraph}`, and `mnet` class objects.
+It uses the quickest `{igraph}` or `{network}` routines where available.
+
+`{netrics}` contains the analytic functions (measures, memberships, and motifs)
+from `{manynet}`, allowing `{manynet}` to concentrate on working with
+different network classes and modifying them.
+`{netrics}` is also designed to work well with
+[`{autograph}`](https://stocnet.github.io/autograph/) for graph drawing,
+and [`{migraph}`](https://stocnet.github.io/migraph/) for further testing and modelling capabilities.
## Funding details
diff --git a/_pkgdown.yml b/_pkgdown.yml
new file mode 100644
index 0000000..911b989
--- /dev/null
+++ b/_pkgdown.yml
@@ -0,0 +1,114 @@
+url: https://stocnet.github.io/netrics/
+
+template:
+ bootstrap: 5
+
+reference:
+- title: Marking
+ desc: >
+ Logical tests of properties for networks, nodes, and ties.
+ contents:
+ - mark_nodes
+ - mark_ties
+ - mark_select
+ - mark_tie_select
+ - mark_core
+ - mark_diff
+ - mark_triangles
+
+- title: Measuring
+ desc: >
+ Numeric measures of properties for networks, nodes, and ties.
+
+- subtitle: Centrality
+ desc: >
+ Measures of node and network centrality.
+ contents:
+ - measure_central_degree
+ - measure_central_close
+ - measure_central_between
+ - measure_central_eigen
+
+- subtitle: Closure
+ desc: >
+ Measures of network closure and clustering.
+ contents:
+ - measure_closure
+
+- subtitle: Cohesion
+ desc: >
+ Measures of network cohesion.
+ contents:
+ - measure_cohesion
+ - measure_fragmentation
+ - measure_breadth
+
+- subtitle: Features
+ desc: >
+ Measures of topological features such as core-periphery,
+ modularity, small-worldness, scale-freeness, and balance.
+ contents:
+ - measure_features
+ - measure_periods
+
+- subtitle: Heterogeneity
+ desc: >
+ Measures of network diversity and heterogeneity.
+ contents:
+ - measure_heterogeneity
+
+- subtitle: Hierarchy
+ desc: >
+ Measures of network hierarchy.
+ contents:
+ - measure_hierarchy
+
+- subtitle: Holes
+ desc: >
+ Measures of structural holes and brokerage opportunity.
+ contents:
+ - measure_holes
+
+- subtitle: Diffusion
+ desc: >
+ Measures of diffusion properties for networks and nodes.
+ contents:
+ - measure_diffusion_net
+ - measure_diffusion_node
+ - measure_diffusion_infection
+
+- title: Memberships
+ desc: >
+ Categorical classifications of nodes into groups.
+ contents:
+ - member_community_hier
+ - member_community_non
+ - member_components
+ - member_core
+ - member_equivalence
+ - member_cliques
+ - member_brokerage
+ - member_diffusion
+
+- title: Motifs
+ desc: >
+ Tabulations of nodes' and networks' participation in
+ network substructures and motifs.
+ contents:
+ - motif_net
+ - motif_node
+ - motif_brokerage
+ - motif_diffusion
+
+- title: Models
+ desc: >
+ Clustering algorithms and methods for selecting the
+ number of clusters.
+ contents:
+ - model_cluster
+ - model_kselect
+
+- title: Other
+ contents:
+ - defunct
+ - reexports
From 174f25892ab8b78c9a0fcc5f3068b4ffd273df99 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sat, 28 Feb 2026 06:44:29 +0000
Subject: [PATCH 03/98] Update README.md to match README.Rmd fixes (replace
manynet/migraph refs, remove tutorials section, update installation)
Co-authored-by: jhollway <5595229+jhollway@users.noreply.github.com>
---
README.md | 100 ++++++++++++++++--------------------------------------
1 file changed, 30 insertions(+), 70 deletions(-)
diff --git a/README.md b/README.md
index b7b0180..d4893a7 100644
--- a/README.md
+++ b/README.md
@@ -16,11 +16,6 @@ Release
Date](https://img.shields.io/github/release-date/stocnet/netrics)
[](https://app.codecov.io/gh/stocnet/netrics?branch=main)
-
-
-
-
-
## About the package
@@ -38,7 +33,7 @@ make learning and using network analysis tools in R challenging.
By contrast, `{netrics}` offers *many* analytic tools that work on
*many* (if not most) types and kinds of networks. It helps researchers
-make, modify, mark, measure, and identify nodes’ motifs and memberships
+mark, measure, and identify nodes’ motifs and memberships
in networks. For graph drawing see
[`{autograph}`](https://stocnet.github.io/autograph/), and for further
testing and modelling capabilities see
@@ -49,7 +44,6 @@ testing and modelling capabilities see
- [Motifs](#motifs)
- [Memberships](#memberships)
- [Measuring](#measuring)
-- [Tutorials](#tutorials)
- [Installation](#installation)
- [Stable](#stable)
- [Development](#development)
@@ -63,7 +57,7 @@ own pretty `print()` and `plot()` methods: marks, measures, motifs, and
memberships. Marks are logical scalars or vectors, measures are numeric,
memberships categorical, and motifs result in tabular outputs.
-`{manynet}`’s `*is_*()` functions offer fast logical tests of various
+`{netrics}`’s `*is_*()` functions offer fast logical tests of various
properties. Whereas `is_*()` returns a single logical value for the
network, `node_is_*()` returns a logical vector the length of the number
of nodes in the network, and `tie_is_*()` returns a logical vector the
@@ -87,7 +81,7 @@ maximum or minimum, respectively, node or tie according to some measure
## Motifs
-`{manynet}`‘s `*by_*()` functions tabulate nodes’ frequency in various
+`{netrics}`‘s `*by_*()` functions tabulate nodes’ frequency in various
motifs. These include:
- `net_by_adhesion()`, `net_by_assortativity()`, `net_by_balance()`,
@@ -130,7 +124,7 @@ motifs. These include:
## Memberships
-`{manynet}`‘s `*in_*()` functions identify nodes’ membership in some
+`{netrics}`‘s `*in_*()` functions identify nodes’ membership in some
grouping, such as a community or component. These functions always
return a character vector, indicating e.g. that the first node is a
member of group “A”, the second in group “B”, etc.
@@ -149,18 +143,18 @@ participation in Gould-Fernandez brokerage roles for a one-mode network,
and the Jasny-Lubell brokerage roles for a two-mode network.
These can be analysed alone, or used as a profile for establishing
-equivalence. `{manynet}` offers both HCA and CONCOR algorithms, as well
+equivalence. `{netrics}` offers both HCA and CONCOR algorithms, as well
as elbow, silhouette, and strict methods for *k*-cluster selection.
-`{manynet}` also includes functions for establishing membership on other
+`{netrics}` also includes functions for establishing membership on other
bases, such as typical community detection algorithms, as well as
component and core-periphery partitioning algorithms.
## Measuring
-`{manynet}` also offers a large and growing smorgasbord of measures that
+`{netrics}` also offers a large and growing smorgasbord of measures that
can be used at the node, tie, and network level to measure some feature,
property, or quantity of the network. Each recognises whether the
network is directed or undirected, weighted or unweighted, one-mode or
@@ -187,48 +181,20 @@ can be overrided. Here are some examples:
`node_thresholds()`
There is a lot here, so we recommend you explore [the list of
-functions](https://stocnet.github.io/migraph/reference/index.html) to
+functions](https://stocnet.github.io/netrics/reference/index.html) to
find out more.
-## Tutorials
-
-This package includes tutorials to help new and experienced users learn
-how they can conduct social network analysis using the package. These
-tutorials leverage the additional package `{learnr}` (see
-[here](https://rstudio.github.io/learnr/)), but we have made it easy to
-use `{manynet}` or `{migraph}` tutorials right out of the box:
-
-``` r
-run_tute()
-#> Checking tutorials in stocnet packages ■■■■■■■■■■■ 33% | …
-#> # A tibble: 10 × 3
-#> package name title
-#>
-#> 1 manynet tutorial0 Intro to R
-#> 2 manynet tutorial1 Data
-#> 3 autograph tutorial2 Visualisation
-#> 4 manynet tutorial3 Centrality
-#> 5 manynet tutorial4 Cohesion and Community
-#> 6 manynet tutorial5 Position and Equivalence
-#> 7 manynet tutorial6 Topology and Resilience
-#> 8 migraph tutorial7 Diffusion and Learning
-#> 9 migraph tutorial8 Diversity and Regression
-#> 10 migraph tutorial9 Modelling with ERGMs
-#> ℹ You can run a tutorial by typing e.g `run_tute('tutorial1')` or `run_tute('Data')` into the console.
-# run_tute("tutorial1")
-```
-
## Installation
### Stable
-The easiest way to install the latest stable version of `{manynet}` is
+The easiest way to install the latest stable version of `{netrics}` is
via CRAN. Simply open the R console and enter:
-`install.packages('manynet')`
+`install.packages('netrics')`
-`library(manynet)` will then load the package and make the data and
-tutorials (see below) contained within the package available.
+`library(netrics)` will then load the package and make the functions
+contained within the package available.
### Development
@@ -236,51 +202,45 @@ For the latest development version, for slightly earlier access to new
features or for testing, you may wish to download and install the
binaries from Github or install from source locally. The latest binary
releases for all major OSes – Windows, Mac, and Linux – can be found
-[here](https://github.com/stocnet/manynet/releases/latest). Download the
+[here](https://github.com/stocnet/netrics/releases/latest). Download the
appropriate binary for your operating system, and install using an
adapted version of the following commands:
- For Windows:
- `install.packages("~/Downloads/manynet_winOS.zip", repos = NULL)`
+ `install.packages("~/Downloads/netrics_winOS.zip", repos = NULL)`
- For Mac:
- `install.packages("~/Downloads/manynet_macOS.tgz", repos = NULL)`
+ `install.packages("~/Downloads/netrics_macOS.tgz", repos = NULL)`
- For Unix:
- `install.packages("~/Downloads/manynet_linuxOS.tar.gz", repos = NULL)`
+ `install.packages("~/Downloads/netrics_linuxOS.tar.gz", repos = NULL)`
-To install from source the latest main version of `{manynet}` from
+To install from source the latest main version of `{netrics}` from
Github, please install the `{remotes}` package from CRAN and then:
- For latest stable version:
- `remotes::install_github("stocnet/manynet")`
+ `remotes::install_github("stocnet/netrics")`
- For latest development version:
- `remotes::install_github("stocnet/manynet@develop")`
+ `remotes::install_github("stocnet/netrics@develop")`
### Other sources
Those using Mac computers may also install using Macports:
-`sudo port install R-manynet`
+`sudo port install R-netrics`
## Relationship to other packages
This package stands on the shoulders of several incredible packages.
-In terms of the objects it works with, this package aims to provide an
-updated, more comprehensive replacement for `{intergraph}`. As such it
-works with objects in `{igraph}` and `{network}` formats, but also
-equally well with base matrices and edgelists (data frames), and formats
-from several other packages.
-
-The user interface is inspired in some ways by Thomas Lin Pedersen’s
-excellent `{tidygraph}` package, though makes some different decisions,
-and uses the quickest `{igraph}` or `{network}` routines where
-available.
-
-`{manynet}` has inherited most of its core functionality from its
-maternal package, `{migraph}`. `{migraph}` continues to offer more
-analytic and modelling functions that builds upon the architecture
-provided by `{manynet}`. For more, please check out `{migraph}`
-directly.
+In terms of the objects it works with,
+`{netrics}` operates on matrices, `{igraph}`, `{network}`, `{tidygraph}`, and `mnet` class objects.
+It uses the quickest `{igraph}` or `{network}` routines where available.
+
+`{netrics}` contains the analytic functions (measures, memberships, and motifs)
+from `{manynet}`, allowing `{manynet}` to concentrate on working with
+different network classes and modifying them.
+`{netrics}` is also designed to work well with
+[`{autograph}`](https://stocnet.github.io/autograph/) for graph drawing,
+and [`{migraph}`](https://stocnet.github.io/migraph/) for further testing and modelling capabilities.
## Funding details
From 0badd415f94dfbf146301598f293f42bc43afea6 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sat, 28 Feb 2026 06:46:33 +0000
Subject: [PATCH 04/98] Fix roxygen examples: remove manynet:: prefix from
ison_brandes to match package conventions
Co-authored-by: jhollway <5595229+jhollway@users.noreply.github.com>
---
R/mark_nodes.R | 4 ++--
R/mark_ties.R | 4 ++--
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/R/mark_nodes.R b/R/mark_nodes.R
index b65d3c7..98f4d72 100644
--- a/R/mark_nodes.R
+++ b/R/mark_nodes.R
@@ -403,7 +403,7 @@ node_is_random <- function(.data, size = 1){
#' three scores.
#' By default, `ranks = 1`.
#' @examples
-#' #node_is_max(node_by_degree(manynet::ison_brandes))
+#' #node_is_max(node_by_degree(ison_brandes))
#' @export
node_is_max <- function(node_measure, ranks = 1){
if(!inherits(node_measure, "node_measure"))
@@ -429,7 +429,7 @@ node_is_max <- function(node_measure, ranks = 1){
#' @rdname mark_select
#' @examples
-#' #node_is_min(node_by_degree(manynet::ison_brandes))
+#' #node_is_min(node_by_degree(ison_brandes))
#' @export
node_is_min <- function(node_measure, ranks = 1){
if(!inherits(node_measure, "node_measure"))
diff --git a/R/mark_ties.R b/R/mark_ties.R
index 947a026..10c7fd1 100644
--- a/R/mark_ties.R
+++ b/R/mark_ties.R
@@ -341,7 +341,7 @@ tie_is_random <- function(.data, size = 1){
#' @rdname mark_tie_select
#' @param tie_measure An object created by a `tie_` measure.
#' @examples
-#' # tie_is_max(tie_by_betweenness(manynet::ison_brandes))
+#' # tie_is_max(tie_by_betweenness(ison_brandes))
#' @export
tie_is_max <- function(tie_measure){
out <- as.numeric(tie_measure) == max(as.numeric(tie_measure))
@@ -351,7 +351,7 @@ tie_is_max <- function(tie_measure){
#' @rdname mark_tie_select
#' @examples
-#' #tie_is_min(tie_by_betweenness(manynet::ison_brandes))
+#' #tie_is_min(tie_by_betweenness(ison_brandes))
#' @export
tie_is_min <- function(tie_measure){
out <- as.numeric(tie_measure) == min(as.numeric(tie_measure))
From 9d7494e463dee552d893ea5730759c0658ee69c9 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Mon, 2 Mar 2026 06:29:41 +0000
Subject: [PATCH 05/98] Clarify package architecture: marks in netrics, is_*()
in manynet, autograph depends on both
Co-authored-by: jhollway <5595229+jhollway@users.noreply.github.com>
---
DESCRIPTION | 4 ++
README.Rmd | 98 ++++++++++++++++++++++++-------------------
README.md | 117 ++++++++++++++++++++++++++++------------------------
3 files changed, 122 insertions(+), 97 deletions(-)
diff --git a/DESCRIPTION b/DESCRIPTION
index f3fdc5c..eb433af 100644
--- a/DESCRIPTION
+++ b/DESCRIPTION
@@ -4,6 +4,10 @@ Version: 0.0.1
Date: 2025-12-05
Description: Many tools for marking, measuring, motifs and memberships of
many different types of networks.
+ Marks ('node_is_*()' and 'tie_is_*()') identify structural positions,
+ measures ('*_by_*()') quantify network properties,
+ memberships ('*_in_*()') classify nodes into groups,
+ and motifs ('*_x_*()') tabulate substructure participation.
All functions operate with matrices, edge lists, and 'igraph', 'network', 'tidygraph', and 'mnet' objects,
on directed, multiplex, multimodal, signed, and other networks.
URL: https://stocnet.github.io/netrics/
diff --git a/README.Rmd b/README.Rmd
index fafe669..4b9a46f 100644
--- a/README.Rmd
+++ b/README.Rmd
@@ -33,24 +33,19 @@ list_data <- function(string){
## About the package
-While many awesome packages for network analysis exist for R,
-all with their own offerings and advantages,
-they also all have their own vocabulary, syntax,
-and expected formats for data inputs and analytic outputs.
-Many of these packages only work on _some_ types of networks
-(usually one-mode, simple, directed or undirected networks) for _some_ types of analysis;
-if you want to analyse a different type of network or try a different analysis,
-a different package is needed.
-And they can rely on a very different visual language (and sometimes plotting engine),
-which can mess up your pretty presentation or paper.
-This can make learning and using network analysis tools in R challenging.
-
-By contrast, `{netrics}` offers _many_ analytic tools that work on _many_ (if not most) types and kinds of networks.
-It helps researchers mark, measure, and identify nodes' motifs and memberships in networks.
-All functions operate on matrices, `{igraph}`, `{network}`, `{tidygraph}`, and `mnet` class objects.
-For graph drawing see [`{autograph}`](https://stocnet.github.io/autograph/),
+`{netrics}` is the analytic engine of the [stocnet](https://github.com/stocnet) ecosystem.
+It provides _many_ tools for marking, measuring, and identifying nodes' motifs and memberships
+in _many_ (if not most) types and kinds of networks.
+All functions operate on matrices, `{igraph}`, `{network}`, `{tidygraph}`, and `mnet` class objects,
+and recognise directed, weighted, multiplex, multimodal, signed, and other types of networks.
+
+`{netrics}` depends on [`{manynet}`](https://stocnet.github.io/manynet/),
+which handles working with different network classes, converting between them, and modifying them.
+Network-level logical tests (e.g. `is_directed()`, `is_twomode()`) remain in `{manynet}`,
+while node- and tie-level analytic marks (e.g. `node_is_*()`, `tie_is_*()`) are in `{netrics}`.
+For graph drawing, see [`{autograph}`](https://stocnet.github.io/autograph/),
and for further testing and modelling capabilities
-see [`{migraph}`](https://stocnet.github.io/migraph/) and the other [stocnet](https://github.com/stocnet) packages.
+see [`{migraph}`](https://stocnet.github.io/migraph/).
- [Marking](#marking)
- [Motifs](#motifs)
@@ -70,10 +65,11 @@ marks, measures, motifs, and memberships.
Marks are logical scalars or vectors, measures are numeric,
memberships categorical, and motifs result in tabular outputs.
-`{netrics}`'s `*is_*()` functions offer fast logical tests of various properties.
-Whereas `is_*()` returns a single logical value for the network,
+`{netrics}`'s `node_is_*()` and `tie_is_*()` functions offer fast logical tests
+of node- and tie-level properties.
`node_is_*()` returns a logical vector the length of the number of nodes in the network,
and `tie_is_*()` returns a logical vector the length of the number of ties in the network.
+Note that network-level tests such as `is_directed()` and `is_twomode()` are in `{manynet}`.
- `r list_functions("^node_is_")`
- `r list_functions("^tie_is_")`
@@ -83,14 +79,15 @@ the maximum or minimum, respectively, node or tie according to some measure (see
## Motifs
-`{netrics}`'s `*by_*()` functions tabulate nodes' frequency in various motifs.
+`{netrics}`'s `*_by_*()` functions offer numeric measures
+as well as tabulations of nodes' frequency in various motifs.
These include:
- `r list_functions("_by_")`
## Memberships
-`{netrics}`'s `*in_*()` functions identify nodes' membership in some grouping,
+`{netrics}`'s `*_in_*()` functions identify nodes' membership in some grouping,
such as a community or component.
These functions always return a character vector,
indicating e.g. that the first node is a member of group "A", the second in group "B", etc.
@@ -123,16 +120,16 @@ All return normalized values wherever possible,
though this can be overrided.
Here are some examples:
-- _Centrality_: `node_degree()`, `node_closeness()`, `node_betweenness()`, and `node_eigenvector()`,
- `net_degree()`, `net_closeness()`, `net_betweenness()`, and `net_eigenvector()`
-- _Cohesion_: `net_density()`, `net_reciprocity()`, `net_transitivity()`, `net_equivalency()`, and `net_congruency()`
-- _Hierarchy_: `net_connectedness()`, `net_efficiency()`, `net_upperbound()`
-- _Resilience_: `net_components()`, `net_cohesion()`, `net_adhesion()`, `net_diameter()`, `net_length()`
-- _Innovation_: e.g. `node_redundancy()`, `node_effsize()`, `node_efficiency()`, `node_constraint()`, `node_hierarchy()`
-- _Diversity_: `net_richness()`, `net_diversity()`, `net_heterophily()`, `net_assortativity()`,
- `node_richness()`, `node_diversity()`, `node_heterophily()`, `node_assortativity()`
-- _Topology_: e.g. `net_core()`, `net_factions()`, `net_modularity()`, `net_smallworld()`, `net_balance()`
-- _Diffusion_: e.g. `net_reproduction()`, `net_immunity()`, `node_thresholds()`
+- _Centrality_: `node_by_degree()`, `node_by_closeness()`, `node_by_betweenness()`, and `node_by_eigenvector()`,
+ `net_by_degree()`, `net_by_closeness()`, `net_by_betweenness()`, and `net_by_eigenvector()`
+- _Cohesion_: `net_by_density()`, `net_by_reciprocity()`, `net_by_transitivity()`, `net_by_equivalency()`, and `net_by_congruency()`
+- _Hierarchy_: `net_by_connectedness()`, `net_by_efficiency()`, `net_by_upperbound()`
+- _Resilience_: `net_by_components()`, `net_by_cohesion()`, `net_by_adhesion()`, `net_by_diameter()`, `net_by_length()`
+- _Innovation_: e.g. `node_by_redundancy()`, `node_by_effsize()`, `node_by_efficiency()`, `node_by_constraint()`, `node_by_hierarchy()`
+- _Diversity_: `net_by_richness()`, `net_by_diversity()`, `net_by_heterophily()`, `net_by_assortativity()`,
+ `node_by_richness()`, `node_by_diversity()`, `node_by_heterophily()`, `node_by_homophily()`
+- _Topology_: e.g. `net_by_core()`, `net_by_factions()`, `net_by_modularity()`, `net_by_smallworld()`, `net_by_balance()`
+- _Diffusion_: e.g. `net_by_reproduction()`, `net_by_immunity()`, `node_by_thresholds()`
There is a lot here,
so we recommend you explore [the list of functions](https://stocnet.github.io/netrics/reference/index.html) to find out more.
@@ -179,18 +176,33 @@ Those using Mac computers may also install using Macports:
## Relationship to other packages
-This package stands on the shoulders of several incredible packages.
-
-In terms of the objects it works with,
-`{netrics}` operates on matrices, `{igraph}`, `{network}`, `{tidygraph}`, and `mnet` class objects.
-It uses the quickest `{igraph}` or `{network}` routines where available.
-
-`{netrics}` contains the analytic functions (measures, memberships, and motifs)
-from `{manynet}`, allowing `{manynet}` to concentrate on working with
-different network classes and modifying them.
-`{netrics}` is also designed to work well with
-[`{autograph}`](https://stocnet.github.io/autograph/) for graph drawing,
-and [`{migraph}`](https://stocnet.github.io/migraph/) for further testing and modelling capabilities.
+`{netrics}` is part of the [stocnet](https://github.com/stocnet) ecosystem of R packages
+for network analysis.
+The packages are designed to be modular, with clear roles and dependencies:
+
+- [`{manynet}`](https://stocnet.github.io/manynet/):
+ The foundation package for working with network data.
+ It handles network classes (matrices, `{igraph}`, `{network}`, `{tidygraph}`, `mnet`),
+ coercion between them, modification, and network-level logical tests (`is_*()` functions).
+- **`{netrics}`**:
+ The analytic package containing all measures (`*_by_*()` functions),
+ memberships (`*_in_*()` functions), motifs (`*_x_*()` functions),
+ and node- and tie-level marks (`node_is_*()`, `tie_is_*()` functions).
+ `{netrics}` depends on `{manynet}`.
+- [`{autograph}`](https://stocnet.github.io/autograph/):
+ The graph drawing package.
+ `{autograph}` depends on both `{manynet}` (for network classes)
+ and `{netrics}` (for analytic results to visualise),
+ since it would typically be used with both.
+- [`{migraph}`](https://stocnet.github.io/migraph/):
+ The modelling and testing package,
+ building on both `{manynet}` and `{netrics}`.
+
+Node- and tie-level marks such as `node_is_cutpoint()` and `tie_is_bridge()` are kept
+in `{netrics}` rather than `{manynet}` because they are analytic functions that identify
+structural positions in the network.
+Network-level property tests like `is_directed()` remain in `{manynet}`
+because they describe the type of data rather than an analytic result.
## Funding details
diff --git a/README.md b/README.md
index d4893a7..3ff1ac9 100644
--- a/README.md
+++ b/README.md
@@ -20,25 +20,19 @@ coverage](https://codecov.io/gh/stocnet/netrics/branch/main/graph/badge.svg)](ht
## About the package
-While many awesome packages for network analysis exist for R, all with
-their own offerings and advantages, they also all have their own
-vocabulary, syntax, and expected formats for data inputs and analytic
-outputs. Many of these packages only work on *some* types of networks
-(usually one-mode, simple, directed or undirected networks) for *some*
-types of analysis; if you want to analyse a different type of network or
-try a different analysis, a different package is needed. And they can
-rely on a very different visual language (and sometimes plotting
-engine), which can mess up your pretty presentation or paper. This can
-make learning and using network analysis tools in R challenging.
-
-By contrast, `{netrics}` offers *many* analytic tools that work on
-*many* (if not most) types and kinds of networks. It helps researchers
-mark, measure, and identify nodes’ motifs and memberships
-in networks. For graph drawing see
-[`{autograph}`](https://stocnet.github.io/autograph/), and for further
-testing and modelling capabilities see
-[`{migraph}`](https://stocnet.github.io/migraph/) and the other
-[stocnet](https://github.com/stocnet) packages.
+`{netrics}` is the analytic engine of the [stocnet](https://github.com/stocnet) ecosystem.
+It provides *many* tools for marking, measuring, and identifying nodes’ motifs and memberships
+in *many* (if not most) types and kinds of networks.
+All functions operate on matrices, `{igraph}`, `{network}`, `{tidygraph}`, and `mnet` class objects,
+and recognise directed, weighted, multiplex, multimodal, signed, and other types of networks.
+
+`{netrics}` depends on [`{manynet}`](https://stocnet.github.io/manynet/),
+which handles working with different network classes, converting between them, and modifying them.
+Network-level logical tests (e.g. `is_directed()`, `is_twomode()`) remain in `{manynet}`,
+while node- and tie-level analytic marks (e.g. `node_is_*()`, `tie_is_*()`) are in `{netrics}`.
+For graph drawing, see [`{autograph}`](https://stocnet.github.io/autograph/),
+and for further testing and modelling capabilities
+see [`{migraph}`](https://stocnet.github.io/migraph/).
- [Marking](#marking)
- [Motifs](#motifs)
@@ -57,11 +51,11 @@ own pretty `print()` and `plot()` methods: marks, measures, motifs, and
memberships. Marks are logical scalars or vectors, measures are numeric,
memberships categorical, and motifs result in tabular outputs.
-`{netrics}`’s `*is_*()` functions offer fast logical tests of various
-properties. Whereas `is_*()` returns a single logical value for the
-network, `node_is_*()` returns a logical vector the length of the number
-of nodes in the network, and `tie_is_*()` returns a logical vector the
-length of the number of ties in the network.
+`{netrics}`’s `node_is_*()` and `tie_is_*()` functions offer fast logical tests
+of node- and tie-level properties.
+`node_is_*()` returns a logical vector the length of the number of nodes in the network,
+and `tie_is_*()` returns a logical vector the length of the number of ties in the network.
+Note that network-level tests such as `is_directed()` and `is_twomode()` are in `{manynet}`.
- `node_is_core()`, `node_is_cutpoint()`, `node_is_exposed()`,
`node_is_fold()`, `node_is_independent()`, `node_is_infected()`,
@@ -161,24 +155,24 @@ network is directed or undirected, weighted or unweighted, one-mode or
two-mode. All return normalized values wherever possible, though this
can be overrided. Here are some examples:
-- *Centrality*: `node_degree()`, `node_closeness()`,
- `node_betweenness()`, and `node_eigenvector()`, `net_degree()`,
- `net_closeness()`, `net_betweenness()`, and `net_eigenvector()`
-- *Cohesion*: `net_density()`, `net_reciprocity()`,
- `net_transitivity()`, `net_equivalency()`, and `net_congruency()`
-- *Hierarchy*: `net_connectedness()`, `net_efficiency()`,
- `net_upperbound()`
-- *Resilience*: `net_components()`, `net_cohesion()`, `net_adhesion()`,
- `net_diameter()`, `net_length()`
-- *Innovation*: e.g. `node_redundancy()`, `node_effsize()`,
- `node_efficiency()`, `node_constraint()`, `node_hierarchy()`
-- *Diversity*: `net_richness()`, `net_diversity()`, `net_heterophily()`,
- `net_assortativity()`, `node_richness()`, `node_diversity()`,
- `node_heterophily()`, `node_assortativity()`
-- *Topology*: e.g. `net_core()`, `net_factions()`, `net_modularity()`,
- `net_smallworld()`, `net_balance()`
-- *Diffusion*: e.g. `net_reproduction()`, `net_immunity()`,
- `node_thresholds()`
+- *Centrality*: `node_by_degree()`, `node_by_closeness()`,
+ `node_by_betweenness()`, and `node_by_eigenvector()`, `net_by_degree()`,
+ `net_by_closeness()`, `net_by_betweenness()`, and `net_by_eigenvector()`
+- *Cohesion*: `net_by_density()`, `net_by_reciprocity()`,
+ `net_by_transitivity()`, `net_by_equivalency()`, and `net_by_congruency()`
+- *Hierarchy*: `net_by_connectedness()`, `net_by_efficiency()`,
+ `net_by_upperbound()`
+- *Resilience*: `net_by_components()`, `net_by_cohesion()`, `net_by_adhesion()`,
+ `net_by_diameter()`, `net_by_length()`
+- *Innovation*: e.g. `node_by_redundancy()`, `node_by_effsize()`,
+ `node_by_efficiency()`, `node_by_constraint()`, `node_by_hierarchy()`
+- *Diversity*: `net_by_richness()`, `net_by_diversity()`, `net_by_heterophily()`,
+ `net_by_assortativity()`, `node_by_richness()`, `node_by_diversity()`,
+ `node_by_heterophily()`, `node_by_homophily()`
+- *Topology*: e.g. `net_by_core()`, `net_by_factions()`, `net_by_modularity()`,
+ `net_by_smallworld()`, `net_by_balance()`
+- *Diffusion*: e.g. `net_by_reproduction()`, `net_by_immunity()`,
+ `node_by_thresholds()`
There is a lot here, so we recommend you explore [the list of
functions](https://stocnet.github.io/netrics/reference/index.html) to
@@ -229,18 +223,33 @@ Those using Mac computers may also install using Macports:
## Relationship to other packages
-This package stands on the shoulders of several incredible packages.
-
-In terms of the objects it works with,
-`{netrics}` operates on matrices, `{igraph}`, `{network}`, `{tidygraph}`, and `mnet` class objects.
-It uses the quickest `{igraph}` or `{network}` routines where available.
-
-`{netrics}` contains the analytic functions (measures, memberships, and motifs)
-from `{manynet}`, allowing `{manynet}` to concentrate on working with
-different network classes and modifying them.
-`{netrics}` is also designed to work well with
-[`{autograph}`](https://stocnet.github.io/autograph/) for graph drawing,
-and [`{migraph}`](https://stocnet.github.io/migraph/) for further testing and modelling capabilities.
+`{netrics}` is part of the [stocnet](https://github.com/stocnet) ecosystem of R packages
+for network analysis.
+The packages are designed to be modular, with clear roles and dependencies:
+
+- [`{manynet}`](https://stocnet.github.io/manynet/):
+ The foundation package for working with network data.
+ It handles network classes (matrices, `{igraph}`, `{network}`, `{tidygraph}`, `mnet`),
+ coercion between them, modification, and network-level logical tests (`is_*()` functions).
+- **`{netrics}`**:
+ The analytic package containing all measures (`*_by_*()` functions),
+ memberships (`*_in_*()` functions), motifs (`*_x_*()` functions),
+ and node- and tie-level marks (`node_is_*()`, `tie_is_*()` functions).
+ `{netrics}` depends on `{manynet}`.
+- [`{autograph}`](https://stocnet.github.io/autograph/):
+ The graph drawing package.
+ `{autograph}` depends on both `{manynet}` (for network classes)
+ and `{netrics}` (for analytic results to visualise),
+ since it would typically be used with both.
+- [`{migraph}`](https://stocnet.github.io/migraph/):
+ The modelling and testing package,
+ building on both `{manynet}` and `{netrics}`.
+
+Node- and tie-level marks such as `node_is_cutpoint()` and `tie_is_bridge()` are kept
+in `{netrics}` rather than `{manynet}` because they are analytic functions that identify
+structural positions in the network.
+Network-level property tests like `is_directed()` remain in `{manynet}`
+because they describe the type of data rather than an analytic result.
## Funding details
From ae17369a39b72c469ac4b035869988349a138558 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Mon, 2 Mar 2026 06:33:08 +0000
Subject: [PATCH 06/98] Reorganize README: separate Measures (_by_) from Motifs
(_x_), fix naming conventions
Co-authored-by: jhollway <5595229+jhollway@users.noreply.github.com>
---
README.Rmd | 45 +++++++++++++++-------------------------
README.md | 58 ++++++++++++++++++++--------------------------------
_pkgdown.yml | 10 ++++-----
3 files changed, 44 insertions(+), 69 deletions(-)
diff --git a/README.Rmd b/README.Rmd
index 4b9a46f..6382dd1 100644
--- a/README.Rmd
+++ b/README.Rmd
@@ -48,9 +48,9 @@ and for further testing and modelling capabilities
see [`{migraph}`](https://stocnet.github.io/migraph/).
- [Marking](#marking)
+- [Measures](#measures)
- [Motifs](#motifs)
- [Memberships](#memberships)
-- [Measuring](#measuring)
- [Installation](#installation)
- [Stable](#stable)
- [Development](#development)
@@ -77,14 +77,20 @@ Note that network-level tests such as `is_directed()` and `is_twomode()` are in
The `*is_max()` and `*is_min()` functions are used to identify
the maximum or minimum, respectively, node or tie according to some measure (see below).
-## Motifs
+## Measures
-`{netrics}`'s `*_by_*()` functions offer numeric measures
-as well as tabulations of nodes' frequency in various motifs.
+`{netrics}`'s `*_by_*()` functions offer numeric measures at the network, node, and tie level.
These include:
- `r list_functions("_by_")`
+## Motifs
+
+`{netrics}`'s `*_x_*()` functions tabulate nodes' and networks' frequency in various motifs.
+These include:
+
+- `r list_functions("_x_")`
+
## Memberships
`{netrics}`'s `*_in_*()` functions identify nodes' membership in some grouping,
@@ -109,30 +115,13 @@ as well as elbow, silhouette, and strict methods for _k_-cluster selection.
such as typical community detection algorithms,
as well as component and core-periphery partitioning algorithms.
-## Measuring
-
-`{netrics}` also offers a large and growing smorgasbord of measures that
-can be used at the node, tie, and network level
-to measure some feature, property, or quantity of the network.
-Each recognises whether the network is directed or undirected,
-weighted or unweighted, one-mode or two-mode.
-All return normalized values wherever possible,
-though this can be overrided.
-Here are some examples:
-
-- _Centrality_: `node_by_degree()`, `node_by_closeness()`, `node_by_betweenness()`, and `node_by_eigenvector()`,
- `net_by_degree()`, `net_by_closeness()`, `net_by_betweenness()`, and `net_by_eigenvector()`
-- _Cohesion_: `net_by_density()`, `net_by_reciprocity()`, `net_by_transitivity()`, `net_by_equivalency()`, and `net_by_congruency()`
-- _Hierarchy_: `net_by_connectedness()`, `net_by_efficiency()`, `net_by_upperbound()`
-- _Resilience_: `net_by_components()`, `net_by_cohesion()`, `net_by_adhesion()`, `net_by_diameter()`, `net_by_length()`
-- _Innovation_: e.g. `node_by_redundancy()`, `node_by_effsize()`, `node_by_efficiency()`, `node_by_constraint()`, `node_by_hierarchy()`
-- _Diversity_: `net_by_richness()`, `net_by_diversity()`, `net_by_heterophily()`, `net_by_assortativity()`,
- `node_by_richness()`, `node_by_diversity()`, `node_by_heterophily()`, `node_by_homophily()`
-- _Topology_: e.g. `net_by_core()`, `net_by_factions()`, `net_by_modularity()`, `net_by_smallworld()`, `net_by_balance()`
-- _Diffusion_: e.g. `net_by_reproduction()`, `net_by_immunity()`, `node_by_thresholds()`
-
-There is a lot here,
-so we recommend you explore [the list of functions](https://stocnet.github.io/netrics/reference/index.html) to find out more.
+The measures are organised into several broad categories, including:
+_Centrality_, _Cohesion_, _Hierarchy_, _Innovation_ (structural holes),
+_Diversity_ (heterogeneity), _Topology_ (features), and _Diffusion_.
+Each measure recognises whether the network is directed or undirected,
+weighted or unweighted, one-mode or two-mode,
+and returns normalized values wherever possible.
+We recommend you explore [the list of functions](https://stocnet.github.io/netrics/reference/index.html) to find out more.
## Installation
diff --git a/README.md b/README.md
index 3ff1ac9..0db54bf 100644
--- a/README.md
+++ b/README.md
@@ -35,9 +35,9 @@ and for further testing and modelling capabilities
see [`{migraph}`](https://stocnet.github.io/migraph/).
- [Marking](#marking)
+- [Measures](#measures)
- [Motifs](#motifs)
- [Memberships](#memberships)
-- [Measuring](#measuring)
- [Installation](#installation)
- [Stable](#stable)
- [Development](#development)
@@ -73,10 +73,10 @@ The `*is_max()` and `*is_min()` functions are used to identify the
maximum or minimum, respectively, node or tie according to some measure
(see below).
-## Motifs
+## Measures
-`{netrics}`‘s `*by_*()` functions tabulate nodes’ frequency in various
-motifs. These include:
+`{netrics}`‘s `*_by_*()` functions offer numeric measures at the network, node, and tie level.
+These include:
- `net_by_adhesion()`, `net_by_assortativity()`, `net_by_balance()`,
`net_by_betweenness()`, `net_by_change()`, `net_by_closeness()`,
@@ -116,9 +116,19 @@ motifs. These include:
`tie_by_betweenness()`, `tie_by_closeness()`, `tie_by_cohesion()`,
`tie_by_degree()`, `tie_by_eigenvector()`
+## Motifs
+
+`{netrics}`’s `*_x_*()` functions tabulate nodes’ and networks’ frequency in various motifs.
+These include:
+
+- `net_x_brokerage()`, `net_x_dyad()`, `net_x_hazard()`,
+ `net_x_mixed()`, `net_x_tetrad()`, `net_x_triad()`,
+ `node_x_brokerage()`, `node_x_dyad()`, `node_x_exposure()`,
+ `node_x_path()`, `node_x_tetrad()`, `node_x_tie()`, `node_x_triad()`
+
## Memberships
-`{netrics}`‘s `*in_*()` functions identify nodes’ membership in some
+`{netrics}`‘s `*_in_*()` functions identify nodes’ membership in some
grouping, such as a community or component. These functions always
return a character vector, indicating e.g. that the first node is a
member of group “A”, the second in group “B”, etc.
@@ -146,37 +156,13 @@ as elbow, silhouette, and strict methods for *k*-cluster selection.
bases, such as typical community detection algorithms, as well as
component and core-periphery partitioning algorithms.
-## Measuring
-
-`{netrics}` also offers a large and growing smorgasbord of measures that
-can be used at the node, tie, and network level to measure some feature,
-property, or quantity of the network. Each recognises whether the
-network is directed or undirected, weighted or unweighted, one-mode or
-two-mode. All return normalized values wherever possible, though this
-can be overrided. Here are some examples:
-
-- *Centrality*: `node_by_degree()`, `node_by_closeness()`,
- `node_by_betweenness()`, and `node_by_eigenvector()`, `net_by_degree()`,
- `net_by_closeness()`, `net_by_betweenness()`, and `net_by_eigenvector()`
-- *Cohesion*: `net_by_density()`, `net_by_reciprocity()`,
- `net_by_transitivity()`, `net_by_equivalency()`, and `net_by_congruency()`
-- *Hierarchy*: `net_by_connectedness()`, `net_by_efficiency()`,
- `net_by_upperbound()`
-- *Resilience*: `net_by_components()`, `net_by_cohesion()`, `net_by_adhesion()`,
- `net_by_diameter()`, `net_by_length()`
-- *Innovation*: e.g. `node_by_redundancy()`, `node_by_effsize()`,
- `node_by_efficiency()`, `node_by_constraint()`, `node_by_hierarchy()`
-- *Diversity*: `net_by_richness()`, `net_by_diversity()`, `net_by_heterophily()`,
- `net_by_assortativity()`, `node_by_richness()`, `node_by_diversity()`,
- `node_by_heterophily()`, `node_by_homophily()`
-- *Topology*: e.g. `net_by_core()`, `net_by_factions()`, `net_by_modularity()`,
- `net_by_smallworld()`, `net_by_balance()`
-- *Diffusion*: e.g. `net_by_reproduction()`, `net_by_immunity()`,
- `node_by_thresholds()`
-
-There is a lot here, so we recommend you explore [the list of
-functions](https://stocnet.github.io/netrics/reference/index.html) to
-find out more.
+The measures are organised into several broad categories, including:
+*Centrality*, *Cohesion*, *Hierarchy*, *Innovation* (structural holes),
+*Diversity* (heterogeneity), *Topology* (features), and *Diffusion*.
+Each measure recognises whether the network is directed or undirected,
+weighted or unweighted, one-mode or two-mode,
+and returns normalized values wherever possible.
+We recommend you explore [the list of functions](https://stocnet.github.io/netrics/reference/index.html) to find out more.
## Installation
diff --git a/_pkgdown.yml b/_pkgdown.yml
index 911b989..4339e82 100644
--- a/_pkgdown.yml
+++ b/_pkgdown.yml
@@ -6,7 +6,7 @@ template:
reference:
- title: Marking
desc: >
- Logical tests of properties for networks, nodes, and ties.
+ Logical tests (`node_is_*()`, `tie_is_*()`) of node- and tie-level properties.
contents:
- mark_nodes
- mark_ties
@@ -16,9 +16,9 @@ reference:
- mark_diff
- mark_triangles
-- title: Measuring
+- title: Measures
desc: >
- Numeric measures of properties for networks, nodes, and ties.
+ Numeric measures (`*_by_*()`) of properties for networks, nodes, and ties.
- subtitle: Centrality
desc: >
@@ -79,7 +79,7 @@ reference:
- title: Memberships
desc: >
- Categorical classifications of nodes into groups.
+ Categorical classifications (`*_in_*()`) of nodes into groups.
contents:
- member_community_hier
- member_community_non
@@ -92,7 +92,7 @@ reference:
- title: Motifs
desc: >
- Tabulations of nodes' and networks' participation in
+ Tabulations (`*_x_*()`) of nodes' and networks' participation in
network substructures and motifs.
contents:
- motif_net
From 766da815a3d0f0f70724c7139e037a7381a487c2 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Mon, 2 Mar 2026 06:34:04 +0000
Subject: [PATCH 07/98] Fix trailing whitespace in README.Rmd
Co-authored-by: jhollway <5595229+jhollway@users.noreply.github.com>
---
README.Rmd | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.Rmd b/README.Rmd
index 6382dd1..2370fb3 100644
--- a/README.Rmd
+++ b/README.Rmd
@@ -116,7 +116,7 @@ such as typical community detection algorithms,
as well as component and core-periphery partitioning algorithms.
The measures are organised into several broad categories, including:
-_Centrality_, _Cohesion_, _Hierarchy_, _Innovation_ (structural holes),
+_Centrality_, _Cohesion_, _Hierarchy_, _Innovation_ (structural holes),
_Diversity_ (heterogeneity), _Topology_ (features), and _Diffusion_.
Each measure recognises whether the network is directed or undirected,
weighted or unweighted, one-mode or two-mode,
From 5afdf7d0a59a9410a5ef8810629c80f5d38db177 Mon Sep 17 00:00:00 2001
From: James Hollway
Date: Tue, 3 Mar 2026 19:41:34 +0100
Subject: [PATCH 08/98] Set up data_objs of different types
---
tests/testthat/helper-netrics.R | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
diff --git a/tests/testthat/helper-netrics.R b/tests/testthat/helper-netrics.R
index 0353258..be73dc1 100644
--- a/tests/testthat/helper-netrics.R
+++ b/tests/testthat/helper-netrics.R
@@ -66,3 +66,19 @@ bot5 <- function(res, dec = 4){
unname(round(res, dec))[(lr-4):lr]
} else unname(res)[(lr-2):lr]
}
+
+# data_objs <- mget(ls("package:manynet"), inherits = TRUE)
+# # Filter to relevant objects
+# # data_objs <- data_objs[grepl("ison_|fict_|irps_|mpn_", names(data_objs))]
+# # data_objs <- data_objs[!grepl("starwars|physicians|potter", names(data_objs))]
+# objs <- table_data() %>% dplyr::filter(!grepl("starwars|physicians|potter", dataset)) %>%
+# dplyr::distinct(directed, weighted, twomode, labelled, signed, multiplex, longitudinal, dynamic, changing, .keep_all = TRUE) %>%
+# dplyr::pull(dataset) %>% as.character()
+# data_objs <- data_objs[objs]
+
+data_objs <- list(directed = generate_random(12, directed = TRUE),
+ undirected = generate_random(12, directed = FALSE),
+ twomode = generate_random(c(6,6)),
+ labelled = add_node_attribute(generate_random(12, directed = TRUE), "name", paste0("Node", 1:12)),
+ signed = to_signed(generate_random(12, directed = TRUE)))
+
From 11b6a5a764f335bc5a7c145838ef90af33fbb089 Mon Sep 17 00:00:00 2001
From: James Hollway
Date: Tue, 3 Mar 2026 19:41:58 +0100
Subject: [PATCH 09/98] Added node_marks tests
---
tests/testthat/test-marks.R | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
create mode 100644 tests/testthat/test-marks.R
diff --git a/tests/testthat/test-marks.R b/tests/testthat/test-marks.R
new file mode 100644
index 0000000..9217e58
--- /dev/null
+++ b/tests/testthat/test-marks.R
@@ -0,0 +1,16 @@
+funs_objs <- mget(ls("package:netrics"), inherits = TRUE)
+# Filter to relevant objects
+
+node_marks <- funs_objs[grepl("node_is_", names(funs_objs))]
+for(fn in names(node_marks)) {
+ for (ob in names(data_objs)) {
+ test_that(paste(fn, "works on", ob), {
+ skip_if(grepl("node_is_recovered|neighbor|min|max|mean|latent|infected|core", fn))
+ if(fn == "node_is_exposed"){
+ expect_s3_class(node_marks[[fn]](data_objs[[ob]], mark = c(1,3)), "node_mark")
+ } else {
+ expect_s3_class(node_marks[[fn]](data_objs[[ob]]), "node_mark")
+ }
+ })
+ }
+}
From d2a15025468a47d256087327c33c840d92c2d620 Mon Sep 17 00:00:00 2001
From: James Hollway
Date: Tue, 3 Mar 2026 19:42:07 +0100
Subject: [PATCH 10/98] Added tie_marks tests
---
tests/testthat/test-marks.R | 19 +++++++++++++++++++
1 file changed, 19 insertions(+)
diff --git a/tests/testthat/test-marks.R b/tests/testthat/test-marks.R
index 9217e58..7567590 100644
--- a/tests/testthat/test-marks.R
+++ b/tests/testthat/test-marks.R
@@ -14,3 +14,22 @@ for(fn in names(node_marks)) {
})
}
}
+
+tie_marks <- funs_objs[grepl("tie_is_", names(funs_objs))]
+for(fn in names(tie_marks)) {
+ for (ob in names(data_objs)) {
+ test_that(paste(fn, "works on", ob), {
+ skip_if(grepl("tie_is_max|tie_is_min|tie_is_recovered", fn))
+ skip_if(grepl("tie_is_imbalanced", fn) && ob == "twomode")
+ if(fn == "tie_is_path"){
+ expect_s3_class(tie_marks[[fn]](data_objs[[ob]], 1, 2), "tie_mark")
+ } else if(fn == "tie_is_max" || fn == "tie_is_min"){
+ expect_s3_class(tie_marks[[fn]](tie_by_degree(data_objs[[ob]])), "tie_mark")
+ } else if(fn == "tie_is_infected"){
+ expect_s3_class(tie_marks[[fn]](play_diffusion(data_objs[[ob]])), "tie_mark")
+ } else {
+ expect_s3_class(tie_marks[[fn]](data_objs[[ob]]), "tie_mark")
+ }
+ })
+ }
+}
From ec5ec3366e9bdbb650d2072d97a27a50834e4111 Mon Sep 17 00:00:00 2001
From: James Hollway
Date: Tue, 3 Mar 2026 19:42:26 +0100
Subject: [PATCH 11/98] Using node_by_ and tie_by_ in examples
---
man/mark_select.Rd | 4 ++--
man/mark_tie_select.Rd | 4 ++--
man/measure_features.Rd | 2 +-
man/motif_net.Rd | 2 +-
4 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/man/mark_select.Rd b/man/mark_select.Rd
index 9e89019..52689bf 100644
--- a/man/mark_select.Rd
+++ b/man/mark_select.Rd
@@ -44,8 +44,8 @@ are key because they minimise or, more often, maximise some measure.
}
\examples{
node_is_random(ison_brandes, 2)
-#node_is_max(migraph::node_degree(ison_brandes))
-#node_is_min(migraph::node_degree(ison_brandes))
+#node_is_max(node_by_degree(ison_brandes))
+#node_is_min(node_by_degree(ison_brandes))
#node_is_mean(node_degree(ison_brandes))
}
\seealso{
diff --git a/man/mark_tie_select.Rd b/man/mark_tie_select.Rd
index 87e471e..8329e82 100644
--- a/man/mark_tie_select.Rd
+++ b/man/mark_tie_select.Rd
@@ -33,8 +33,8 @@ are key because they minimise or, more often, maximise some measure.
}
}
\examples{
-# tie_is_max(migraph::tie_betweenness(ison_brandes))
-#tie_is_min(migraph::tie_betweenness(ison_brandes))
+# tie_is_max(tie_by_betweenness(ison_brandes))
+#tie_is_min(tie_by_betweenness(ison_brandes))
}
\seealso{
Other marks:
diff --git a/man/measure_features.Rd b/man/measure_features.Rd
index 26fa8d2..cf14bdf 100644
--- a/man/measure_features.Rd
+++ b/man/measure_features.Rd
@@ -230,7 +230,7 @@ Cartwright, D., and Frank Harary. 1956.
}
}
\seealso{
-\code{\link[manynet:measure_closure]{manynet::net_transitivity()}} and \code{\link[manynet:measure_closure]{manynet::net_equivalency()}}
+\code{\link[=net_by_transitivity]{net_by_transitivity()}} and \code{\link[=net_by_equivalency]{net_by_equivalency()}}
for how clustering is calculated
Other measures:
diff --git a/man/motif_net.Rd b/man/motif_net.Rd
index 3223627..419f9f7 100644
--- a/man/motif_net.Rd
+++ b/man/motif_net.Rd
@@ -24,7 +24,7 @@ net_x_mixed(.data, object2)
For more information on the standard coercion possible,
see \code{\link[manynet:manip_as]{manynet::as_tidygraph()}}.}
-\item{object2}{A second, two-mode migraph-consistent object.}
+\item{object2}{A second, two-mode network object.}
}
\description{
These functions include ways to take a census of the graphlets
From 5ffd21fd2392e0aabbe3930a0b8004e26223df36 Mon Sep 17 00:00:00 2001
From: James Hollway
Date: Tue, 3 Mar 2026 19:42:53 +0100
Subject: [PATCH 12/98] Fixed tie_is_random to return tie marks
---
R/mark_ties.R | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/R/mark_ties.R b/R/mark_ties.R
index 10c7fd1..3f6ca4f 100644
--- a/R/mark_ties.R
+++ b/R/mark_ties.R
@@ -335,7 +335,7 @@ tie_is_random <- function(.data, size = 1){
n <- manynet::net_ties(.data)
out <- rep(FALSE, n)
out[sample.int(n, size)] <- TRUE
- make_node_mark(out, .data)
+ make_tie_mark(out, .data)
}
#' @rdname mark_tie_select
From 19412635af467a009fdbe446c0cfda652aa5b538 Mon Sep 17 00:00:00 2001
From: James Hollway
Date: Wed, 4 Mar 2026 13:09:52 +0100
Subject: [PATCH 13/98] Added .github files
---
.github/.gitignore | 1 +
.github/CODE_OF_CONDUCT.md | 25 +++
.github/CONTRIBUTING.md | 123 ++++++++++++++
.github/ISSUE_TEMPLATE/bug_report.md | 31 ++++
.../ISSUE_TEMPLATE/documentation_request.md | 12 ++
.github/ISSUE_TEMPLATE/feature_request.md | 20 +++
.github/ISSUE_TEMPLATE/test_request.md | 10 ++
.github/pull_request_template.md | 13 ++
.github/workflows/prchecks.yml | 74 +++++++++
.github/workflows/pushrelease.yml | 157 ++++++++++++++++++
10 files changed, 466 insertions(+)
create mode 100644 .github/.gitignore
create mode 100644 .github/CODE_OF_CONDUCT.md
create mode 100644 .github/CONTRIBUTING.md
create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md
create mode 100644 .github/ISSUE_TEMPLATE/documentation_request.md
create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md
create mode 100644 .github/ISSUE_TEMPLATE/test_request.md
create mode 100644 .github/pull_request_template.md
create mode 100644 .github/workflows/prchecks.yml
create mode 100644 .github/workflows/pushrelease.yml
diff --git a/.github/.gitignore b/.github/.gitignore
new file mode 100644
index 0000000..2d19fc7
--- /dev/null
+++ b/.github/.gitignore
@@ -0,0 +1 @@
+*.html
diff --git a/.github/CODE_OF_CONDUCT.md b/.github/CODE_OF_CONDUCT.md
new file mode 100644
index 0000000..24aa0a3
--- /dev/null
+++ b/.github/CODE_OF_CONDUCT.md
@@ -0,0 +1,25 @@
+# Contributor Code of Conduct
+
+As contributors and maintainers of this project, we pledge to respect all people who
+contribute through reporting issues, posting feature requests, updating documentation,
+submitting pull requests or patches, and other activities.
+
+We are committed to making participation in this project a harassment-free experience for
+everyone, regardless of level of experience, gender, gender identity and expression,
+sexual orientation, disability, personal appearance, body size, race, ethnicity, age, or religion.
+
+Examples of unacceptable behavior by participants include the use of sexual language or
+imagery, derogatory comments or personal attacks, trolling, public or private harassment,
+insults, or other unprofessional conduct.
+
+Project maintainers have the right and responsibility to remove, edit, or reject comments,
+commits, code, wiki edits, issues, and other contributions that are not aligned to this
+Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed
+from the project team.
+
+Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by
+opening an issue or contacting one or more of the project maintainers.
+
+This Code of Conduct is adapted from the Contributor Covenant
+(http://contributor-covenant.org), version 1.0.0, available at
+http://contributor-covenant.org/version/1/0/0/
diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md
new file mode 100644
index 0000000..dbbe01f
--- /dev/null
+++ b/.github/CONTRIBUTING.md
@@ -0,0 +1,123 @@
+# Contributing
+
+Contributions to `netrics`,
+whether in the form of issue identification, bug fixes, new code or documentation
+are encouraged and welcome.
+
+## Aims
+
+Here is some things that Guy Kawasaki, Silicon Valley venture capitalist,
+learned from Steve Jobs:
+
+- "Experts" are clueless. Especially self-declared ones.
+- Customers cannot tell you what they need. They can help with evolution, but not revolution.
+- Biggest challenges beget the best work.
+- Design counts. Users will see the skin/UI of your product, not the great algorithms.
+- Big graphics, big fonts.
+- Jump curves---do things 10 times better, not 10 percent.
+- All that truly matters is whether something works or doesn't work. Open or close, iPhone or Android, car or train, doesn't matter---make
+it work.
+- "Value" is different from "price". There is a class of people who do care about value. Ease of use -> less support costs. You have to create a unique and valuable product as an engineer.
+- Real CEOs can demo. If you can't demo your own product, then quit.
+- Real entrepreneurs ship, not slip.
+- Some things need to be believed to be seen.
+
+## Git and Bitbucket
+
+`stocnet` projects are maintained using the git version control system.
+A plain-English introduction to git can be found [here](https://blog.red-badger.com/2016/11/29/gitgithub-in-plain-english).
+I recommend you read this before continuing.
+A more recent motivation can be found [here](https://www.r-bloggers.com/2024/04/git-gud-version-control-best-practices/).
+It will explain the basics of git version control, committing and repos, pulling and pushing,
+branching and merging.
+
+Using git from the command line on your lap- or desktop can be intimidating,
+but I recommend [Fork](https://git-fork.com) software for Mac and Windows.
+This allows mostly visual management of commits, diffs, branches, etc.
+There are various other git software packages available, but this one is fairly fully featured.
+
+The Github page allows to access the issues assigned to you and check the commits.
+You can also access the documents in the repository,
+although this won't be necessary after you have cloned it on your computer via Fork.
+
+## Style
+
+In terms of style, we are aiming for pleasant predictability in terms of user experience.
+To that end, we have a regular syntax that users can rely on producing expected effects.
+
+## Fork
+
+### Cloning
+Once you have downloaded Fork, the first thing you have to do is to
+clone the remote repository on your computer.
+Before cloning, you will be able to choose on which `branch` you want to work:
+develop or main.
+
+### Pull
+This command allows you to `pull` changes from the remote repository to your local repository on Sourcetree.
+Make sure you do that before starting working on your files so you have the newest versions.
+When pulling, make sure you choose master or develop,
+depending on the branch you decided to work with.
+Once you pulled, you have now all the new commits and files and
+you can start working on your assigned tasks.
+Note that you can access and open the files either from the Finder or from Fork.
+Some documents might be stored using Large File Storage (LFS) to save space on the repository.
+
+### Commit and Push
+
+Once you have made modifications on a file and saved them, it will appear in your `commit` window.
+Here you can control one last time your file, write the commit message with the
+issue reference (see below) and commit.
+Once your commit is ready, you can `push` them to the origin/main repository.
+Note that you can click the "push immediately" box in the commit window
+if you don't want to do it in two steps.
+If you are working on a separate branch,
+it is important to select this branch when pushing to origin/main.
+
+## Issues and tests
+
+Please use the issues tracker on Github to identify any function-related issues.
+You can use these issues to track progress on the issue and
+to comment or continue a conversation on that issue.
+Currently issue tracking is only open to those involved in the project.
+
+The most useful issues are ones that precisely identify an error,
+or propose a test that should pass but instead fails.
+This package uses the `testthat` package for testing functions.
+Please see the [testthat website](https://testthat.r-lib.org) for more details.
+
+## Bug fixing or adding new code
+
+Independent or assigned code contributions are most welcome.
+When writing new code, please follow
+[standard R guidelines](https://www.r-bloggers.com/🖊-r-coding-style-guide/).
+It can help to use packages such as `lintr`, `goodpractice` and `formatR`
+to ensure these are followed.
+
+Currently, commits can only be pushed to Bitbucket where they reference an existing issue.
+If no issue exists for the code you have developed, please add an issue first before pushing.
+Once the issue exists, you will need to mention the issue number (preceded by a hash symbol: #)
+in the commit description:
+
+` Resolved #31 by adding a new function that does things, also updated documentation `
+
+Where the issue hash (i.e. #31) is preceded by
+`resolve`, `resolves`, `resolved`, `close`, `closes`, `closed`, `fix`, `fixes`, or `fixed`
+(capitalised or not),
+Github will automatically updated the status of the issue(s) mentioned.
+
+Our current syntactical standard is to mention the issue first and then
+provide a short description of what the committed changes do
+in relation to that issue.
+Any ancillary changes can be mentioned after a comma.
+
+## Documentation
+
+A final way of contributing to the package is in developing the
+vignettes/articles that illustrate the value added in the package.
+Please contact me with any proposals here.
+
+Please note that the `netrics` project is released with a
+[Contributor Code of Conduct](CODE_OF_CONDUCT.md).
+By contributing to this project, you agree to abide by its terms.
+
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
new file mode 100644
index 0000000..cfd4a70
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -0,0 +1,31 @@
+---
+name: Bug report
+about: Create a report to help us improve
+title: ''
+labels: bug
+assignees: ''
+
+---
+
+**Describe the bug**
+A clear and concise description of what the bug is.
+
+**Provide system information**
+ - OS: [e.g. MacOS Mojave 10.14.6]
+ - R version: [e.g. 3.5.3]
+ - migraph version: [e.g. 1.4.0]
+
+**To Reproduce**
+Please consider providing a [reprex](https://reprex.tidyverse.org) (a reproducible example),
+or outline the steps taken to reproduce the behaviour, e.g.:
+1. Go to '...'
+2. Click on '....'
+3. Scroll down to '....'
+4. See error
+If applicable, add screenshots to help explain your problem.
+
+**Expected behavior**
+A clear and concise description of what you expected to happen.
+
+**Additional context**
+Add any other context about the problem here.
diff --git a/.github/ISSUE_TEMPLATE/documentation_request.md b/.github/ISSUE_TEMPLATE/documentation_request.md
new file mode 100644
index 0000000..8c4e661
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/documentation_request.md
@@ -0,0 +1,12 @@
+---
+name: Documentation request
+about: Create a report to help us improve
+title: ''
+labels: documentation
+assignees: ''
+
+---
+
+**Name the function or functions that require further documentation here and in the title**
+
+Please describe what is not clear in the current documentation that can be expanded upon.
diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md
new file mode 100644
index 0000000..cb310f2
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/feature_request.md
@@ -0,0 +1,20 @@
+---
+name: Feature request
+about: Suggest an idea for this project
+title: ''
+labels: feature_request
+assignees: ''
+
+---
+
+**Is your feature request related to a problem? Please describe.**
+A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
+
+**Describe the solution you'd like**
+A clear and concise description of what you want to happen.
+
+**Describe alternatives you've considered**
+A clear and concise description of any alternative solutions or features you've considered.
+
+**Additional context**
+Add any other context or screenshots about the feature request here.
diff --git a/.github/ISSUE_TEMPLATE/test_request.md b/.github/ISSUE_TEMPLATE/test_request.md
new file mode 100644
index 0000000..35bf9ce
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/test_request.md
@@ -0,0 +1,10 @@
+---
+name: Test request
+about: Create a report to help us improve
+title: ''
+labels: tests
+assignees: ''
+
+---
+
+**Name the function or functions that require the tests here and in the title**
diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md
new file mode 100644
index 0000000..4e13534
--- /dev/null
+++ b/.github/pull_request_template.md
@@ -0,0 +1,13 @@
+# Description
+
+# Checklist:
+
+- Documentation
+ - [ ] DESCRIPTION file version is bumped by the appropriate increment (major, minor, patch)
+ - [ ] Date in DESCRIPTION is correct
+ - [ ] Longer functions are commented inline or broken down into helper functions to help debugging
+- PR form
+ - [ ] Title indicates expected version number
+ - [ ] PR description above and the NEWS.md file are aligned
+ - [ ] Description above itemizes changes under subsection titles, e.g. "## Data""
+ - [ ] Closed, fixed, or related issues are referenced and explained in the description above, e.g. "Fixed #0 by adding A"
diff --git a/.github/workflows/prchecks.yml b/.github/workflows/prchecks.yml
new file mode 100644
index 0000000..7ad888f
--- /dev/null
+++ b/.github/workflows/prchecks.yml
@@ -0,0 +1,74 @@
+on:
+ pull_request:
+ branches:
+ - main
+
+name: Binary checks
+
+jobs:
+
+ build:
+ name: Build for ${{ matrix.config.os }}
+ runs-on: ${{ matrix.config.os }}
+ strategy:
+ fail-fast: false
+ matrix:
+ config:
+ - {os: macOS-latest, r: 'release', artifact_name: '*.tar.gz', asset_name: macOS}
+ - {os: windows-latest, r: 'release', artifact_name: '*.zip', asset_name: winOS}
+ - {os: ubuntu-latest, r: 'release', artifact_name: '*.tar.gz', asset_name: linuxOS}
+
+ env:
+ R_REMOTES_NO_ERRORS_FROM_WARNINGS: true
+ RSPM: ${{ matrix.config.rspm }}
+ GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}
+
+ steps:
+ - uses: actions/checkout@v2
+
+ - uses: r-lib/actions/setup-r@v2
+ with:
+ r-version: ${{ matrix.config.r }}
+
+ - uses: r-lib/actions/setup-pandoc@v2
+
+ - uses: r-lib/actions/setup-r-dependencies@v2
+ with:
+ cache-version: 2
+ needs: check, build
+ extra-packages: |
+ any::rcmdcheck
+
+ - uses: r-lib/actions/check-r-package@v2
+ env:
+ _R_CHECK_FORCE_SUGGESTS_: false
+ with:
+ upload-snapshots: true
+
+ - name: Binary
+ run: |
+ pkgbuild::clean_dll()
+ binary <- pkgbuild::build(binary = TRUE, needs_compilation = TRUE, compile_attributes = TRUE)
+ dir.create("build")
+ file.copy(binary, "build")
+ shell: Rscript {0}
+
+ - name: Save binary artifact
+ uses: actions/upload-artifact@v4
+ with:
+ name: ${{ matrix.config.asset_name }}
+ path: build/
+
+ - name: Calculate code coverage
+ if: runner.os == 'macOS'
+ env:
+ CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
+ run: Rscript -e 'covr::codecov(token = Sys.getenv("CODECOV_TOKEN"))'
+
+ - name: Lint
+ run: lintr::lint_package()
+ shell: Rscript {0}
+
+ - name: Spell check
+ run: spelling::spell_check_package()
+ shell: Rscript {0}
diff --git a/.github/workflows/pushrelease.yml b/.github/workflows/pushrelease.yml
new file mode 100644
index 0000000..bd4a22b
--- /dev/null
+++ b/.github/workflows/pushrelease.yml
@@ -0,0 +1,157 @@
+on:
+ push:
+ branches:
+ - main
+
+name: Check and release
+
+jobs:
+
+ build:
+ name: Build for ${{ matrix.config.os }}
+ runs-on: ${{ matrix.config.os }}
+ strategy:
+ fail-fast: false
+ matrix:
+ config:
+ - {os: macOS-latest, r: 'release', artifact_name: '*.tar.gz', asset_name: macOS}
+ - {os: windows-latest, r: 'release', artifact_name: '*.zip', asset_name: winOS}
+ - {os: ubuntu-latest, r: 'release', artifact_name: '*.tar.gz', asset_name: linuxOS}
+
+ env:
+ R_REMOTES_NO_ERRORS_FROM_WARNINGS: true
+ RSPM: ${{ matrix.config.rspm }}
+ GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}
+
+ steps:
+ - uses: actions/checkout@v2
+
+ - uses: r-lib/actions/setup-r@v2
+ with:
+ r-version: ${{ matrix.config.r }}
+
+ - uses: r-lib/actions/setup-pandoc@v2
+
+ - uses: r-lib/actions/setup-r-dependencies@v2
+ with:
+ cache-version: 2
+ needs: check, build
+ extra-packages: |
+ any::rcmdcheck
+ any::remotes
+
+ - uses: r-lib/actions/check-r-package@v2
+ env:
+ _R_CHECK_FORCE_SUGGESTS_: false
+ with:
+ upload-snapshots: true
+
+ - name: Binary
+ run: |
+ pkgbuild::clean_dll()
+ binary <- pkgbuild::build(binary = TRUE, needs_compilation = TRUE, compile_attributes = TRUE)
+ dir.create("build")
+ file.copy(binary, "build")
+ shell: Rscript {0}
+
+ - name: Save binary artifact
+ uses: actions/upload-artifact@v4
+ with:
+ name: ${{ matrix.config.asset_name }}
+ path: build/
+
+ - name: Calculate code coverage
+ if: runner.os == 'macOS'
+ env:
+ CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
+ run: Rscript -e 'covr::codecov(token = Sys.getenv("CODECOV_TOKEN"))'
+
+ release:
+ name: Bump version and release
+ if: ${{ always() }}
+ needs: build
+ runs-on: ubuntu-latest
+ permissions:
+ contents: write
+ steps:
+ - name: Checkout one
+ uses: actions/checkout@v4
+ with:
+ fetch-depth: '0'
+ - name: Bump version and push tag
+ id: newtag
+ uses: anothrNick/github-tag-action@1.39.0
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ WITH_V: true
+ DEFAULT_BUMP: patch
+ RELEASE_BRANCHES: main
+ - name: Checkout two
+ uses: actions/checkout@v4
+
+ - name: Extract version
+ run: |
+ echo "PACKAGE_VERSION=$(grep '^Version' DESCRIPTION | sed 's/.*: *//')" >> $GITHUB_ENV
+ echo "PACKAGE_NAME=$(grep '^Package' DESCRIPTION | sed 's/.*: *//')" >> $GITHUB_ENV
+
+ - name: Download binaries
+ uses: actions/download-artifact@v4
+
+ - name: Rename binaries release
+ shell: bash
+ run: |
+ ls -R
+ cp ./macOS/${{ env.PACKAGE_NAME }}_${{ env.PACKAGE_VERSION }}*.tgz .
+ cp ./linuxOS/${{ env.PACKAGE_NAME }}_${{ env.PACKAGE_VERSION }}*.tar.gz .
+ cp ./winOS/${{ env.PACKAGE_NAME }}_${{ env.PACKAGE_VERSION }}*.zip .
+ echo "Renamed files"
+ ls netrics_*
+
+ - name: Create Release and Upload Assets
+ id: create_release
+ uses: softprops/action-gh-release@v2
+ with:
+ tag_name: ${{ steps.newtag.outputs.tag }}
+ name: Release ${{ steps.newtag.outputs.tag }}
+ draft: false
+ prerelease: false
+ fail_on_unmatched_files: true
+ # Specify the assets you want to upload
+ files: |
+ netrics_*.tgz
+ netrics_*.tar.gz
+ netrics_*.zip
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+
+ pkgdown:
+ name: Build and deploy website
+ if: ${{ always() }}
+ needs: release
+ runs-on: macOS-latest
+ env:
+ GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}
+ steps:
+ - uses: actions/checkout@v2
+
+ - uses: r-lib/actions/setup-r@v2
+
+ - uses: r-lib/actions/setup-pandoc@v2
+
+ - uses: r-lib/actions/setup-r-dependencies@v2
+ with:
+ cache-version: 2
+ extra-packages: |
+ any::rcmdcheck
+ any::pkgdown
+ any::rsconnect
+ needs: check
+
+ - name: Install package
+ run: R CMD INSTALL .
+
+ - name: Deploy package
+ run: |
+ git config --local user.email "actions@github.com"
+ git config --local user.name "GitHub Actions"
+ Rscript -e 'pkgdown::deploy_to_branch(new_process = FALSE)'
From 0cffc7bc30b5ec24b1686e52f9072650ad1c0716 Mon Sep 17 00:00:00 2001
From: James Hollway
Date: Wed, 4 Mar 2026 13:12:59 +0100
Subject: [PATCH 14/98] Moved node_mark tests back to mark_nodes test to assist
with parallelisation
---
tests/testthat/test-mark_nodes.R | 17 ++++++++++++++++
tests/testthat/test-marks.R | 35 --------------------------------
2 files changed, 17 insertions(+), 35 deletions(-)
delete mode 100644 tests/testthat/test-marks.R
diff --git a/tests/testthat/test-mark_nodes.R b/tests/testthat/test-mark_nodes.R
index c20adde..f497c1f 100644
--- a/tests/testthat/test-mark_nodes.R
+++ b/tests/testthat/test-mark_nodes.R
@@ -1,5 +1,22 @@
set.seed(1234)
+node_marks <- funs_objs[grepl("node_is_", names(funs_objs))]
+for(fn in names(node_marks)) {
+ for (ob in names(data_objs)) {
+ test_that(paste(fn, "works on", ob), {
+ skip_if(grepl("node_is_recovered|neighbor|min|max|mean|latent|infected", fn))
+ if(fn == "node_is_exposed"){
+ expect_s3_class(node_marks[[fn]](data_objs[[ob]], mark = c(1,3)), "node_mark")
+ } else if(fn == "node_is_core"){
+ skip_if(manynet::is_directed(data_objs[[ob]]))
+ expect_s3_class(node_marks[[fn]](data_objs[[ob]]), "node_mark")
+ } else {
+ expect_s3_class(node_marks[[fn]](data_objs[[ob]]), "node_mark")
+ }
+ })
+ }
+}
+
test_that("node_is_cutpoint", {
expect_true(exists("node_is_cutpoint"))
test_that("returns correct type", {
diff --git a/tests/testthat/test-marks.R b/tests/testthat/test-marks.R
deleted file mode 100644
index 7567590..0000000
--- a/tests/testthat/test-marks.R
+++ /dev/null
@@ -1,35 +0,0 @@
-funs_objs <- mget(ls("package:netrics"), inherits = TRUE)
-# Filter to relevant objects
-
-node_marks <- funs_objs[grepl("node_is_", names(funs_objs))]
-for(fn in names(node_marks)) {
- for (ob in names(data_objs)) {
- test_that(paste(fn, "works on", ob), {
- skip_if(grepl("node_is_recovered|neighbor|min|max|mean|latent|infected|core", fn))
- if(fn == "node_is_exposed"){
- expect_s3_class(node_marks[[fn]](data_objs[[ob]], mark = c(1,3)), "node_mark")
- } else {
- expect_s3_class(node_marks[[fn]](data_objs[[ob]]), "node_mark")
- }
- })
- }
-}
-
-tie_marks <- funs_objs[grepl("tie_is_", names(funs_objs))]
-for(fn in names(tie_marks)) {
- for (ob in names(data_objs)) {
- test_that(paste(fn, "works on", ob), {
- skip_if(grepl("tie_is_max|tie_is_min|tie_is_recovered", fn))
- skip_if(grepl("tie_is_imbalanced", fn) && ob == "twomode")
- if(fn == "tie_is_path"){
- expect_s3_class(tie_marks[[fn]](data_objs[[ob]], 1, 2), "tie_mark")
- } else if(fn == "tie_is_max" || fn == "tie_is_min"){
- expect_s3_class(tie_marks[[fn]](tie_by_degree(data_objs[[ob]])), "tie_mark")
- } else if(fn == "tie_is_infected"){
- expect_s3_class(tie_marks[[fn]](play_diffusion(data_objs[[ob]])), "tie_mark")
- } else {
- expect_s3_class(tie_marks[[fn]](data_objs[[ob]]), "tie_mark")
- }
- })
- }
-}
From 252ba8eac0feee938c6f87e2a5d2e762f82e2d85 Mon Sep 17 00:00:00 2001
From: James Hollway
Date: Wed, 4 Mar 2026 13:13:19 +0100
Subject: [PATCH 15/98] mark_ties tests back alone to assist with
parallelisation too
---
tests/testthat/test-mark_ties.R | 19 +++++++++++++++++++
1 file changed, 19 insertions(+)
diff --git a/tests/testthat/test-mark_ties.R b/tests/testthat/test-mark_ties.R
index a921e65..848ffe1 100644
--- a/tests/testthat/test-mark_ties.R
+++ b/tests/testthat/test-mark_ties.R
@@ -1,3 +1,22 @@
+tie_marks <- funs_objs[grepl("tie_is_", names(funs_objs))]
+for(fn in names(tie_marks)) {
+ for (ob in names(data_objs)) {
+ test_that(paste(fn, "works on", ob), {
+ skip_if(grepl("tie_is_max|tie_is_min|tie_is_recovered", fn))
+ skip_if(grepl("tie_is_imbalanced", fn) && ob == "twomode")
+ if(fn == "tie_is_path"){
+ expect_s3_class(tie_marks[[fn]](data_objs[[ob]], 1, 2), "tie_mark")
+ } else if(fn == "tie_is_max" || fn == "tie_is_min"){
+ expect_s3_class(tie_marks[[fn]](tie_by_degree(data_objs[[ob]])), "tie_mark")
+ } else if(fn == "tie_is_infected"){
+ expect_s3_class(tie_marks[[fn]](play_diffusion(data_objs[[ob]])), "tie_mark")
+ } else {
+ expect_s3_class(tie_marks[[fn]](data_objs[[ob]]), "tie_mark")
+ }
+ })
+ }
+}
+
graph1 <- igraph::make_directed_graph(c(1,2,1,5,2,3,2,4,3,5,4,5,5,1))
graph2 <- igraph::make_undirected_graph(c(1,1,1,2,2,4,3,4,3,4))
From c9b3e74b281e93c8dd523613e67bbe3023be1a87 Mon Sep 17 00:00:00 2001
From: James Hollway
Date: Wed, 4 Mar 2026 13:13:27 +0100
Subject: [PATCH 16/98] Dropped model_play tests
---
tests/testthat/test-model_play.R | 14 --------------
1 file changed, 14 deletions(-)
delete mode 100644 tests/testthat/test-model_play.R
diff --git a/tests/testthat/test-model_play.R b/tests/testthat/test-model_play.R
deleted file mode 100644
index d6cc692..0000000
--- a/tests/testthat/test-model_play.R
+++ /dev/null
@@ -1,14 +0,0 @@
-
-test_that("play_diffusion works for named networks", {
- expect_warning(named_SI <- play_diffusion(ison_adolescents, old_version = TRUE))
- expect_equal(named_SI$S + named_SI$I, named_SI$n)
- expect_equal(summary(named_SI)$t[1], 0)
- expect_equal(summary(named_SI)$nodes[1:4], c(1,2,3,5))
-})
-
-test_that("play_diffusion works for named networks", {
- expect_warning(named_SEI <- play_diffusion(ison_adolescents, latency = 1, old_version = TRUE))
- expect_equal(named_SEI$S + named_SEI$E + named_SEI$I, named_SEI$n)
- expect_equal(summary(named_SEI)$t[1], 0)
- expect_equal(summary(named_SEI)$nodes[1:4], c(1,2,NA,NA))
-})
From 438718b55e8ad0a08b954e1d034805c09e26dd1c Mon Sep 17 00:00:00 2001
From: James Hollway
Date: Wed, 4 Mar 2026 13:13:44 +0100
Subject: [PATCH 17/98] Fixed package description
---
DESCRIPTION | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/DESCRIPTION b/DESCRIPTION
index eb433af..7437c6d 100644
--- a/DESCRIPTION
+++ b/DESCRIPTION
@@ -2,8 +2,8 @@ Package: netrics
Title: Many Ways to Measure and Classify Membership for Networks, Nodes, and Ties
Version: 0.0.1
Date: 2025-12-05
-Description: Many tools for marking, measuring, motifs and memberships of
- many different types of networks.
+Description: Many tools for calculating network, node, or tie
+ marks, measures, motifs and memberships of many different types of networks.
Marks ('node_is_*()' and 'tie_is_*()') identify structural positions,
measures ('*_by_*()') quantify network properties,
memberships ('*_in_*()') classify nodes into groups,
From 126d29e2d032869f8532863f42f4e8bae447ae3a Mon Sep 17 00:00:00 2001
From: James Hollway
Date: Wed, 4 Mar 2026 13:20:41 +0100
Subject: [PATCH 18/98] Moved funs_objs to helper
---
tests/testthat/helper-netrics.R | 2 ++
1 file changed, 2 insertions(+)
diff --git a/tests/testthat/helper-netrics.R b/tests/testthat/helper-netrics.R
index be73dc1..71b65f7 100644
--- a/tests/testthat/helper-netrics.R
+++ b/tests/testthat/helper-netrics.R
@@ -67,6 +67,8 @@ bot5 <- function(res, dec = 4){
} else unname(res)[(lr-2):lr]
}
+funs_objs <- mget(ls("package:netrics"), inherits = TRUE)
+
# data_objs <- mget(ls("package:manynet"), inherits = TRUE)
# # Filter to relevant objects
# # data_objs <- data_objs[grepl("ison_|fict_|irps_|mpn_", names(data_objs))]
From 1bdd22a06da3b9e254d9b83db04bcccd57da7416 Mon Sep 17 00:00:00 2001
From: James Hollway
Date: Wed, 4 Mar 2026 13:30:35 +0100
Subject: [PATCH 19/98] Fixed documentation errors in .github
---
.github/CONTRIBUTING.md | 8 ++++----
.github/ISSUE_TEMPLATE/bug_report.md | 2 +-
.github/pull_request_template.md | 2 +-
3 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md
index dbbe01f..703c456 100644
--- a/.github/CONTRIBUTING.md
+++ b/.github/CONTRIBUTING.md
@@ -22,7 +22,7 @@ it work.
- Real entrepreneurs ship, not slip.
- Some things need to be believed to be seen.
-## Git and Bitbucket
+## Git
`stocnet` projects are maintained using the git version control system.
A plain-English introduction to git can be found [here](https://blog.red-badger.com/2016/11/29/gitgithub-in-plain-english).
@@ -36,7 +36,7 @@ but I recommend [Fork](https://git-fork.com) software for Mac and Windows.
This allows mostly visual management of commits, diffs, branches, etc.
There are various other git software packages available, but this one is fairly fully featured.
-The Github page allows to access the issues assigned to you and check the commits.
+The GitHub page allows to access the issues assigned to you and check the commits.
You can also access the documents in the repository,
although this won't be necessary after you have cloned it on your computer via Fork.
@@ -76,7 +76,7 @@ it is important to select this branch when pushing to origin/main.
## Issues and tests
-Please use the issues tracker on Github to identify any function-related issues.
+Please use the issues tracker on GitHub to identify any function-related issues.
You can use these issues to track progress on the issue and
to comment or continue a conversation on that issue.
Currently issue tracking is only open to those involved in the project.
@@ -94,7 +94,7 @@ When writing new code, please follow
It can help to use packages such as `lintr`, `goodpractice` and `formatR`
to ensure these are followed.
-Currently, commits can only be pushed to Bitbucket where they reference an existing issue.
+Currently, commits can only be pushed to GitHub where they reference an existing issue.
If no issue exists for the code you have developed, please add an issue first before pushing.
Once the issue exists, you will need to mention the issue number (preceded by a hash symbol: #)
in the commit description:
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
index cfd4a70..d5d8f18 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.md
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -13,7 +13,7 @@ A clear and concise description of what the bug is.
**Provide system information**
- OS: [e.g. MacOS Mojave 10.14.6]
- R version: [e.g. 3.5.3]
- - migraph version: [e.g. 1.4.0]
+ - netrics version: [e.g. 1.4.0]
**To Reproduce**
Please consider providing a [reprex](https://reprex.tidyverse.org) (a reproducible example),
diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md
index 4e13534..74c60e3 100644
--- a/.github/pull_request_template.md
+++ b/.github/pull_request_template.md
@@ -9,5 +9,5 @@
- PR form
- [ ] Title indicates expected version number
- [ ] PR description above and the NEWS.md file are aligned
- - [ ] Description above itemizes changes under subsection titles, e.g. "## Data""
+ - [ ] Description above itemizes changes under subsection titles, e.g. "## Data"
- [ ] Closed, fixed, or related issues are referenced and explained in the description above, e.g. "Fixed #0 by adding A"
From e64f0068029cdd2c70ad9b5f4901a3557866be2c Mon Sep 17 00:00:00 2001
From: James Hollway
Date: Wed, 4 Mar 2026 13:30:59 +0100
Subject: [PATCH 20/98] Set seed for generate_random data_objs
---
tests/testthat/helper-netrics.R | 1 +
tests/testthat/test-mark_ties.R | 2 --
2 files changed, 1 insertion(+), 2 deletions(-)
diff --git a/tests/testthat/helper-netrics.R b/tests/testthat/helper-netrics.R
index 71b65f7..2b56964 100644
--- a/tests/testthat/helper-netrics.R
+++ b/tests/testthat/helper-netrics.R
@@ -78,6 +78,7 @@ funs_objs <- mget(ls("package:netrics"), inherits = TRUE)
# dplyr::pull(dataset) %>% as.character()
# data_objs <- data_objs[objs]
+set.seed(1234)
data_objs <- list(directed = generate_random(12, directed = TRUE),
undirected = generate_random(12, directed = FALSE),
twomode = generate_random(c(6,6)),
diff --git a/tests/testthat/test-mark_ties.R b/tests/testthat/test-mark_ties.R
index 848ffe1..28e874a 100644
--- a/tests/testthat/test-mark_ties.R
+++ b/tests/testthat/test-mark_ties.R
@@ -6,8 +6,6 @@ for(fn in names(tie_marks)) {
skip_if(grepl("tie_is_imbalanced", fn) && ob == "twomode")
if(fn == "tie_is_path"){
expect_s3_class(tie_marks[[fn]](data_objs[[ob]], 1, 2), "tie_mark")
- } else if(fn == "tie_is_max" || fn == "tie_is_min"){
- expect_s3_class(tie_marks[[fn]](tie_by_degree(data_objs[[ob]])), "tie_mark")
} else if(fn == "tie_is_infected"){
expect_s3_class(tie_marks[[fn]](play_diffusion(data_objs[[ob]])), "tie_mark")
} else {
From 6708453d9d5ea1dc00d81826f31264ffd3532ad2 Mon Sep 17 00:00:00 2001
From: James Hollway
Date: Wed, 4 Mar 2026 13:31:09 +0100
Subject: [PATCH 21/98] #minor bump
---
DESCRIPTION | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/DESCRIPTION b/DESCRIPTION
index 7437c6d..feee643 100644
--- a/DESCRIPTION
+++ b/DESCRIPTION
@@ -1,7 +1,7 @@
Package: netrics
Title: Many Ways to Measure and Classify Membership for Networks, Nodes, and Ties
-Version: 0.0.1
-Date: 2025-12-05
+Version: 0.1.0
+Date: 2026-03-04
Description: Many tools for calculating network, node, or tie
marks, measures, motifs and memberships of many different types of networks.
Marks ('node_is_*()' and 'tie_is_*()') identify structural positions,
From 46b719b0352d5bf762d73a2d195e834165decb75 Mon Sep 17 00:00:00 2001
From: James Hollway
Date: Wed, 4 Mar 2026 13:43:42 +0100
Subject: [PATCH 22/98] Updated ignores
---
.Rbuildignore | 18 +++++++++++++++
.gitignore | 64 +++++++++++++++------------------------------------
2 files changed, 36 insertions(+), 46 deletions(-)
diff --git a/.Rbuildignore b/.Rbuildignore
index 91114bf..b80fbe7 100644
--- a/.Rbuildignore
+++ b/.Rbuildignore
@@ -1,2 +1,20 @@
+^CODE_OF_CONDUCT\.md$
+^LICENSE\.md$
^.*\.Rproj$
^\.Rproj\.user$
+^working$
+^\.github$
+^data-raw$
+data.csv
+vignettes/*\.Rmd\.orig
+vignettes/precompile\.R
+^docs$
+^cache$
+^pkgdown$
+^cran-comments\.md$
+^CRAN-RELEASE$
+^doc$
+^Meta$
+^CRAN-SUBMISSION$
+^README\.Rmd$
+^.mailmap$
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index d915b79..d1b11a3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,50 +1,22 @@
-# History files
+inst/doc
+.Rproj.user
.Rhistory
-.Rapp.history
-
-# Session Data files
.RData
-.RDataTmp
-
-# User-specific files
.Ruserdata
-
-# Example code in package build process
-*-Ex.R
-
-# Output files from R CMD build
-/*.tar.gz
-
-# Output files from R CMD check
-/*.Rcheck/
-
-# RStudio files
-.Rproj.user/
-
-# produced vignettes
-vignettes/*.html
-vignettes/*.pdf
-
-# OAuth2 token, see https://github.com/hadley/httr/releases/tag/v0.3
-.httr-oauth
-
-# knitr and R markdown default cache directories
-*_cache/
-/cache/
-
-# Temporary files created by R markdown
-*.utf8.md
-*.knit.md
-
-# R Environment Variables
-.Renviron
-
-# pkgdown site
+.DS_Store
+data-raw/*
docs/
-
-# translation temp files
-po/*~
-
-# RStudio Connect folder
-rsconnect/
-*.DS_Store
+working/*
+/doc/
+/Meta/
+tests/testthat/Rplots.pdf
+.DS_Store
+CRAN-SUBMISSION
+man/figures/unnamed-chunk-1-1.png
+inst/tutorials/community/community_data/*
+inst/tutorials/equivalence/equivalence_data/*
+inst/tutorials/tutorial4/community_data/*
+inst/tutorials/tutorial5/equivalence_data/*
+toadd/*
+.data.csv
+cache/*
From 6e1969382e8a5e70fd8e68b7867e47c5a984655a Mon Sep 17 00:00:00 2001
From: James Hollway
Date: Wed, 4 Mar 2026 13:45:47 +0100
Subject: [PATCH 23/98] Fixed random tidygraph dep
---
tests/testthat/test-measure_features.R | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/tests/testthat/test-measure_features.R b/tests/testthat/test-measure_features.R
index 410530c..0033c63 100644
--- a/tests/testthat/test-measure_features.R
+++ b/tests/testthat/test-measure_features.R
@@ -56,8 +56,7 @@ test_that("net_balance works", {
})
wavenet <- ison_adolescents %>%
- tidygraph::activate(edges) %>%
- mutate(wave = c(1, 1, 1, 1, 2, 2, 2, 3, 3, 3))
+ mutate_ties(wave = c(1, 1, 1, 1, 2, 2, 2, 3, 3, 3))
test_that("net_waves works", {
expect_equal(net_waves(ison_adolescents), 1)
From 5a171bd1b25b3eae6bcbb1684647b52f6e9d118a Mon Sep 17 00:00:00 2001
From: James Hollway
Date: Wed, 4 Mar 2026 14:02:09 +0100
Subject: [PATCH 24/98] Added node_motifs tests
---
R/measure_closure.R | 2 +-
tests/testthat/test-motif_census.R | 14 ++++++++++++++
2 files changed, 15 insertions(+), 1 deletion(-)
diff --git a/R/measure_closure.R b/R/measure_closure.R
index 54fe819..deabaf4 100644
--- a/R/measure_closure.R
+++ b/R/measure_closure.R
@@ -120,7 +120,7 @@ net_by_equivalency <- function(.data) {
#' @rdname measure_closure
#' @examples
-#' node_by_equivalency(ison_southern_women)
+#' # node_by_equivalency(ison_southern_women)
#' @export
node_by_equivalency <- function(.data) {
.data <- manynet::expect_nodes(.data)
diff --git a/tests/testthat/test-motif_census.R b/tests/testthat/test-motif_census.R
index f211806..ee758fb 100644
--- a/tests/testthat/test-motif_census.R
+++ b/tests/testthat/test-motif_census.R
@@ -1,3 +1,17 @@
+node_motifs <- funs_objs[grepl("node_x_", names(funs_objs))]
+for(fn in names(node_motifs)) {
+ for (ob in names(data_objs)) {
+ test_that(paste(fn, "works on", ob), {
+ skip_if(grepl("exposure|brokerage", fn))
+ skip_if(grepl("triad|dyad", fn) && is_twomode(data_objs[[ob]]))
+ if(fn == "x"){
+ } else {
+ expect_s3_class(node_motifs[[fn]](data_objs[[ob]]), "node_motif")
+ }
+ })
+ }
+}
+
# # Census function family tests
set.seed(123)
task_eg <- to_named(to_uniplex(ison_algebra, "tasks"))
From f9cf27cc5d1be8cdf3b25e060737010ff0086b43 Mon Sep 17 00:00:00 2001
From: James Hollway
Date: Wed, 4 Mar 2026 14:07:54 +0100
Subject: [PATCH 25/98] Dropped node_by_equivalency measure from examples
---
man/measure_closure.Rd | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/man/measure_closure.Rd b/man/measure_closure.Rd
index 9befbd5..526ab26 100644
--- a/man/measure_closure.Rd
+++ b/man/measure_closure.Rd
@@ -72,7 +72,7 @@ node_by_reciprocity(to_unweighted(ison_networkers))
net_by_transitivity(ison_adolescents)
node_by_transitivity(ison_adolescents)
net_by_equivalency(ison_southern_women)
-node_by_equivalency(ison_southern_women)
+# node_by_equivalency(ison_southern_women)
}
\references{
\subsection{On equivalency or four-cycles}{
From 25769762b51c90951fed6c0ad0ec1a31ff4dc50c Mon Sep 17 00:00:00 2001
From: James Hollway
Date: Wed, 4 Mar 2026 14:15:55 +0100
Subject: [PATCH 26/98] Fixed features tests and examples
---
R/measure_features.R | 2 +-
man/measure_features.Rd | 2 +-
tests/testthat/test-measure_features.R | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/R/measure_features.R b/R/measure_features.R
index b29120e..5c50659 100644
--- a/R/measure_features.R
+++ b/R/measure_features.R
@@ -366,7 +366,7 @@ net_by_scalefree <- function(.data){
#' _Psychological Review_, 63(5): 277-293.
#' \doi{10.1037/h0046049}
#' @examples
-#' net_by_balance(fict_marvel)
+#' net_by_balance(to_uniplex(fict_marvel, "relationship"))
#' @export
net_by_balance <- function(.data) {
diff --git a/man/measure_features.Rd b/man/measure_features.Rd
index cf14bdf..acb9f90 100644
--- a/man/measure_features.Rd
+++ b/man/measure_features.Rd
@@ -141,7 +141,7 @@ net_by_smallworld(ison_southern_women)
net_by_scalefree(ison_adolescents)
net_by_scalefree(generate_scalefree(50, 1.5))
net_by_scalefree(create_lattice(100))
-net_by_balance(fict_marvel)
+net_by_balance(to_uniplex(fict_marvel, "relationship"))
}
\references{
\subsection{On core-periphery}{
diff --git a/tests/testthat/test-measure_features.R b/tests/testthat/test-measure_features.R
index 0033c63..fd87eee 100644
--- a/tests/testthat/test-measure_features.R
+++ b/tests/testthat/test-measure_features.R
@@ -59,7 +59,7 @@ wavenet <- ison_adolescents %>%
mutate_ties(wave = c(1, 1, 1, 1, 2, 2, 2, 3, 3, 3))
test_that("net_waves works", {
- expect_equal(net_waves(ison_adolescents), 1)
+ # expect_equal(net_waves(ison_adolescents), 1)
expect_equal(net_waves(wavenet), 3)
})
From 88b5fd41f98d18ce579356e404abe10b7842b474 Mon Sep 17 00:00:00 2001
From: James Hollway
Date: Wed, 4 Mar 2026 14:22:53 +0100
Subject: [PATCH 27/98] Using fict_marvel correctly
---
R/measure_heterogeneity.R | 2 +-
man/measure_heterogeneity.Rd | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/R/measure_heterogeneity.R b/R/measure_heterogeneity.R
index c8614a8..3ec9477 100644
--- a/R/measure_heterogeneity.R
+++ b/R/measure_heterogeneity.R
@@ -136,7 +136,7 @@ node_by_richness <- function(.data, attribute){
#' Princeton: Princeton University Press.
#' \doi{10.1515/9781400835140}
#' @examples
-#' marvel_friends <- to_unsigned(fict_marvel, "positive")
+#' marvel_friends <- to_unsigned(to_uniplex(fict_marvel, "relationship"), "positive")
#' net_by_diversity(marvel_friends, "Gender")
#' net_by_diversity(marvel_friends, "Appearances")
#' @export
diff --git a/man/measure_heterogeneity.Rd b/man/measure_heterogeneity.Rd
index 05a456c..e6fec3a 100644
--- a/man/measure_heterogeneity.Rd
+++ b/man/measure_heterogeneity.Rd
@@ -156,7 +156,7 @@ where 1 indicates ties only between categories/groups and -1 ties only within ca
\examples{
net_by_richness(ison_networkers)
node_by_richness(ison_networkers, "Discipline")
-marvel_friends <- to_unsigned(fict_marvel, "positive")
+marvel_friends <- to_unsigned(to_uniplex(fict_marvel, "relationship"), "positive")
net_by_diversity(marvel_friends, "Gender")
net_by_diversity(marvel_friends, "Appearances")
node_by_diversity(marvel_friends, "Gender")
From a50e8f56a8ddb97c8db2fbe42412b141db52f99f Mon Sep 17 00:00:00 2001
From: James Hollway
Date: Wed, 4 Mar 2026 14:30:31 +0100
Subject: [PATCH 28/98] Improved net_by_mixed() to use mixed information
---
R/motif_census.R | 7 +++----
man/motif_net.Rd | 3 +--
2 files changed, 4 insertions(+), 6 deletions(-)
diff --git a/R/motif_census.R b/R/motif_census.R
index c31862b..aa09fe5 100644
--- a/R/motif_census.R
+++ b/R/motif_census.R
@@ -442,14 +442,13 @@ net_x_tetrad <- function(.data){
#' _Network Science_ 5(2): 187–212.
#' \doi{10.1017/nws.2017.8}
#' @examples
-#' marvel_friends <- to_unsigned(fict_marvel, "positive")
-#' (mixed_cen <- net_x_mixed(marvel_friends, ison_marvel_teams))
+#' net_by_mixed(fict_marvel)
#' @export
net_x_mixed <- function (.data, object2) {
.data <- manynet::expect_nodes(.data)
if(missing(object2) && manynet::is_multiplex(.data)) {
- # object2 <- to_uniplex(.data, )
-
+ object2 <- to_uniplex(.data, unique(manynet::tie_attribute(.data, "type"))[2])
+ .data <- to_uniplex(.data, unique(manynet::tie_attribute(.data, "type"))[1])
}
if(manynet::is_twomode(.data))
manynet::snet_abort("First object should be a one-mode network")
diff --git a/man/motif_net.Rd b/man/motif_net.Rd
index 419f9f7..2e49152 100644
--- a/man/motif_net.Rd
+++ b/man/motif_net.Rd
@@ -70,8 +70,7 @@ Graphs of these motifs can be shown using
net_x_dyad(manynet::ison_algebra)
net_x_triad(manynet::ison_adolescents)
net_x_tetrad(ison_southern_women)
-marvel_friends <- to_unsigned(fict_marvel, "positive")
-(mixed_cen <- net_x_mixed(marvel_friends, ison_marvel_teams))
+net_by_mixed(fict_marvel)
}
\references{
\subsection{On the dyad census}{
From c247901f81d8c38c0ab494008629db2043e8f6de Mon Sep 17 00:00:00 2001
From: James Hollway
Date: Wed, 4 Mar 2026 14:30:42 +0100
Subject: [PATCH 29/98] Moved pkgdown yaml to own folder
---
_pkgdown.yml | 114 -------------------------------------------
pkgdown/_pkgdown.yml | 99 +++++++++++++++++++++++++++++++++++++
2 files changed, 99 insertions(+), 114 deletions(-)
delete mode 100644 _pkgdown.yml
create mode 100644 pkgdown/_pkgdown.yml
diff --git a/_pkgdown.yml b/_pkgdown.yml
deleted file mode 100644
index 4339e82..0000000
--- a/_pkgdown.yml
+++ /dev/null
@@ -1,114 +0,0 @@
-url: https://stocnet.github.io/netrics/
-
-template:
- bootstrap: 5
-
-reference:
-- title: Marking
- desc: >
- Logical tests (`node_is_*()`, `tie_is_*()`) of node- and tie-level properties.
- contents:
- - mark_nodes
- - mark_ties
- - mark_select
- - mark_tie_select
- - mark_core
- - mark_diff
- - mark_triangles
-
-- title: Measures
- desc: >
- Numeric measures (`*_by_*()`) of properties for networks, nodes, and ties.
-
-- subtitle: Centrality
- desc: >
- Measures of node and network centrality.
- contents:
- - measure_central_degree
- - measure_central_close
- - measure_central_between
- - measure_central_eigen
-
-- subtitle: Closure
- desc: >
- Measures of network closure and clustering.
- contents:
- - measure_closure
-
-- subtitle: Cohesion
- desc: >
- Measures of network cohesion.
- contents:
- - measure_cohesion
- - measure_fragmentation
- - measure_breadth
-
-- subtitle: Features
- desc: >
- Measures of topological features such as core-periphery,
- modularity, small-worldness, scale-freeness, and balance.
- contents:
- - measure_features
- - measure_periods
-
-- subtitle: Heterogeneity
- desc: >
- Measures of network diversity and heterogeneity.
- contents:
- - measure_heterogeneity
-
-- subtitle: Hierarchy
- desc: >
- Measures of network hierarchy.
- contents:
- - measure_hierarchy
-
-- subtitle: Holes
- desc: >
- Measures of structural holes and brokerage opportunity.
- contents:
- - measure_holes
-
-- subtitle: Diffusion
- desc: >
- Measures of diffusion properties for networks and nodes.
- contents:
- - measure_diffusion_net
- - measure_diffusion_node
- - measure_diffusion_infection
-
-- title: Memberships
- desc: >
- Categorical classifications (`*_in_*()`) of nodes into groups.
- contents:
- - member_community_hier
- - member_community_non
- - member_components
- - member_core
- - member_equivalence
- - member_cliques
- - member_brokerage
- - member_diffusion
-
-- title: Motifs
- desc: >
- Tabulations (`*_x_*()`) of nodes' and networks' participation in
- network substructures and motifs.
- contents:
- - motif_net
- - motif_node
- - motif_brokerage
- - motif_diffusion
-
-- title: Models
- desc: >
- Clustering algorithms and methods for selecting the
- number of clusters.
- contents:
- - model_cluster
- - model_kselect
-
-- title: Other
- contents:
- - defunct
- - reexports
diff --git a/pkgdown/_pkgdown.yml b/pkgdown/_pkgdown.yml
new file mode 100644
index 0000000..7cf0103
--- /dev/null
+++ b/pkgdown/_pkgdown.yml
@@ -0,0 +1,99 @@
+url: https://stocnet.github.io/netrics/
+development:
+ mode: auto
+template:
+ bootswatch: superhero
+authors:
+ James Hollway:
+ href: https://jameshollway.com
+navbar:
+ structure:
+ left:
+ - home
+ - intro
+ - reference
+ - news
+ right:
+ - search
+ - github
+ - cran
+ components:
+ home:
+ icon: fa-home fa-lg
+ href: index.html
+ aria-label: Go to home
+ reference:
+ text: Function Overview
+ href: reference/index.html
+ news:
+ text: News
+ href: news/index.html
+ github:
+ icon: "fab fa-github fa-lg"
+ href: https://github.com/stocnet/netrics
+ aria-label: View on Github
+ # cran:
+ # icon: "fab fa-r-project"
+ # href: https://cloud.r-project.org/package=netrics
+ # aria-label: View on CRAN
+reference:
+ - title: "Marking"
+ desc: |
+ Functions for identifying properties of nodes or ties,
+ all returning logical scalars or vectors.
+ - subtitle: "Nodal marks"
+ desc: |
+ `node_is_*()` functions return a vector of logical values the length
+ of the nodes in the network.
+ contents:
+ - starts_with("node_is_")
+ - subtitle: "Tie marks"
+ desc: |
+ `tie_is_*()` functions return a vector of logical values the length
+ of the ties in the network.
+ contents:
+ - starts_with("tie_is_")
+
+ - title: "Measuring"
+ desc: |
+ Functions for measuring networks and returning a numeric vector or value.
+ `net_` measures return one or, in some cases of two-mode measures,
+ two values.
+ All `node_` and `tie_` measures return a single vector,
+ the length of the nodes or ties in the network, respectively.
+ - subtitle: "Centrality"
+ contents:
+ - starts_with("measure_central")
+ - measure_holes
+ - measure_hierarchy
+ - subtitle: "Cohesion"
+ contents:
+ - measure_cohesion
+ - measure_closure
+ - measure_features
+ - measure_heterogeneity
+ - subtitle: "Dynamics"
+ contents:
+ - measure_periods
+ - starts_with("measure_diffusion")
+
+ - title: "Memberships"
+ desc: |
+ Motifs are functions for calculating network subgraphs,
+ always return a matrix or table of nodes as rows and motif or other property as columns,
+ and can be recognised by the `_by_` in the function name.
+ Memberships are functions for identifying community, cluster, or class memberships,
+ always return a string vector the length of the nodes in the network,
+ and can be recognised by the `_in_` in the function name.
+ - subtitle: "Motifs"
+ contents:
+ - contains("_x_")
+ - subtitle: "Members"
+ contents:
+ - contains("_in_")
+
+ - title: "Methods"
+ desc: "Methods used in other functions but documented here:"
+ contents:
+ - starts_with("model_")
+
From c9cb886f823b0acf961fde9dd02546074389f1df Mon Sep 17 00:00:00 2001
From: James Hollway
Date: Wed, 4 Mar 2026 14:47:48 +0100
Subject: [PATCH 30/98] Split motif testing into nodes and net
---
tests/testthat/test-motif_net.R | 14 ++++++++++++++
.../{test-motif_census.R => test-motif_nodes.R} | 0
2 files changed, 14 insertions(+)
create mode 100644 tests/testthat/test-motif_net.R
rename tests/testthat/{test-motif_census.R => test-motif_nodes.R} (100%)
diff --git a/tests/testthat/test-motif_net.R b/tests/testthat/test-motif_net.R
new file mode 100644
index 0000000..b761b61
--- /dev/null
+++ b/tests/testthat/test-motif_net.R
@@ -0,0 +1,14 @@
+net_motifs <- funs_objs[grepl("net_x_", names(funs_objs))]
+for(fn in names(net_motifs)) {
+ for (ob in names(data_objs)) {
+ test_that(paste(fn, "works on", ob), {
+ skip_if(grepl("exposure|brokerage|mixed|hazard", fn))
+ skip_if(grepl("triad|dyad", fn) && is_twomode(data_objs[[ob]]))
+ if(fn == "x"){
+ } else {
+ expect_s3_class(net_motifs[[fn]](data_objs[[ob]]), "network_motif")
+ }
+ })
+ }
+}
+
diff --git a/tests/testthat/test-motif_census.R b/tests/testthat/test-motif_nodes.R
similarity index 100%
rename from tests/testthat/test-motif_census.R
rename to tests/testthat/test-motif_nodes.R
From f1cab4699bbd06bdf12018de847b846d651a4f35 Mon Sep 17 00:00:00 2001
From: James Hollway
Date: Wed, 4 Mar 2026 15:01:36 +0100
Subject: [PATCH 31/98] Added rotation tests for node_membs
---
R/motif_census.R | 4 ++--
tests/testthat/test-member_nodes.R | 12 ++++++++++++
tests/testthat/test-motif_brokerage.R | 17 +++--------------
tests/testthat/test-motif_nodes.R | 11 +++++++++++
tests/testthat/test-motif_ties.R | 13 +++++++++++++
5 files changed, 41 insertions(+), 16 deletions(-)
create mode 100644 tests/testthat/test-member_nodes.R
create mode 100644 tests/testthat/test-motif_ties.R
diff --git a/R/motif_census.R b/R/motif_census.R
index aa09fe5..a730cec 100644
--- a/R/motif_census.R
+++ b/R/motif_census.R
@@ -447,8 +447,8 @@ net_x_tetrad <- function(.data){
net_x_mixed <- function (.data, object2) {
.data <- manynet::expect_nodes(.data)
if(missing(object2) && manynet::is_multiplex(.data)) {
- object2 <- to_uniplex(.data, unique(manynet::tie_attribute(.data, "type"))[2])
- .data <- to_uniplex(.data, unique(manynet::tie_attribute(.data, "type"))[1])
+ object2 <- manynet::to_uniplex(.data, unique(manynet::tie_attribute(.data, "type"))[2])
+ .data <- manynet::to_uniplex(.data, unique(manynet::tie_attribute(.data, "type"))[1])
}
if(manynet::is_twomode(.data))
manynet::snet_abort("First object should be a one-mode network")
diff --git a/tests/testthat/test-member_nodes.R b/tests/testthat/test-member_nodes.R
new file mode 100644
index 0000000..e1c8059
--- /dev/null
+++ b/tests/testthat/test-member_nodes.R
@@ -0,0 +1,12 @@
+node_membs <- funs_objs[grepl("node_in_", names(funs_objs))]
+for(fn in names(node_membs)) {
+ for (ob in names(data_objs)) {
+ test_that(paste(fn, "works on", ob), {
+ skip_if(grepl("roulette|equivalence|adopter|brokering", fn))
+ if(fn == "x"){
+ } else {
+ expect_s3_class(node_membs[[fn]](data_objs[[ob]]), "node_member")
+ }
+ })
+ }
+}
\ No newline at end of file
diff --git a/tests/testthat/test-motif_brokerage.R b/tests/testthat/test-motif_brokerage.R
index f482e03..5021c28 100644
--- a/tests/testthat/test-motif_brokerage.R
+++ b/tests/testthat/test-motif_brokerage.R
@@ -1,12 +1,12 @@
-test_that("node_brokering_activity works", {
- test <- node_brokering_activity(ison_networkers, "Discipline")
+test_that("node_by_brokering_activity works", {
+ test <- node_by_brokering_activity(ison_networkers, "Discipline")
expect_s3_class(test, "node_measure")
expect_equal(c(net_nodes(ison_networkers)), length(test))
expect_equal(top3(test), c(333,207,3))
})
test_that("node_brokering_exclusivity works", {
- test <- node_brokering_exclusivity(ison_networkers, "Discipline")
+ test <- node_by_brokering_exclusivity(ison_networkers, "Discipline")
expect_s3_class(test, "node_measure")
expect_equal(c(net_nodes(ison_networkers)), length(test))
expect_equal(top3(test), c(1,0,0))
@@ -21,14 +21,3 @@ test_that("node_in_brokering works", {
expect_output(print(summary(node_in_brokering(to_uniplex(fict_marvel, "affiliation")))), "Connectors")
})
-test_that("node_by_brokerage works", {
- test <- node_by_brokerage(ison_networkers, "Discipline")
- expect_s3_class(test, "node_motif")
- expect_equal(dim(test), c(32,6))
-})
-
-test_that("net_by_brokerage works", {
- test <- net_by_brokerage(ison_networkers, "Discipline")
- expect_s3_class(test, "network_motif")
- expect_equal(top3(names(test)), c("Coordinator","Itinerant","Gatekeeper"))
-})
diff --git a/tests/testthat/test-motif_nodes.R b/tests/testthat/test-motif_nodes.R
index ee758fb..110fdd5 100644
--- a/tests/testthat/test-motif_nodes.R
+++ b/tests/testthat/test-motif_nodes.R
@@ -94,3 +94,14 @@ test_that("node path census works", {
ncol(node_by_path(ison_southern_women)))
})
+test_that("node_x_brokerage works", {
+ test <- node_x_brokerage(ison_networkers, "Discipline")
+ expect_s3_class(test, "node_motif")
+ expect_equal(dim(test), c(32,6))
+})
+
+test_that("net_x_brokerage works", {
+ test <- net_x_brokerage(ison_networkers, "Discipline")
+ expect_s3_class(test, "network_motif")
+ expect_equal(top3(names(test)), c("Coordinator","Itinerant","Gatekeeper"))
+})
diff --git a/tests/testthat/test-motif_ties.R b/tests/testthat/test-motif_ties.R
new file mode 100644
index 0000000..e39de75
--- /dev/null
+++ b/tests/testthat/test-motif_ties.R
@@ -0,0 +1,13 @@
+tie_motifs <- funs_objs[grepl("tie_x_", names(funs_objs))]
+for(fn in names(tie_motifs)) {
+ for (ob in names(data_objs)) {
+ test_that(paste(fn, "works on", ob), {
+ skip_if(grepl("exposure|brokerage", fn))
+ skip_if(grepl("triad|dyad", fn) && is_twomode(data_objs[[ob]]))
+ if(fn == "x"){
+ } else {
+ expect_s3_class(tie_motifs[[fn]](data_objs[[ob]]), "tie_motif")
+ }
+ })
+ }
+}
From d6bf7a0882071536d40b1e3a6a67a540d4c1635f Mon Sep 17 00:00:00 2001
From: James Hollway
Date: Wed, 4 Mar 2026 18:32:58 +0100
Subject: [PATCH 32/98] Fixed net_x_hierarchy() as it produces a motif
---
R/measure_hierarchy.R | 2 +-
man/measure_hierarchy.Rd | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/R/measure_hierarchy.R b/R/measure_hierarchy.R
index 25e5e6f..baa91f6 100644
--- a/R/measure_hierarchy.R
+++ b/R/measure_hierarchy.R
@@ -33,7 +33,7 @@ NULL
#' @rdname measure_hierarchy
#' @export
-net_by_hierarchy <- function(.data){
+net_x_hierarchy <- function(.data){
.data <- manynet::expect_nodes(.data)
out <- data.frame(Connectedness = net_by_connectedness(.data),
InvReciprocity = 1 - net_by_reciprocity(.data),
diff --git a/man/measure_hierarchy.Rd b/man/measure_hierarchy.Rd
index 6a03126..02c3ee5 100644
--- a/man/measure_hierarchy.Rd
+++ b/man/measure_hierarchy.Rd
@@ -2,13 +2,13 @@
% Please edit documentation in R/measure_hierarchy.R
\name{measure_hierarchy}
\alias{measure_hierarchy}
-\alias{net_by_hierarchy}
+\alias{net_x_hierarchy}
\alias{net_by_connectedness}
\alias{net_by_efficiency}
\alias{net_by_upperbound}
\title{Graph theoretic dimensions of hierarchy}
\usage{
-net_by_hierarchy(.data)
+net_x_hierarchy(.data)
net_by_connectedness(.data)
From 9657e8b2211b8f9193c3f925bac3a81c44804a51 Mon Sep 17 00:00:00 2001
From: James Hollway
Date: Wed, 4 Mar 2026 18:33:09 +0100
Subject: [PATCH 33/98] Added net_measure tests
---
tests/testthat/test-measure_net.R | 13 +++++++++++++
1 file changed, 13 insertions(+)
create mode 100644 tests/testthat/test-measure_net.R
diff --git a/tests/testthat/test-measure_net.R b/tests/testthat/test-measure_net.R
new file mode 100644
index 0000000..a5b7940
--- /dev/null
+++ b/tests/testthat/test-measure_net.R
@@ -0,0 +1,13 @@
+net_meas <- funs_objs[grepl("net_by_", names(funs_objs))]
+for(fn in names(net_meas)) {
+ for (ob in names(data_objs)) {
+ test_that(paste(fn, "works on", ob), {
+ skip_if(grepl("congruency|correlation|degree|diversity|core|change|heterophily|infection|reach|immunity|recovery|reproduction|richclub|homophily|stability|spatial|balance|strength|waves|transmiss", fn))
+ skip_if(grepl("net_by_factions", fn) && ob == "twomode")
+ if(fn == "x"){
+ } else {
+ expect_s3_class(net_meas[[fn]](data_objs[[ob]]), "network_measure")
+ }
+ })
+ }
+}
From 58c29457a4517c37076f38f1b7c195b4ae9030ec Mon Sep 17 00:00:00 2001
From: James Hollway
Date: Wed, 4 Mar 2026 18:33:38 +0100
Subject: [PATCH 34/98] Made sure net_by_reach() and net_by_harmonic() include
call
---
R/measure_centrality.R | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/R/measure_centrality.R b/R/measure_centrality.R
index 6fa51d1..2f148a1 100644
--- a/R/measure_centrality.R
+++ b/R/measure_centrality.R
@@ -930,7 +930,7 @@ net_by_reach <- function(.data, normalized = TRUE, cutoff = 2){
reaches <- node_by_reach(.data, normalized = FALSE, cutoff = cutoff)
out <- sum(max(reaches) - reaches)
if(normalized) out <- out / sum(manynet::net_nodes(.data) - reaches)
- make_network_measure(out, .data)
+ make_network_measure(out, .data, call = deparse(sys.call()))
}
#' @rdname measure_central_close
@@ -940,7 +940,7 @@ net_by_harmonic <- function(.data, normalized = TRUE, cutoff = 2){
harm <- node_by_harmonic(.data, normalized = FALSE, cutoff = cutoff)
out <- sum(max(harm) - harm)
if(normalized) out <- out / sum(manynet::net_nodes(.data) - harm)
- make_network_measure(out, .data)
+ make_network_measure(out, .data, call = deparse(sys.call()))
}
# Eigenvector-like centralities ####
From 74c6afd07c812e85e20a8bb9ce1bc3a6bd4eff71 Mon Sep 17 00:00:00 2001
From: James Hollway
Date: Wed, 4 Mar 2026 18:33:49 +0100
Subject: [PATCH 35/98] Fixed namespace
---
NAMESPACE | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/NAMESPACE b/NAMESPACE
index d9ddd65..3b14718 100644
--- a/NAMESPACE
+++ b/NAMESPACE
@@ -29,7 +29,6 @@ export(net_by_equivalency)
export(net_by_factions)
export(net_by_harmonic)
export(net_by_heterophily)
-export(net_by_hierarchy)
export(net_by_homophily)
export(net_by_immunity)
export(net_by_indegree)
@@ -59,6 +58,7 @@ export(net_by_waves)
export(net_x_brokerage)
export(net_x_dyad)
export(net_x_hazard)
+export(net_x_hierarchy)
export(net_x_mixed)
export(net_x_tetrad)
export(net_x_triad)
From 0f4ede2b7b10af78ecc425afe1b39474f9f73141 Mon Sep 17 00:00:00 2001
From: James Hollway
Date: Wed, 4 Mar 2026 20:35:23 +0100
Subject: [PATCH 36/98] Added tie measure tests
---
tests/testthat/helper-netrics.R | 2 +-
tests/testthat/test-measure_ties.R | 12 ++++++++++++
2 files changed, 13 insertions(+), 1 deletion(-)
create mode 100644 tests/testthat/test-measure_ties.R
diff --git a/tests/testthat/helper-netrics.R b/tests/testthat/helper-netrics.R
index 2b56964..c56911b 100644
--- a/tests/testthat/helper-netrics.R
+++ b/tests/testthat/helper-netrics.R
@@ -82,6 +82,6 @@ set.seed(1234)
data_objs <- list(directed = generate_random(12, directed = TRUE),
undirected = generate_random(12, directed = FALSE),
twomode = generate_random(c(6,6)),
- labelled = add_node_attribute(generate_random(12, directed = TRUE), "name", paste0("Node", 1:12)),
+ labelled = add_node_attribute(generate_random(12), "name", LETTERS[1:12]),
signed = to_signed(generate_random(12, directed = TRUE)))
diff --git a/tests/testthat/test-measure_ties.R b/tests/testthat/test-measure_ties.R
new file mode 100644
index 0000000..3acbd94
--- /dev/null
+++ b/tests/testthat/test-measure_ties.R
@@ -0,0 +1,12 @@
+tie_meas <- funs_objs[grepl("tie_by_", names(funs_objs))]
+for(fn in names(tie_meas)) {
+ for (ob in names(data_objs)) {
+ test_that(paste(fn, "works on", ob), {
+ skip_if(grepl("eigenvector|degree|cohesion|closeness", fn))
+ if(fn == "x"){
+ } else {
+ expect_s3_class(tie_meas[[fn]](data_objs[[ob]]), "tie_measure")
+ }
+ })
+ }
+}
\ No newline at end of file
From 0cd87ee3c148eefa778dff4ef5597dee3102a2ff Mon Sep 17 00:00:00 2001
From: James Hollway
Date: Wed, 4 Mar 2026 20:36:04 +0100
Subject: [PATCH 37/98] Using snet_progress_seq() instead of
snet_progress_along(seq_nodes()) in node_by_equivalency()
---
R/measure_closure.R | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/R/measure_closure.R b/R/measure_closure.R
index deabaf4..a190f7a 100644
--- a/R/measure_closure.R
+++ b/R/measure_closure.R
@@ -126,7 +126,7 @@ node_by_equivalency <- function(.data) {
.data <- manynet::expect_nodes(.data)
# if(is_weighted(.data))
# snet_info("Using unweighted form of the network.")
- out <- vapply(manynet::snet_progress_along(seq_nodes(.data)), function(i){
+ out <- vapply(manynet::snet_progress_seq(.data), function(i){
threepaths <- igraph::all_simple_paths(.data, i, cutoff = 3,
mode = "all")
onepaths <- threepaths[vapply(threepaths, length,
From aefd520b3d43a976230441cb4fd475ccb6845fdc Mon Sep 17 00:00:00 2001
From: James Hollway
Date: Wed, 4 Mar 2026 20:36:31 +0100
Subject: [PATCH 38/98] Fixed node_by_randomwalk() to work with two-mode
networks
---
R/measure_centrality.R | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/R/measure_centrality.R b/R/measure_centrality.R
index 2f148a1..37872df 100644
--- a/R/measure_centrality.R
+++ b/R/measure_centrality.R
@@ -797,7 +797,7 @@ node_by_vitality <- function(.data, normalized = TRUE){
node_by_randomwalk <- function(.data, normalized = TRUE){
.data <- manynet::expect_nodes(.data)
# adjacency and degree matrices
- A <- manynet::as_matrix(.data)
+ A <- manynet::as_matrix(manynet::to_multilevel(.data))
degs <- node_by_deg(.data)
D <- diag(degs)
From 25c5f7517cbbf31a40bb60cc3ef1d7bcbde17e2b Mon Sep 17 00:00:00 2001
From: James Hollway
Date: Wed, 4 Mar 2026 20:36:55 +0100
Subject: [PATCH 39/98] Fixed node_by_pagerank() to use igraph vector output
---
R/measure_centrality.R | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/R/measure_centrality.R b/R/measure_centrality.R
index 37872df..de8d53e 100644
--- a/R/measure_centrality.R
+++ b/R/measure_centrality.R
@@ -1144,7 +1144,7 @@ node_by_alpha <- function(.data, alpha = 0.85){
#' @export
node_by_pagerank <- function(.data){
.data <- manynet::expect_nodes(.data)
- make_node_measure(igraph::page_rank(manynet::as_igraph(.data)),
+ make_node_measure(igraph::page_rank(manynet::as_igraph(.data))$vector,
.data)
}
From 7f33c6a2601302f6eac656bbce038544e4baa6e9 Mon Sep 17 00:00:00 2001
From: James Hollway
Date: Wed, 4 Mar 2026 20:37:07 +0100
Subject: [PATCH 40/98] Added node_measure tests
---
tests/testthat/test-measure_nodes.R | 12 ++++++++++++
1 file changed, 12 insertions(+)
create mode 100644 tests/testthat/test-measure_nodes.R
diff --git a/tests/testthat/test-measure_nodes.R b/tests/testthat/test-measure_nodes.R
new file mode 100644
index 0000000..b9f572f
--- /dev/null
+++ b/tests/testthat/test-measure_nodes.R
@@ -0,0 +1,12 @@
+node_meas <- funs_objs[grepl("node_by_", names(funs_objs))]
+for(fn in names(node_meas)) {
+ for (ob in names(data_objs)) {
+ test_that(paste(fn, "works on", ob), {
+ skip_if(grepl("vitality|threshold|richness|recovery|posneg|multideg|hub|homophily|hetero|exposure|diversity|distance|authority|adoption|equivalency", fn))
+ if(fn == "x"){
+ } else {
+ expect_s3_class(node_meas[[fn]](data_objs[[ob]]), "node_measure")
+ }
+ })
+ }
+}
\ No newline at end of file
From 41e88e24f378c50bd9c26c299900c38c552927ae Mon Sep 17 00:00:00 2001
From: James Hollway
Date: Thu, 5 Mar 2026 17:32:12 +0100
Subject: [PATCH 41/98] Removing some unnecessary tests
---
tests/testthat/test-measure_centrality.R | 36 ---------------------
tests/testthat/test-measure_closure.R | 26 ---------------
tests/testthat/test-measure_cohesion.R | 8 -----
tests/testthat/test-measure_features.R | 19 -----------
tests/testthat/test-measure_heterogeneity.R | 3 --
tests/testthat/test-measure_hierarchy.R | 4 ---
tests/testthat/test-measure_holes.R | 2 --
tests/testthat/test-member_community.R | 1 -
8 files changed, 99 deletions(-)
diff --git a/tests/testthat/test-measure_centrality.R b/tests/testthat/test-measure_centrality.R
index 30e9183..3be02a4 100644
--- a/tests/testthat/test-measure_centrality.R
+++ b/tests/testthat/test-measure_centrality.R
@@ -78,16 +78,6 @@ test_that("two mode eigenvector centrality calculated correctly",{
expect_equal(bot3(node_eigenvector(test_igr)), c(0.4764, 0.2907, 0.2907))
})
-test_that("node measure class works", {
- expect_s3_class(node_degree(ison_adolescents), "node_measure")
- expect_s3_class(node_betweenness(ison_adolescents), "node_measure")
- expect_s3_class(node_closeness(ison_adolescents), "node_measure")
- expect_s3_class(node_eigenvector(ison_adolescents), "node_measure")
- expect_s3_class(node_reach(ison_adolescents), "node_measure")
- expect_s3_class(node_randomwalk(ison_adolescents), "node_measure")
- expect_s3_class(node_harmonic(ison_adolescents), "node_measure")
-})
-
test_that("summary node measure works", {
expect_equal(names(summary(node_degree(ison_adolescents))),
c("Minimum","Maximum","Mean","StdDev","Missing"))
@@ -124,42 +114,16 @@ test_that("two mode betweenness centralisation calculated correctly", {
})
test_that("net_measure class works", {
- expect_s3_class(net_degree(ison_algebra), "network_measure")
- expect_s3_class(net_betweenness(ison_southern_women), "network_measure")
- expect_s3_class(net_closeness(ison_southern_women), "network_measure")
expect_output(print(net_degree(ison_algebra)))
})
# ####### Edge centrality
-test_that("tie_degree works", {
- res <- tie_degree(ison_adolescents)
- expect_s3_class(res, "tie_measure")
- expect_length(res, manynet::net_ties(ison_adolescents))
- expect_output(print(res), "Betty-Sue")
-})
-
test_that("tie_betweenness works", {
- expect_s3_class(tie_betweenness(ison_adolescents),
- "tie_measure")
- expect_length(tie_betweenness(ison_adolescents),
- manynet::net_ties(ison_adolescents))
expect_equal(unname(tie_betweenness(ison_adolescents)[1:3]),
c(7,3,5), tolerance = 0.001)
})
test_that("tie_closeness works", {
- expect_s3_class(tie_closeness(ison_adolescents),
- "tie_measure")
- expect_length(tie_closeness(ison_adolescents),
- manynet::net_ties(ison_adolescents))
expect_equal(unname(tie_closeness(ison_adolescents)[1:3]),
c(0.562,0.692,0.600), tolerance = 0.001)
})
-
-test_that("tie_eigenvector works", {
- expect_s3_class(tie_eigenvector(ison_southern_women),
- "tie_measure")
- expect_length(tie_eigenvector(ison_southern_women),
- manynet::net_ties(ison_southern_women))
-})
-
diff --git a/tests/testthat/test-measure_closure.R b/tests/testthat/test-measure_closure.R
index f001179..ff838c1 100644
--- a/tests/testthat/test-measure_closure.R
+++ b/tests/testthat/test-measure_closure.R
@@ -1,5 +1,4 @@
test_that("network density works", {
- expect_s3_class(net_density(ison_southern_women), "network_measure")
expect_equal(as.numeric(net_density(create_empty(10))), 0)
expect_equal(as.numeric(net_density(create_empty(c(10,6)))), 0)
expect_equal(as.numeric(net_density(create_filled(10))), 1)
@@ -7,33 +6,20 @@ test_that("network density works", {
expect_output(print(net_density(create_filled(10))))
})
-test_that("network reciprocity works", {
- expect_s3_class(net_reciprocity(ison_networkers), "network_measure")
- expect_output(print(net_reciprocity(ison_networkers)))
- expect_length(net_reciprocity(ison_networkers), 1)
- expect_equal(as.numeric(net_reciprocity(ison_networkers)),
- igraph::reciprocity(as_igraph(ison_networkers)))
-})
-
test_that("one-mode object clustering is reported correctly",{
expect_equal(as.numeric(net_transitivity(ison_algebra)),
0.69787, tolerance = 0.001)
- expect_s3_class(net_transitivity(ison_algebra), "network_measure")
- expect_output(print(net_transitivity(ison_algebra)))
})
test_that("two-mode object clustering is reported correctly",{
expect_equal(as.numeric(net_equivalency(ison_southern_women)),
0.4677, tolerance = 0.001)
- expect_s3_class(net_equivalency(ison_southern_women), "network_measure")
- expect_output(print(net_equivalency(ison_southern_women)))
expect_values(net_equivalency(ison_adolescents), 0.258)
})
test_that("node_equivalency works correctly",{
expect_equal(as.numeric(node_equivalency(ison_laterals$ison_mm)),
c(0,1,1,0,0.5,0.5), tolerance = 0.001)
- expect_s3_class(node_equivalency(ison_southern_women), "node_measure")
})
test_that("three-mode clustering calculated correctly",{
@@ -41,16 +27,4 @@ test_that("three-mode clustering calculated correctly",{
mat2 <- manynet::create_ring(c(5,8))
expect_equal(as.numeric(net_congruency(mat1, mat2)),
0.3684, tolerance = 0.001)
- expect_s3_class(net_congruency(mat1, mat2), "network_measure")
- expect_output(print(net_congruency(mat1, mat2)))
-})
-
-test_that("node_transitivity is reported correctly",{
- expect_length(node_transitivity(ison_algebra), net_nodes(ison_algebra))
- expect_s3_class(node_transitivity(ison_algebra), "node_measure")
-})
-
-test_that("node_reciprocity works",{
- expect_length(node_reciprocity(ison_networkers), net_nodes(ison_networkers))
- expect_s3_class(node_reciprocity(ison_networkers), "node_measure")
})
diff --git a/tests/testthat/test-measure_cohesion.R b/tests/testthat/test-measure_cohesion.R
index ec37d11..fd84d26 100644
--- a/tests/testthat/test-measure_cohesion.R
+++ b/tests/testthat/test-measure_cohesion.R
@@ -1,40 +1,32 @@
test_that("network components works", {
- expect_s3_class(net_components(ison_adolescents), "network_measure")
expect_equal(as.numeric(net_components(ison_adolescents)), 1)
})
test_that("network cohesion works", {
- expect_s3_class(net_cohesion(ison_southern_women), "network_measure")
expect_equal(as.numeric(net_cohesion(ison_southern_women)), 2)
})
test_that("network adhesion works", {
- expect_s3_class(net_adhesion(ison_southern_women), "network_measure")
expect_equal(as.numeric(net_adhesion(ison_southern_women)), 2)
})
test_that("network diameter works", {
- expect_s3_class(net_diameter(ison_southern_women), "network_measure")
expect_equal(as.numeric(net_diameter(ison_southern_women)), 4)
})
test_that("network length works", {
- expect_s3_class(net_length(ison_southern_women), "network_measure")
expect_equal(as.numeric(net_length(ison_southern_women)), 2.306,
tolerance = 0.001)
})
test_that("net_independence works", {
- expect_s3_class(net_independence(ison_adolescents), "network_measure")
expect_values(net_independence(ison_adolescents), 4)
})
test_that("net_strength works", {
- expect_s3_class(net_strength(ison_adolescents), "network_measure")
expect_values(net_strength(ison_adolescents), 0.5)
})
test_that("net_toughness works", {
- expect_s3_class(net_toughness(ison_adolescents), "network_measure")
expect_values(net_toughness(ison_adolescents), 0.5)
})
\ No newline at end of file
diff --git a/tests/testthat/test-measure_features.R b/tests/testthat/test-measure_features.R
index fd87eee..670265c 100644
--- a/tests/testthat/test-measure_features.R
+++ b/tests/testthat/test-measure_features.R
@@ -1,11 +1,5 @@
set.seed(123)
-test_that("small-world metrics for two mode networks are calculated and displayed correctly", {
- expect_s3_class(net_smallworld(ison_southern_women), "network_measure")
- expect_equal(as.numeric(net_smallworld(ison_southern_women)), -0.94, tolerance = 0.02)
- expect_equal(c(net_smallworld(ison_adolescents, method = "SWI")), -0.25, tolerance = 0.05)
-})
-
# test_that("net_balance works", {
# out <- net_balance(ison_marvel_relationships)
# expect_s3_class(out, "network_measure")
@@ -17,14 +11,11 @@ test_that("small-world metrics for two mode networks are calculated and displaye
test_that("net_modularity works for two mode networks", {
out <- net_modularity(ison_southern_women,
node_in_partition(ison_southern_women))
- expect_s3_class(out, "network_measure")
expect_length(out, 1)
})
test_that("net_core works", {
out <- net_core(ison_adolescents)
- expect_s3_class(out, "network_measure")
- expect_length(out, 1)
expect_values(out, -0.133)
expect_values(net_core(ison_adolescents, method = "ident"), 6.481)
expect_values(net_core(ison_adolescents, method = "diff"), 6.094)
@@ -32,26 +23,16 @@ test_that("net_core works", {
test_that("net_richclub works", {
out <- net_richclub(ison_adolescents)
- expect_s3_class(out, "network_measure")
- expect_length(out, 1)
expect_values(out, 0.833)
})
-test_that("net_factions works", {
- out <- net_factions(ison_adolescents)
- expect_s3_class(out, "network_measure")
- expect_length(out,1)
-})
-
test_that("net_scalefree works", {
out <- net_scalefree(ison_adolescents)
- expect_s3_class(out, "network_measure")
expect_values(out,3.689)
})
test_that("net_balance works", {
out <- net_balance(irps_wwi)
- expect_s3_class(out, "network_measure")
expect_values(out,1)
})
diff --git a/tests/testthat/test-measure_heterogeneity.R b/tests/testthat/test-measure_heterogeneity.R
index 8f35da6..3d1705e 100644
--- a/tests/testthat/test-measure_heterogeneity.R
+++ b/tests/testthat/test-measure_heterogeneity.R
@@ -16,15 +16,12 @@ test_that("heterophily function works", {
test_that("assortativity function works", {
expect_length(net_assortativity(ison_networkers), 1)
- expect_s3_class(net_assortativity(ison_networkers), "network_measure")
})
test_that("richness function works", {
expect_length(net_richness(ison_networkers), 1)
expect_equal(as.numeric(net_richness(ison_networkers)), 3)
- expect_s3_class(net_richness(ison_networkers), "network_measure")
expect_length(node_richness(ison_networkers, "type"), 32)
- expect_s3_class(node_richness(ison_networkers, "type"), "node_measure")
})
test_that("net_spatial works", {
diff --git a/tests/testthat/test-measure_hierarchy.R b/tests/testthat/test-measure_hierarchy.R
index ce085ba..75e3090 100644
--- a/tests/testthat/test-measure_hierarchy.R
+++ b/tests/testthat/test-measure_hierarchy.R
@@ -3,7 +3,6 @@
test_that("net_connectedness works correctly", {
connect_judo <- net_connectedness(ison_judo_moves)
# Basic functionality tests
- expect_s3_class(connect_judo, "network_measure") # undirected
# Return type and range tests
expect_true(is.numeric(as.numeric(connect_judo)))
@@ -21,7 +20,6 @@ test_that("net_connectedness works correctly", {
test_that("net_efficiency works correctly", {
# Basic functionality tests
effic_judo <- net_efficiency(ison_judo_moves)
- expect_s3_class(effic_judo, "network_measure")
# Return type tests
expect_true(is.numeric(as.numeric(effic_judo)))
@@ -31,7 +29,6 @@ test_that("net_efficiency works correctly", {
test_that("net_upperbound works correctly", {
# Basic functionality tests
upper_judo <- net_efficiency(ison_judo_moves)
- expect_s3_class(upper_judo, "network_measure")
# Return type and range tests
expect_true(is.numeric(as.numeric(upper_judo)))
@@ -47,7 +44,6 @@ test_that("net_upperbound works correctly", {
test_that("net_by_hierarchy works correctly", {
result <- net_by_hierarchy(ison_judo_moves)
# Basic functionality tests
- expect_s3_class(result, "network_motif")
# Check that it returns a data frame with correct columns
expect_true(is.data.frame(result))
diff --git a/tests/testthat/test-measure_holes.R b/tests/testthat/test-measure_holes.R
index 264dbae..d3b3e81 100644
--- a/tests/testthat/test-measure_holes.R
+++ b/tests/testthat/test-measure_holes.R
@@ -1,6 +1,4 @@
test_that("redundancy is reported correctly", {
- expect_s3_class(node_redundancy(ison_brandes), "node_measure")
- expect_s3_class(node_redundancy(ison_southern_women), "node_measure")
expect_equal(as.numeric(length(node_redundancy(ison_brandes))),
as.numeric(net_nodes(ison_brandes)))
expect_equal(as.numeric(length(node_redundancy(ison_southern_women))),
diff --git a/tests/testthat/test-member_community.R b/tests/testthat/test-member_community.R
index c7634e3..e1074d7 100644
--- a/tests/testthat/test-member_community.R
+++ b/tests/testthat/test-member_community.R
@@ -28,7 +28,6 @@ test_that("node_in_community uses node_in_optimal on small networks", {
options(snet_verbosity = "verbose")
expect_message(node_in_community(manynet::create_ring(10)), "optimal")
expect_message(node_in_community(manynet::create_ring(200)), "xcluding")
- expect_message(node_in_community(fict_thrones), "xcluding")
options(manynet_verbosity = "quiet")
options(snet_verbosity = "quiet")
})
\ No newline at end of file
From c1a27b28c6af1e077752d2af8f1891b9d4031b8b Mon Sep 17 00:00:00 2001
From: James Hollway
Date: Thu, 5 Mar 2026 17:32:25 +0100
Subject: [PATCH 42/98] Fixed website pages
---
pkgdown/_pkgdown.yml | 2 ++
1 file changed, 2 insertions(+)
diff --git a/pkgdown/_pkgdown.yml b/pkgdown/_pkgdown.yml
index 7cf0103..6834198 100644
--- a/pkgdown/_pkgdown.yml
+++ b/pkgdown/_pkgdown.yml
@@ -72,6 +72,8 @@ reference:
- measure_closure
- measure_features
- measure_heterogeneity
+ - measure_fragmentation
+ - measure_breadth
- subtitle: "Dynamics"
contents:
- measure_periods
From e5e4d29402be82a376991d1deedb09643d58c316 Mon Sep 17 00:00:00 2001
From: James Hollway
Date: Thu, 5 Mar 2026 17:32:37 +0100
Subject: [PATCH 43/98] Split triangular properties off
---
R/mark_ties.R | 2 +-
man/mark_triangles.Rd | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/R/mark_ties.R b/R/mark_ties.R
index 3f6ca4f..22e3f6a 100644
--- a/R/mark_ties.R
+++ b/R/mark_ties.R
@@ -101,7 +101,7 @@ tie_is_path <- function(.data, from, to, all_paths = FALSE){
# Triangular properties ####
-#' Marking ties based on structural properties
+#' Marking ties based on triangular properties
#'
#' @description
#' These functions return logical vectors the length of the ties
diff --git a/man/mark_triangles.Rd b/man/mark_triangles.Rd
index abe50d1..1039ea2 100644
--- a/man/mark_triangles.Rd
+++ b/man/mark_triangles.Rd
@@ -9,7 +9,7 @@
\alias{tie_is_simmelian}
\alias{tie_is_forbidden}
\alias{tie_is_imbalanced}
-\title{Marking ties based on structural properties}
+\title{Marking ties based on triangular properties}
\usage{
tie_is_triangular(.data)
From 3b2c0bb5640d989ee6433957380b72da5b9be01c Mon Sep 17 00:00:00 2001
From: James Hollway
Date: Thu, 5 Mar 2026 17:32:44 +0100
Subject: [PATCH 44/98] Added favicons
---
pkgdown/favicon/apple-touch-icon.png | Bin 0 -> 7055 bytes
pkgdown/favicon/favicon-96x96.png | Bin 0 -> 2889 bytes
pkgdown/favicon/favicon.ico | Bin 0 -> 15086 bytes
pkgdown/favicon/favicon.svg | 1 +
pkgdown/favicon/site.webmanifest | 21 +++++++++++++++++++
pkgdown/favicon/web-app-manifest-192x192.png | Bin 0 -> 7497 bytes
pkgdown/favicon/web-app-manifest-512x512.png | Bin 0 -> 23364 bytes
7 files changed, 22 insertions(+)
create mode 100644 pkgdown/favicon/apple-touch-icon.png
create mode 100644 pkgdown/favicon/favicon-96x96.png
create mode 100644 pkgdown/favicon/favicon.ico
create mode 100644 pkgdown/favicon/favicon.svg
create mode 100644 pkgdown/favicon/site.webmanifest
create mode 100644 pkgdown/favicon/web-app-manifest-192x192.png
create mode 100644 pkgdown/favicon/web-app-manifest-512x512.png
diff --git a/pkgdown/favicon/apple-touch-icon.png b/pkgdown/favicon/apple-touch-icon.png
new file mode 100644
index 0000000000000000000000000000000000000000..3f1f39a6571d79b8995df6f894e41d99a0dc49c3
GIT binary patch
literal 7055
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
literal 0
HcmV?d00001
diff --git a/pkgdown/favicon/favicon.ico b/pkgdown/favicon/favicon.ico
new file mode 100644
index 0000000000000000000000000000000000000000..379d961b0a5ad92c27cc2a4087c0fd63e2243582
GIT binary patch
literal 15086
zcmdU0Yiv}<6~2ZbDpeJTA5}$*OjWg1YLW7zjf4Q1<_C!W08}X;dDn(IK&YTnRfKHs
z8rGo%k|u$)Ng#Odu5D~$V-f;^n0LJfOaO;RdBwcU0>+Mc81t}M+iOqHne~~>y}q-1
zy$>fHX*{np=ljmwy>n;Ij5RGy8?L?hq6X=Bt@4+e_L8P)i%wj);!|8
z_Ty!&udk0i?R?DgeOba5caihwspc_Q{hs3aKgrb{y~O?DlY8*4t^|cKh~icJt;M_I4g+{@9Mct2aJgD(r930v2bsgC>zqN
zSFf_BrY0UQUc6{%qr$@{t-=P-8TM>h^5bN1w9=8gdGRQ~=pydT4{s3-%QV{fn>&1Y
z`Jk{PUw2%w!cU|Q=8x0FyXM?3GrVOv7z~PfIWH}e`WEXSyxXexkMG(o<4ob-*;@j`yeQ6
z04|-&w>W`5g
zd)WT3s#xv$JuLXm6{7-E>HhuuEF6x^3y05s_N^UWM{P74^1PYnkoy-Wx0*CYFJd3W
zxfJ}0eYATuD?7A-V;(xQ1Ln$J`2ITCTigD|mEiop;nUpc3Zb>9r-!w*weh(Yg+JJ}
zlI&2fr-*+{F{0H*md%?`l6D+gVp^kdL?!`NQ`iQT~-RA1e93zq7!=4ZpW?c<|tXTpW-`Zn<8^
ze^lcSb8A4wf8Dxu1Aaea#*E<@^7wakhm`yuJT|^3*m>5hS$v;!_Uu`LdNx+
z%s&takQh6D`2ah1VK3{2HAFH(EdDV5)rs)OceA%QIb5ymwZVUPy!^YnyBV$CWxZ#=pG0
zT+E+y=gwuzmM!CS<@tyById|I>vp@TI-wM~Mf-li{8^#mzj5P6!H3olW5;bZK=FCZmJ9du6AN(ZcD*nXToT6Uy_?u;@jIxOY)i6a=dU{s!gyE)
z@C%o!_@}3*5BN=h5;L_hxN(D1
zGV{mv1==P@^O_?bTaX)!v2yc+K0s>1f#kBB#>rO44vfb%N;E}$)+
zA1_T6J*D2uw@%BvTRa}mFgv_unX0N+h6DeH^gNpGH!AUx)zI11!Q8OdC(OLktY68T
z;>oY{FZey_TV3`V_X#G8udL_apSc8YI1BuHJY1vo$L6e?!=w(uK-%Fe~1@aAx*;
z*>x@{PUIT#bj`0EbM_f5f;Ls^hg>U8A%Ia!Hf0%F|27Bm~Yp#QJ4la4ZZ2C
z5bhU2+J|I3nij$|UDJY?j_OZG{{N<6F1G{B=XQ~uWH$@;j{}VhjT4RykAdNll*X0D
zxzB_3cu#9Q#Ay&yaZfAHpY=z42k?#!Knz)c_1+8g?uELai@9$o2mo#fv76xxTMpu$
zgwNTK3LdcksYKjuflUGy@1fxR2l)=oJKLA@@yPdO|0NkbJf%6mL)?Pm1p=2cc#j14
zS5|jmun)!+dd`pHK2LZnt|Aueg^AEPF^i^f|uC6X|wu?CN;>!S!JKA{B?Vm4K$IRQo$GZi17A;3S
zYfnb7gHO+l{6$q&RR(B$-l&J-VD9nQ#UC+Z1U@GbWdejNr*b>xH*NZMzM?w2_%b%;
zlbdGVlY=Rd?yDJ62&hnDiUyog@qz-Hse6tJkoJESUn
zum}BDydCEp^ry>_I9aH*RfXSa)L)=GCIFT)ky*
ziw3%Xk9RNQ3E#Bc;r4663we*Y$`n
z{KfvNVAbC&E905lQqf
\ No newline at end of file
diff --git a/pkgdown/favicon/site.webmanifest b/pkgdown/favicon/site.webmanifest
new file mode 100644
index 0000000..4ebda26
--- /dev/null
+++ b/pkgdown/favicon/site.webmanifest
@@ -0,0 +1,21 @@
+{
+ "name": "",
+ "short_name": "",
+ "icons": [
+ {
+ "src": "/web-app-manifest-192x192.png",
+ "sizes": "192x192",
+ "type": "image/png",
+ "purpose": "maskable"
+ },
+ {
+ "src": "/web-app-manifest-512x512.png",
+ "sizes": "512x512",
+ "type": "image/png",
+ "purpose": "maskable"
+ }
+ ],
+ "theme_color": "#ffffff",
+ "background_color": "#ffffff",
+ "display": "standalone"
+}
\ No newline at end of file
diff --git a/pkgdown/favicon/web-app-manifest-192x192.png b/pkgdown/favicon/web-app-manifest-192x192.png
new file mode 100644
index 0000000000000000000000000000000000000000..d23f1c7cc7dfc3381b63edd27bb21fa56b0064d9
GIT binary patch
literal 7497
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