Skip to content

Commit

Permalink
Merge pull request #25 from schochastics/netswan
Browse files Browse the repository at this point in the history
  • Loading branch information
schochastics authored Feb 5, 2025
2 parents ae706ee + 2c8ce6e commit b9cd308
Show file tree
Hide file tree
Showing 10 changed files with 499 additions and 6 deletions.
4 changes: 2 additions & 2 deletions DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Package: netrankr
Type: Package
Title: Analyzing Partial Rankings in Networks
Version: 1.2.3.9000
Version: 1.2.4
Authors@R: c(
person("David", "Schoch", email = "david@schochastics.net", role = c("aut", "cre"),
comment = c(ORCID = "0000-0003-2952-4812")),
Expand All @@ -25,7 +25,7 @@ Imports: igraph (>= 1.0.1),
Rcpp (>= 0.12.8),
Matrix
LinkingTo: Rcpp,RcppArmadillo
RoxygenNote: 7.2.3
RoxygenNote: 7.3.2
Roxygen: list(markdown = TRUE)
Suggests:
knitr,
Expand Down
4 changes: 4 additions & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ export(neighborhood_inclusion)
export(positional_dominance)
export(rank_intervals)
export(spectral_gap)
export(swan_closeness)
export(swan_combinatory)
export(swan_connectivity)
export(swan_efficiency)
export(threshold_graph)
export(transitive_reduction)
export(walks_attenuated)
Expand Down
4 changes: 4 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# netrankr 1.2.4

* added functions from archived NetSwan package

# netrankr 1.2.3

* removed test causing issues on some platforms
Expand Down
293 changes: 293 additions & 0 deletions R/netswan.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,293 @@
#' @name swan_closeness
#' @title Impact on closeness when a node is removed
#'
#' @description
#' `swan_closeness` measures the change in the sum of the inverse of distances between all node pairs
#' when excluding that node.#'
#' @param g An `igraph` object representing the graph to analyze.
#'
#' @details
#' `swan_closeness` measures the impact of a node's removal by computing the change in
#' the sum of inverse distances between all node pairs.
#'
#' The code is an adaptation from the NetSwan package that was archived on CRAN.
#' @return
#' A numeric vector containing the `swan_closeness` values for all vertices.
#'
#' @references
#' Lhomme S. (2015). *Analyse spatiale de la structure des réseaux techniques dans un contexte de risques*.
#' Cybergeo: European Journal of Geography.
#' @examples
#' library(igraph)
#' # Example graph (electrical network structure)
#' elec <- matrix(ncol = 2, byrow = TRUE, c(
#' 11,1, 11,10, 1,2, 2,3, 2,9,
#' 3,4, 3,8, 4,5, 5,6, 5,7,
#' 6,7, 7,8, 8,9, 9,10
#' ))
#' gra <- graph_from_edgelist(elec, directed = FALSE)
#'
#' # Compute swan_closeness
#' f2 <- swan_closeness(gra)
#'
#' # Compare with betweenness centrality
#' bet <- betweenness(gra)
#' reg <- lm(bet ~ f2)
#' summary(reg)
#' @export
swan_closeness <- function(g) {
n <- igraph::vcount(g)
swancc <- rep(0, n)
cc <- igraph::distances(g)
ccb <- 1 / cc
ccb[is.infinite(ccb)] <- 0
tot <- sum(ccb)
for (i in seq_len(n)) {
g2 <- g
g2 <- igraph::delete_vertices(g2, i)
cc_no_i <- igraph::distances(g2)
cc_no_ib <- 1 / cc_no_i
cc_no_ib[is.infinite(cc_no_ib)] <- 0
tot2 <- sum(cc_no_ib)
swancc[i] <- tot2 - (tot - sum(ccb[i, ]) - sum(ccb[, i]))
}
return(swancc)
}

#' @name swan_combinatory
#' @title Error and attack tolerance of complex networks
#'
#' @description
#' `swan_combinatory` assesses network vulnerability and the resistance of networks
#' to node removals, whether due to random failures or intentional attacks.
#'
#' @param g An `igraph` object representing the graph to analyze.
#' @param k The number of iterations for assessing the impact of random failures.
#'
#' @details
#' Many complex systems display a surprising degree of tolerance against random failures.
#' However, this resilience often comes at the cost of extreme vulnerability to targeted attacks,
#' where removing key nodes (high-degree or high-betweenness nodes) can severely impact network connectivity.
#'
#' `swan_combinatory` simulates different attack strategies:
#' - **Random failure:** Nodes are removed randomly over multiple iterations.
#' - **Degree-based attack:** Nodes are removed in decreasing order of their degree.
#' - **Betweenness-based attack:** Nodes are removed in decreasing order of their betweenness centrality.
#' - **Cascading failure:** Nodes are removed based on recalculated betweenness after each removal.
#'
#' The function returns a matrix showing the connectivity loss for each attack scenario.
#'
#' The code is an adaptation from the NetSwan package that was archived on CRAN.
#' @return
#' A matrix with five columns:
#' \itemize{
#' \item Column 1: Fraction of nodes removed.
#' \item Column 2: Connectivity loss from betweenness-based attack.
#' \item Column 3: Connectivity loss from degree-based attack.
#' \item Column 4: Connectivity loss from cascading failure.
#' \item Column 5: Connectivity loss from random failures (averaged over `k` iterations).
#' }
#'
#' @references
#' Albert R., Jeong H., Barabási A. (2000). *Error and attack tolerance of complex networks*.
#' Nature, 406(6794), 378-382.
#'
#' @examples
#' library(igraph)
#' # Example electrical network graph
#' elec <- matrix(ncol = 2, byrow = TRUE, c(
#' 11,1, 11,10, 1,2, 2,3, 2,9,
#' 3,4, 3,8, 4,5, 5,6, 5,7,
#' 6,7, 7,8, 8,9, 9,10
#' ))
#' gra <- graph_from_edgelist(elec, directed = FALSE)
#'
#' # Compute vulnerability measures
#' f4 <- swan_combinatory(gra, 10)
#' @export
swan_combinatory <- function(g, k) {
n <- igraph::vcount(g)
dist <- igraph::distances(g)
dist[is.infinite(dist)] <- 0
dist[dist > 0] <- 1
tot <- sum(dist)
fin <- matrix(ncol = 5, nrow = n, 0)
mat <- matrix(ncol = 2, nrow = n, 0)
mat[, 1] <- 1:n
bet <- igraph::betweenness(g)
mat[, 2] <- bet
matri <- mat[order(mat[, 2]), ]
g2 <- g
for (i in seq_len(n)) {
v = n + 1 - i
g2 <- igraph::delete_vertices(g2, matri[v, 1])
dist2 <- igraph::distances(g2)
dist2[is.infinite(dist2)] <- 0
dist2[dist2 > 0] <- 1
tot2 <- sum(dist2)
fin[i, 1] <- i / n
fin[i, 2] <- tot - tot2
matri[matri[, 1] > matri[v, 1], 1] <- matri[matri[, 1] > matri[v, 1], 1] - 1
}
mat <- matrix(ncol = 2, nrow = n, 0) #degree attack
mat[, 1] <- 1:n
deg <- igraph::degree(g)
mat[, 2] <- deg
matri <- mat[order(mat[, 2]), ]
g2 <- g
for (i in seq_len(n)) {
v = n + 1 - i
g2 <- igraph::delete_vertices(g2, matri[v, 1])
dist2 <- igraph::distances(g2)
dist2[is.infinite(dist2)] <- 0
dist2[dist2 > 0] <- 1
tot2 <- sum(dist2)
fin[i, 3] <- tot - tot2
matri[matri[, 1] > matri[v, 1], 1] <- matri[matri[, 1] > matri[v, 1], 1] -
1 #bluff
}
g2 <- g #cascading
npro <- n
lim <- n - 1
for (i in 1:lim) {
mat <- matrix(ncol = 2, nrow = npro, 0)
mat[, 1] <- 1:npro
bet <- igraph::betweenness(g2)
mat[, 2] <- bet
matri <- mat[order(mat[, 2]), ]
g2 <- igraph::delete_vertices(g2, matri[npro, 1])
dist2 <- igraph::distances(g2)
dist2[is.infinite(dist2)] <- 0
dist2[dist2 > 0] <- 1
tot2 <- sum(dist2)
fin[i, 4] <- tot - tot2
npro <- npro - 1
}
fin[n, 4] <- tot
#random
for (l in seq_len(k)) {
al <- sample(1:n, n)
g2 <- g
for (i in seq_len(k)) {
g2 <- igraph::delete_vertices(g2, al[i])
dist2 <- igraph::distances(g2)
dist2[is.infinite(dist2)] <- 0
dist2[dist2 > 0] <- 1
tot2 <- sum(dist2)
fin[i, 5] <- fin[i, 5] + (tot - tot2)
al[al > al[i]] <- al[al > al[i]] - 1 #bluff
}
}
fin[, 2:4] <- fin[, 2:4] / tot
fin[, 5] <- fin[, 5] / tot / k
return(fin)
}

#' @name swan_connectivity
#' @title Impact on connectivity when a node is removed
#'
#' @description
#' `swan_connectivity` measures the loss of connectivity when a node is removed from the network.
#'
#' @usage
#' swan_connectivity(g)
#'
#' @param g An `igraph` object representing the graph to analyze.
#'
#' @details
#' Connectivity loss indices quantify the decrease in the number of relationships between nodes
#' when one or more components are removed. `swan_connectivity` computes the connectivity loss
#' by systematically excluding each node and evaluating the resulting changes in the network structure.
#'
#' The code is an adaptation from the NetSwan package that was archived on CRAN.
#' @return
#' A numeric vector where each entry represents the connectivity loss when the corresponding node is removed.
#'
#' @references
#' Lhomme S. (2015). *Analyse spatiale de la structure des réseaux techniques dans un contexte de risques*.
#' Cybergeo: European Journal of Geography.
#' @examples
#' library(igraph)
#' # Example graph (electrical network structure)
#' elec <- matrix(ncol = 2, byrow = TRUE, c(
#' 11,1, 11,10, 1,2, 2,3, 2,9,
#' 3,4, 3,8, 4,5, 5,6, 5,7,
#' 6,7, 7,8, 8,9, 9,10
#' ))
#' gra <- graph_from_edgelist(elec, directed = FALSE)
#'
#' # Compute connectivity loss
#' f3 <- swan_connectivity(gra)
#' @export
swan_connectivity <- function(g) {
n <- igraph::vcount(g)
fin <- rep(0, n)
dist <- igraph::distances(g)
dist[!is.infinite(dist)] <- 0
dist[is.infinite(dist)] <- 1
con <- sum(dist)
for (i in seq_len(n)) {
g2 <- g
g2 <- igraph::delete_vertices(g2, i)
dist2 <- igraph::distances(g2)
dist2[!is.infinite(dist2)] <- 0
dist2[is.infinite(dist2)] <- 1
con2 <- sum(dist2)
fin[i] <- con2 - con
}
return(fin)
}

#' @name swan_efficiency
#' @title Impact on farness when a node is removed
#'
#' @description
#' `swan_efficiency` measures the change in the sum of distances between all node pairs
#' when excluding a node from the network.
#'
#' @param g An `igraph` object representing the graph to analyze.
#' `swan_efficiency` is based on geographic accessibility, similar to indices used for
#' assessing transportation network performance, such as closeness accessibility.
#' It quantifies the impact of node removal by calculating the change in the sum of
#' distances between all node pairs.
#'
#' The code is an adaptation from the NetSwan package that was archived on CRAN.
#'
#' @return
#' A numeric vector where each entry represents the `swan_efficiency` value for the
#' corresponding node.
#'
#' @references
#' Lhomme S. (2015). *Analyse spatiale de la structure des réseaux techniques dans un
#' contexte de risques*. Cybergeo: European Journal of Geography.
#'
#' @examples
#' library(igraph)
#' # Example graph (electrical network structure)
#' elec <- matrix(ncol = 2, byrow = TRUE, c(
#' 11,1, 11,10, 1,2, 2,3, 2,9,
#' 3,4, 3,8, 4,5, 5,6, 5,7,
#' 6,7, 7,8, 8,9, 9,10
#' ))
#' gra <- graph_from_edgelist(elec, directed = FALSE)
#'
#' # Compute efficiency impact of node removal
#' f2 <- swan_efficiency(gra)
#' bet <- betweenness(gra)
#' reg <- lm(bet ~ f2)
#' summary(reg)
#' @export
swan_efficiency <- function(g) {
n <- igraph::vcount(g)
fin <- rep(0, n)
dt = igraph::distances(g)
tot = sum(dt)
for (i in 1:n) {
g2 <- g
g2 <- igraph::delete_vertices(g2, i)
dt2 <- igraph::distances(g2)
tot2 <- sum(dt2)
fin[i] <- tot2 - (tot - sum(dt[i, ]) - sum(dt[, i]))
}
return(fin)
}
5 changes: 2 additions & 3 deletions cran-comments.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
# Update from 1.2.2 to 1.2.3
# Update from 1.2.3 to 1.2.4

quick bugfix release to prevent package from being deleted from CRAN due to
failing test
added functions from an archived CRAN package

## Test environments
* ubuntu 22.04, R 4.3.2
Expand Down
2 changes: 1 addition & 1 deletion man/netrankr-package.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

46 changes: 46 additions & 0 deletions man/swan_closeness.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit b9cd308

Please sign in to comment.