Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
58f9cee
net_diversity() no longer offers a clusters option, which can be obta…
jhollway Nov 6, 2025
d922111
Improved net_diversity() to allow using the Teachman's index instead …
jhollway Nov 6, 2025
70de9a3
Improved net_diversity() and node_diversity() to allow the use of var…
jhollway Nov 6, 2025
538bfd4
Improved net_diversity() and node_diversity() to correct the method w…
jhollway Nov 6, 2025
79c35ae
Improved vector printing to be more succinct and suggest `print_all()`
jhollway Nov 6, 2025
7c90f20
Added references for richness
jhollway Nov 6, 2025
7482ac6
Updated documentation
jhollway Nov 6, 2025
58eb04a
Added `net_homophily()` and `node_homophily()` for measuring homophil…
jhollway Nov 6, 2025
78940ac
Fixed sd namespace issue
jhollway Nov 7, 2025
6cfbff2
Fixed description of to_directed() and to_redirected() in data tutorial
jhollway Nov 12, 2025
ec14600
Fixed node_is_latent(), node_is_infected(), and node_is_recovered() t…
jhollway Nov 17, 2025
1bf0de0
Updated node_is_exposed() to work with changing data, now also accept…
jhollway Nov 17, 2025
eebc7d4
Fixed net_diversity() referencing "cv" internally instead of "variation"
jhollway Nov 17, 2025
942540c
Improved mnet printing to describe changing networks correctly
jhollway Nov 18, 2025
d06c0f1
Added delete_changes() for removing all changes from an mnet object
jhollway Nov 18, 2025
6100234
to_waves() now splits changing networks
jhollway Nov 18, 2025
1524147
Fixed as_diffusion() to not trim off final wave
jhollway Nov 18, 2025
9571cfb
Fixed is_longitudinal() to only test whether edges are in waves
jhollway Nov 18, 2025
8ef599c
net_infection_complete() example doesn't need recovery
jhollway Nov 18, 2025
dcda954
Fixed node_diversity() method assignment
jhollway Nov 18, 2025
1c0ef51
Improved to_waves() to handle both changing and longitudinal data
jhollway Nov 18, 2025
bd05a07
Fixed as_diffusion.mnet() to return correct E and R compartments
jhollway Nov 19, 2025
b0d86e3
#minor bump
jhollway Nov 19, 2025
5cf6535
Refactored geary
jhollway Nov 19, 2025
44508be
Update cran comments
jhollway Nov 19, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Package: manynet
Title: Many Ways to Make, Modify, Mark, and Measure Myriad Networks
Version: 1.6.7
Date: 2025-11-05
Version: 1.7.0
Date: 2025-11-19
Description: Many tools for making, modifying, marking, measuring,
and motifs and memberships of many different types of networks.
All functions operate with matrices, edge lists, and 'igraph', 'network', and 'tidygraph' objects,
Expand Down
3 changes: 3 additions & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,7 @@ export(create_star)
export(create_tree)
export(create_wheel)
export(create_windmill)
export(delete_changes)
export(delete_nodes)
export(delete_ties)
export(describe_changes)
Expand Down Expand Up @@ -446,6 +447,7 @@ export(net_factions)
export(net_harmonic)
export(net_hazard)
export(net_heterophily)
export(net_homophily)
export(net_immunity)
export(net_indegree)
export(net_independence)
Expand Down Expand Up @@ -571,6 +573,7 @@ export(node_fluid)
export(node_harmonic)
export(node_heterophily)
export(node_hierarchy)
export(node_homophily)
export(node_hub)
export(node_in_adopter)
export(node_in_automorphic)
Expand Down
36 changes: 36 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,39 @@
# manynet 1.7.0

## Making

- Improved vector printing to be more succinct and suggest `print_all()`
- Improved printing methods for changing mnet objects

## Modifying

- Added `delete_changes()` for deleting all changes from a changing network
- Improved `to_waves()` to work with networks that are changing, longitudinal, or both
- Fixed `as_diffusion()` to not trim off final wave in the report
- Fixed `as_diffusion()` to return correct E and R compartments for waning diffusion models

## Marking

- Improved `is_longitudinal()` to only check for tie waves and not nodal changes

## Measuring

- Improved `net_diversity()` and `node_diversity()`
- No longer offers a cluster option, which can be obtained using `over_membership()`
- Added more methods for calculating diversity, including Teachman's, coefficient of variation, and the Gini coefficient
- Improved `net_diversity()` and `node_diversity()` to use and declare methods appropriate for the vector of attributes
- Added `net_homophily()` and `node_homophily()` for measuring homophily according to different methods including
- Krackhardt's EI index of heterophily as well as its inverse as a measure of homophily
- Yule's Q as a further measure of homophily
- Geary's C as a measure of homophily for numeric attributes
- Updated documentation for richness, diversity, and homophily measures
- Fixed `node_is_latent()`, `node_is_infected()`, `node_is_recoverd()` to work with changing networks
- Improved `node_is_exposed()` to work with changing networks and now accepts a time argument

## Tutorials

- Fixed description of `to_directed()` and `to_redirected()` in the data tutorial

# manynet 1.6.7

## Making
Expand Down
6 changes: 3 additions & 3 deletions R/class_marks.R
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,9 @@ print_tblvec <- function(y, names, n){
body <- pillar::tbl_format_body(tibs, setup)[c(TRUE, FALSE, TRUE)]
if(setup$extra_cols_total > 0){
print(body)
cat(pillar::style_subtle(paste("# ... with",
cat(pillar::style_subtle(paste("# ... and",
setup$extra_cols_total,
"more values from this nodeset unprinted.",
"Use `print(..., n = Inf)` to print all values.")))
"more values from this nodeset.",
"Use `print_all(...)` to print all values.")))
} else print(body)
}
1 change: 1 addition & 0 deletions R/class_models.R
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ print.diff_model <- function(x, ..., verbose = FALSE){
if(!verbose){
x$n <- NULL
x$s <- NULL
x$S_new <- NULL
x$I_new <- NULL
x$E_new <- NULL
x$R_new <- NULL
Expand Down
5 changes: 3 additions & 2 deletions R/class_networks.R
Original file line number Diff line number Diff line change
Expand Up @@ -180,9 +180,10 @@ describe_ties <- function(x){
#' @export
describe_changes <- function(x){
if(is_longitudinal(x)){
paste(" over", max(tie_attribute(x, "wave")), "waves")
waves <- tie_attribute(x, "wave")
if(is.null(waves)) waves <- as_changelist(x)$time
paste(" over", max(waves), "waves")
} else if (is_dynamic(x)){

if("time" %in% net_tie_attributes(x)){
paste(" from", min(tie_attribute(x, "time"), na.rm = TRUE),
"to", max(tie_attribute(x, "time"), na.rm = TRUE))
Expand Down
30 changes: 16 additions & 14 deletions R/manip_as.R
Original file line number Diff line number Diff line change
Expand Up @@ -1001,24 +1001,26 @@ as_diffusion.diff_model <- function(.data, twomode = FALSE, events) {
#' @export
as_diffusion.mnet <- function(.data, twomode = FALSE, events) {
events <- as_changelist(.data)
nodes <- net_nodes(.data)
nodes <- c(net_nodes(.data))
sumchanges <- events %>% dplyr::group_by(time) %>%
dplyr::reframe(I_new = sum(value == "I"),
dplyr::reframe(S_new = sum(value == "S"),
E_new = sum(value == "E"),
I_new = sum(value == "I"),
R_new = sum(value == "R"))
report <- dplyr::tibble(time = seq_len(max(events$time)) - 1,
report <- dplyr::tibble(time = 0:max(events$time),
n = nodes) %>%
dplyr::left_join(sumchanges, by = dplyr::join_by(time))
report[is.na(report)] <- 0
report$R <- cumsum(report$R_new)
report$I <- cumsum(report$I_new) - report$R
report$E <- ifelse(report$E_new == 0 &
cumsum(report$E_new) == max(cumsum(report$E_new)),
report$E_new, cumsum(report$E_new))
report$E <- ifelse(report$R + report$I + report$E > report$n,
report$n - (report$R + report$I),
report$E)
report$S <- report$n - report$R - report$I - report$E

if(all(report$E_new == 0)){
report$S = report$n + cumsum(report$S_new - report$I_new)
report$E = rep(0, nrow(report))
} else {
report$S = report$n + cumsum(report$S_new - report$E_new) # susceptible decreases as they become exposed
report$E = cumsum(report$E_new) - cumsum(report$I_new) # exposed become infectious
}
report$I = cumsum(report$I_new) - cumsum(report$R_new) # infectious recover
report$R = cumsum(report$R_new) - cumsum(report$S_new) # recovered accumulate
report$s <- vapply(report$time, function(t){
twin <- dplyr::filter(events, events$time <= t)
infected <- dplyr::filter(twin, twin$value == "I")$node
Expand All @@ -1028,11 +1030,11 @@ as_diffusion.mnet <- function(.data, twomode = FALSE, events) {
expos[recovered] <- F
sum(expos)
}, numeric(1) )
if (any(report$R + report$I + report$E + report$S != report$n)) {
if (any((report$R + report$I + report$E + report$S) != report$n)) {
snet_abort("Oops, something is wrong")
}
report <- dplyr::select(report,
dplyr::any_of(c("time", "n", "S", "s", "E", "E_new",
dplyr::any_of(c("time", "n", "S", "s", "S_new", "E", "E_new",
"I", "I_new", "R", "R_new")))
# make_diff_model(events, report, .data)
class(report) <- c("diff_model", class(report))
Expand Down
6 changes: 6 additions & 0 deletions R/manip_nodes.R
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,12 @@ add_changes <- function(.data, changes){
.data %>% mutate_nodes(diffusion = "S")
}

#' @rdname manip_changes
#' @export
delete_changes <- function(.data){
igraph::delete_graph_attr(.data, "changes")
}

#' @rdname manip_changes
#' @export
mutate_changes <- function(.data, ...) UseMethod("mutate_changes")
Expand Down
97 changes: 81 additions & 16 deletions R/manip_split.R
Original file line number Diff line number Diff line change
Expand Up @@ -182,21 +182,86 @@ to_waves <- function(.data, attribute = "wave", panels = NULL,
#' @export
to_waves.tbl_graph <- function(.data, attribute = "wave", panels = NULL,
cumulative = FALSE) {
wp <- unique(tie_attribute(.data, attribute))
if(!is.null(panels))
wp <- intersect(panels, wp)
if(length(wp) > 1) {
out <- lapply(wp, function(l){
filter_ties(.data, !!as.name(attribute) == l)
out <- NULL
if(is_changing(.data) && is_longitudinal(.data)){
cl <- as_changelist(.data)
el <- as_edgelist(.data)

# Get all unique times in order
times <- sort(unique(cl$time))
if(!is.null(panels))
times <- intersect(panels, times)

waves <- lapply(times, function(t) {
# Latest changes by time t
changes <- cl %>%
dplyr::filter(time <= t) %>%
dplyr::group_by(node) %>%
dplyr::reframe(var = var,
latest_value = value[which.max(time)],
.groups = "drop")
for(v in unique(changes$var)){
upd <- rep(NA, net_nodes(.data))
upd[changes[var = v,]$node] <- changes[var = v,]$latest_value
old <- node_attribute(.data, v)
if(inherits(old, "logi")) old <- as.logical(old)
out <- add_node_attribute(.data, v, dplyr::coalesce(upd, old))
}
out <- delete_changes(out)
out <- filter_ties(out, wave == t)
out
})
names(out) <- wp
} else {
out <- filter_ties(.data, !!as.name(attribute) == wp)
}
if (isTRUE(cumulative)) {
out <- cumulative_ties(out, attribute)
names(waves) <- paste("Wave", times)
out <- waves
} else if(is_changing(.data)){
cl <- as_changelist(.data)
if(!attribute %in% names(cl) && "time" %in% names(cl)){
attribute <- "time"
}
# Get all unique times in order
times <- sort(unique(cl$time))
if(!is.null(panels))
times <- intersect(panels, times)

# Iterate over times
waves <- lapply(times, function(t) {
# Latest changes by time t
changes <- cl %>%
dplyr::filter(time <= t) %>%
dplyr::group_by(node) %>%
dplyr::reframe(var = var,
latest_value = value[which.max(time)],
.groups = "drop")
for(v in unique(changes$var)){
upd <- rep(NA, net_nodes(.data))
upd[changes[var = v,]$node] <- changes[var = v,]$latest_value
old <- node_attribute(.data, v)
if(inherits(old, "logi")) old <- as.logical(old)
out <- add_node_attribute(.data, v, dplyr::coalesce(upd, old))
}
out <- delete_changes(out)
out
})
names(waves) <- paste("Wave", times)
out <- waves
} else if(is_longitudinal(.data)){
wp <- unique(tie_attribute(.data, attribute))
if(!is.null(panels))
wp <- intersect(panels, wp)
if(length(wp) > 1) {
out <- lapply(wp, function(l){
filter_ties(.data, !!as.name(attribute) == l)
})
names(out) <- wp
} else {
out <- filter_ties(.data, !!as.name(attribute) == wp)
}
if (isTRUE(cumulative)) {
out <- .cumulative_ties(out, attribute)
}
out <- out[order(names(out))]
}
out[order(names(out))]
if(is.null(out)) .data else out
}

#' @export
Expand All @@ -218,7 +283,7 @@ to_waves.data.frame <- function(.data, attribute = "wave", panels = NULL,
out <- .data[,attribute == wp]
}
if (isTRUE(cumulative)) {
out <- cumulative_ties(out, attribute)
out <- .cumulative_ties(out, attribute)
}
out
}
Expand All @@ -240,12 +305,12 @@ to_waves.diff_model <- function(.data, attribute = "t", panels = NULL,
Recovered = node_is_recovered(diff, time = k))
}
if (isTRUE(cumulative)) {
out <- cumulative_ties(out, attribute)
out <- .cumulative_ties(out, attribute)
}
out
}

cumulative_ties <- function(x, attribute) {
.cumulative_ties <- function(x, attribute) {
edges <- to <- from <- NULL
thisRequires("zoo")
thisRequires("purrr")
Expand Down
3 changes: 2 additions & 1 deletion R/manynet-utils.R
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ utils::globalVariables(c(".data", "obs",
"nodes","event","exposure",
"student","students","colleges",
"node","value","var","active","time",
"A","B","C","D"))
"A","B","C","D",
"n"))

# Helper function for declaring available methods
available_methods <- function(fun_vctr) {
Expand Down
5 changes: 3 additions & 2 deletions R/mark_net.R
Original file line number Diff line number Diff line change
Expand Up @@ -105,9 +105,10 @@ is_list <- function(.data) {
is_longitudinal <- function(.data) {
if(is_manynet(.data)) {
ig <- as_igraph(.data)
catts <- names(igraph::graph_attr(ig, "changes"))
# catts <- names(igraph::graph_attr(ig, "changes"))
tatts <- igraph::edge_attr_names(ig)
return("time" %in% catts | "wave" %in% tatts | "panel" %in% tatts)
return(#"time" %in% catts |
"wave" %in% tatts | "panel" %in% tatts)
} else if(is_list(.data)){
all(lapply(.data, net_nodes)==net_nodes(.data[[1]]))
}
Expand Down
Loading
Loading