Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Package: manynet
Title: Many Ways to Make, Manipulate, and Modify Myriad Networks
Version: 2.3.0
Version: 2.3.1
Description: Many tools for making, manipulating, and modifying many different types of networks.
All functions operate with matrices, edge lists, and 'igraph', 'network', and 'tidygraph' objects,
on directed, multiplex, multimodal, signed, and other networks.
Expand Down
16 changes: 16 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,19 @@
# manynet 2.3.1

## Marking

- Fixed `is_longitudinal()` marking a network whose ties carry no moments

## Measures

- Fixed `tie_attribute.stocnet()` and `node_attribute.stocnet()`
- Returns every attribute where no attribute is named, as `.igraph` method already did
- Reports tie attributes without 'from' and 'to', which identify a tie rather than describe it

## Manipulating

- Fixed a mark inside `filter_ties()` or `mutate_ties()` reading correct `{tidygraph}` context

# manynet 2.3.0

## Package
Expand Down
6 changes: 5 additions & 1 deletion R/mark_changes.R
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,11 @@ is_longitudinal.default <- function(.data) {
is_longitudinal.igraph <- function(.data) {
# A panel network re-observes the whole network at each moment, so each
# moment replaces the one before it. See `.time_rule()`.
identical(.time_rule(.data), "replace") && !is.null(.time_moments(.data))
# It re-observes the ties too, so the ties carry the stamp. A network that
# records only nodal changes, such as a diffusion on a static network,
# is therefore not a panel.
identical(.time_rule(.data), "replace") && !is.null(.time_moments(.data)) &&
any(c("time", "wave", "panel") %in% net_tie_attributes(.data))
}

#' @export
Expand Down
18 changes: 18 additions & 0 deletions R/measure_attributes.R
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,17 @@ node_attribute.default <- function(.data, attr_name){

#' @export
node_attribute.stocnet <- function(.data, attr_name){
# `igraph::vertex_attr()` returns every attribute where none is named,
# so every other class returns every attribute there too.
if(missing(attr_name)) return(as.list(.data$nodes))
out <- .data$nodes[[attr_name]]
if(is.numeric(out)) make_node_measure(out, .data) else out
}

#' @export
node_attribute.network <- function(.data, attr_name){
if(missing(attr_name)) return(.all_attributes(.data, node_attribute,
net_node_attributes(.data)))
network::get.vertex.attribute(.data, attr_name)
}

Expand Down Expand Up @@ -115,15 +120,28 @@ tie_attribute.default <- function(.data, attr_name){

#' @export
tie_attribute.stocnet <- function(.data, attr_name){
# `igraph::edge_attr()` returns every attribute where none is named,
# so every other class returns every attribute there too.
# 'from' and 'to' identify a tie rather than describe it, so they are dropped.
if(missing(attr_name))
return(as.list(.data$ties[setdiff(names(.data$ties), c("from", "to"))]))
out <- .data$ties[[attr_name]]
if(is.numeric(out)) make_tie_measure(out, .data) else out
}

#' @export
tie_attribute.network <- function(.data, attr_name){
if(missing(attr_name)) return(.all_attributes(.data, tie_attribute,
net_tie_attributes(.data)))
network::get.edge.attribute(.data, attr_name)
}

# Collects every named attribute into a list, as `igraph::vertex_attr()` and
# `igraph::edge_attr()` do where the caller names no attribute.
.all_attributes <- function(.data, FUN, attr_names){
stats::setNames(lapply(attr_names, function(x) FUN(.data, x)), attr_names)
}

#' @rdname measure_attributes_ties
#' @examples
#' tie_weights(to_mode1(ison_southern_women))
Expand Down
46 changes: 26 additions & 20 deletions R/reexports_classes.R
Original file line number Diff line number Diff line change
Expand Up @@ -102,25 +102,10 @@ clear_active_context <- function() {

active_network <- function(required = NULL) {

ctx <- get_active_context()

if (!is.null(ctx$data)) {

if (!is.null(required) &&
!identical(ctx$active, required)) {
snet_abort(
paste0(
"This call requires ",
if (identical(required, "edges")) "ties" else required,
" to be active"
),
call. = FALSE
)
}

return(ctx$data)
}

# A tidygraph context is set only inside a tidygraph verb, so where one is
# set it names a nearer network than the stored context does. Reading it
# first keeps a nested call, such as a mark inside `filter_ties()`, from
# resolving against the outer network.
tg <- tryCatch({

if (!tidygraph::.graph_context$free()) {
Expand All @@ -146,7 +131,28 @@ active_network <- function(required = NULL) {

}, error = function(e) NULL)

tg
if (!is.null(tg)) return(tg)

ctx <- get_active_context()

if (!is.null(ctx$data)) {

if (!is.null(required) &&
!identical(ctx$active, required)) {
snet_abort(
paste0(
"This call requires ",
if (identical(required, "edges")) "ties" else required,
" to be active"
),
call. = FALSE
)
}

return(ctx$data)
}

NULL
}

with_active_context <- function(data, active, expr) {
Expand Down
17 changes: 17 additions & 0 deletions cran-comments.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,20 @@
## R CMD check results

0 errors | 0 warnings | 0 notes

## Reverse dependencies

The auto-check of 2.3.0 reported new failures in `autograph` and `netrics`.
I maintain both packages.

This version fixes the three causes that were bugs in `manynet`:

* `tie_attribute()` and `node_attribute()` aborted on a stocnet object where
the caller named no attribute, which the `{igraph}` method allows.
* A mark inside `filter_ties()` read the outer network, not the filtered one.
* `is_longitudinal()` marked a network whose ties carry no moment.

The remaining failures are calls in the released `autograph` 1.1.2 to
`to_no_isolates()`, which 2.3.0 deprecates, and to arguments that `autograph`
itself deprecates. `autograph` 1.2.0 removes them and passes against this
version with 0 failures. It is submitted separately.
33 changes: 33 additions & 0 deletions tests/testthat/test-manip_grab.R
Original file line number Diff line number Diff line change
Expand Up @@ -195,3 +195,36 @@ test_that("describe_ties counts the parallel ties", {
expect_no_match(describe_ties(ison_adolescents), "parallel")
expect_no_match(describe_ties(ison_monks), "parallel")
})

test_that("tie_attribute and node_attribute return every attribute where none is named", {
# `igraph::edge_attr()` and `igraph::vertex_attr()` do this, so the other
# classes' methods do too.
expect_type(tie_attribute(irps_nuclear), "list")
expect_equal(names(tie_attribute(irps_nuclear)),
names(tie_attribute(as_igraph(irps_nuclear))))
expect_equal(names(node_attribute(as_igraph(irps_nuclear))),
net_node_attributes(as_igraph(irps_nuclear)))
# 'from' and 'to' identify a tie rather than describe it, so a stocnet
# object reports the attributes alone.
expect_false(any(c("from", "to") %in% names(tie_attribute(irps_nuclear))))
expect_equal(names(tie_attribute(as_network(irps_nuclear))),
net_tie_attributes(as_network(irps_nuclear)))
})

test_that("a mark inside filter_ties reads the filtered network", {
# `filter_ties()` on an igraph object sets a tidygraph context, which names
# a nearer network than any context an outer stocnet call has stored.
like <- to_uniplex(ison_monks, "like")
seen <- NULL
count_ties <- function() {
seen <<- as.integer(net_ties(expect_ties()))
TRUE
}
wave1 <- filter_ties(as_igraph(like), time == 1)
expect_lt(net_ties(wave1), net_ties(like))
mutate_ties(like, all = {
filter_ties(wave1, count_ties())
TRUE
})
expect_equal(seen, as.integer(net_ties(wave1)))
})
14 changes: 14 additions & 0 deletions tests/testthat/test-mark_is.R
Original file line number Diff line number Diff line change
Expand Up @@ -70,3 +70,17 @@ test_that("is_connected respects connectivity", {
expect_true(is_connected(ison_adolescents, connectivity = "strong"))
expect_error(is_connected(ison_adolescents, connectivity = "bloop"))
})

test_that("is_longitudinal does not mark a network whose ties carry no moment", {
# A panel re-observes the ties, so the ties carry the stamp. A diffusion
# records only how the nodes change, on a network whose ties never change,
# so it is not a panel.
diff <- play_diffusion(create_ring(12), seeds = 1)
expect_true(is_changing(diff))
expect_equal(net_tie_attributes(diff), character(0))
expect_false(is_longitudinal(diff))
# The panels still mark TRUE, whether or not they also record changes.
expect_true(is_longitudinal(ison_monks))
expect_true(is_longitudinal(ison_classmates))
expect_true(is_longitudinal(fict_starwars))
})
Loading