Skip to content

Commit

Permalink
added evcent and tests
Browse files Browse the repository at this point in the history
  • Loading branch information
schochastics committed Jan 7, 2020
1 parent 114dd6d commit 3ba6f29
Show file tree
Hide file tree
Showing 10 changed files with 252 additions and 6 deletions.
1 change: 1 addition & 0 deletions .gitignore
Expand Up @@ -35,3 +35,4 @@ vignettes/*.pdf
# Shiny token, see https://shiny.rstudio.com/articles/shinyapps.html
rsconnect/
.Rproj.user
inst/doc
5 changes: 4 additions & 1 deletion DESCRIPTION
Expand Up @@ -23,7 +23,10 @@ Suggests:
testthat (>= 2.1.0),
covr,
ggplot2,
ggraph
ggraph,
knitr,
rmarkdown
LinkingTo:
Rcpp,
RcppArmadillo
VignetteBuilder: knitr
2 changes: 2 additions & 0 deletions NAMESPACE
Expand Up @@ -4,13 +4,15 @@ export(as_adj_complex)
export(as_adj_signed)
export(as_complex_edges)
export(as_incidence_complex)
export(as_incidence_signed)
export(as_signed_proj)
export(as_unsigned_2mode)
export(balance_score)
export(complex_walks)
export(count_complex_triangles)
export(count_signed_triangles)
export(degree_signed)
export(eigen_centrality_signed)
export(ggblock)
export(ggsigned)
export(graph_circular_signed)
Expand Down
72 changes: 71 additions & 1 deletion R/centrality_indices.R
Expand Up @@ -7,6 +7,21 @@
#' @references Everett, M. and Borgatti, S. (2014) Networks containing negative ties. *Social Networks* 38 111-120
#' @author David Schoch
#' @importFrom Matrix t
#' @examples
#' library(igraph)
#' A <- matrix(c(0, 1, 0, 1, 0, 0, 0, -1, -1, 0,
#' 1, 0, 1, -1, 1, -1, -1, 0, 0, 0,
#' 0, 1, 0, 1, -1, 0, 0, 0, -1, 0,
#' 1, -1, 1, 0, 1, -1, -1, 0, 0, 0,
#' 0, 1, -1, 1, 0, 1, 0, -1, 0, -1,
#' 0, -1, 0, -1, 1, 0, 1, 0, 1, -1,
#' 0, -1, 0, -1, 0, 1, 0, 1, -1, 1,
#' -1, 0, 0, 0, -1, 0, 1, 0, 1, 0,
#' -1, 0, -1, 0, 0, 1, -1, 1, 0, 1,
#' 0, 0, 0, 0, -1, -1, 1, 0, 1, 0), 10, 10)

#'g <- igraph::graph_from_adjacency_matrix(A,"undirected",weighted = "sign")
#'pn_index(g)
#' @export

pn_index <- function(g,mode=c("all","in","out")){
Expand Down Expand Up @@ -95,6 +110,61 @@ degree_signed <- function(g,mode=c("all","in","out"), type = c("pos","neg","rati
ratio = Matrix::colSums(P)/(Matrix::colSums(P)+Matrix::colSums(N)),
net = Matrix::colSums(P)-Matrix::colSums(N)
)
res
return(res)
}
}

#' @title Signed Eigenvector centrality
#' @description Calcul
#' @details Note that, with negative values, the adjacency matrix may not have a dominant eigenvalue.
#' This means it is not clear which eigenvector should be used. In addition it is possible for the adjacency matrix to have repeated #'eigenvalues and hence multiple linearly independent eigenvectors. In this case certain centralities can be arbitrarily assigned. The function returns an error if this is the case.
#' @param g igraph object. Must have a "sign" edge attribute.
#' @param scale Logical scalar, whether to scale the result to have a maximum score of one. If no scaling is used then the result vector is the same as returned by `eigen()`.
#' @return centrality scores as numeric vector.
#' @references
#' Bonacich, P. and Lloyd, P. (2004). "Calculating Status with Negative Relations." *Social Networks* 26 (4): 331–38.
#'
#' Everett, M. and Borgatti, S.P. (2014). "Networks Containing Negative Ties." *Social Networks* 38: 111–20.
#'
#' @author David Schoch
#' @examples
#' library(igraph)
#' # example for network without dominant eigenvalue (from Everett&Borgatti)
#'
#' A <- matrix(c( 0, 1, 1, -1, 0, 0, -1, 0, 0,
#' 1, 0, 1, 0, -1, 0, 0, -1, 0,
#' 1, 1, 0, 0, 0, -1, 0, 0, -1,
#' -1, 0, 0, 0, 1, 1, -1, 0, 0,
#' 0, -1, 0, 1, 0, 1, 0, -1, 0,
#' 0, 0, -1, 1, 1, 0, 0, 0, -1,
#' -1, 0, 0, -1, 0, 0, 0, 1, 1,
#' 0, -1, 0, 0, -1, 0, 1, 0, 1,
#' 0, 0, -1, 0, 0, -1, 1, 1, 0), 9, 9)
#' g <- graph_from_adjacency_matrix(A,"undirected",weighted = "sign")
#'
#' # eigen_centrality_signed(g)
#'
#' @export

eigen_centrality_signed <- function(g, scale = TRUE){
if (!igraph::is_igraph(g)) {
stop("Not a graph object")
}
if(!"sign"%in%igraph::edge_attr_names(g)){
stop("network does not have a sign edge attribute")
}
A <- as_adj_signed(g,sparse = TRUE)
sA <- eigen(A)
evals <- round(sA$values,8)
max_evals <- which(abs(evals)==max(abs(evals)))

if(length(max_evals)!=1){
stop("no dominant eigenvalue exists")
} else{
evcent <- sA$vectors[,max_evals]
}

if(scale) evcent <- evcent/max(evcent)

return(evcent)
}
22 changes: 22 additions & 0 deletions R/complex_matrices.R
Expand Up @@ -18,6 +18,28 @@ as_adj_signed <- function(g,sparse = FALSE){
igraph::as_adj(g,type = "both",attr = "sign", sparse = sparse)
}

#' Convert a signed two-mode network to a signed matrix
#'
#' This function returns the incidence matrix for a signed two-mode network.
#'
#' @param g igraph object (bipartite). Must have a "sign" edge attribute.
#' @param sparse Logical scalar, whether to return the result as a sparse matrix. The Matrix package is required for sparse matrices.
#' @return signed incidence matrix
#' @export

as_incidence_signed <- function(g,sparse = FALSE){
if (!igraph::is_igraph(g)) {
stop("Not a graph object")
}
if(!"sign"%in%igraph::edge_attr_names(g)){
stop("network does not have a sign edge attribute")
}
if(!"type"%in%igraph::vertex_attr_names(g)){
stop("network must have a type vertex attribute")
}
igraph::as_incidence_matrix(g,attr = "sign", sparse = sparse)
}


#' Convert a signed graph to a complex adjacency matrix
#'
Expand Down
19 changes: 19 additions & 0 deletions man/as_incidence_signed.Rd

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

49 changes: 49 additions & 0 deletions man/eigen_centrality_signed.Rd

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

15 changes: 15 additions & 0 deletions man/pn_index.Rd

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

41 changes: 41 additions & 0 deletions tests/testthat/test-centrality_indices.R
Expand Up @@ -63,3 +63,44 @@ test_that("pn index works",{
igraph::E(g)$sign <- 1
expect_equal(pn_index(g,mode="in"),c(2, 2, 2, 2, 2))
})

test_that("evcent not graph error works",{
expect_error(eigen_centrality_signed(g = 5))
})

test_that("evcent no sign error works",{
g <- igraph::graph.full(5)
expect_error(eigen_centrality_signed(g))
})

test_that("evcent not dominant works",{
A <- matrix(c( 0, 1, 1, -1, 0, 0, -1, 0, 0,
1, 0, 1, 0, -1, 0, 0, -1, 0,
1, 1, 0, 0, 0, -1, 0, 0, -1,
-1, 0, 0, 0, 1, 1, -1, 0, 0,
0, -1, 0, 1, 0, 1, 0, -1, 0,
0, 0, -1, 1, 1, 0, 0, 0, -1,
-1, 0, 0, -1, 0, 0, 0, 1, 1,
0, -1, 0, 0, -1, 0, 1, 0, 1,
0, 0, -1, 0, 0, -1, 1, 1, 0), 9, 9)
g <- igraph::graph_from_adjacency_matrix(A,"undirected",weighted = "sign")
expect_error(eigen_centrality_signed(g))
})

test_that("evcent works",{
g <- igraph::graph.full(5)
igraph::E(g)$sign <- 1
igraph::E(g)$sign[1] <- -1
ev <- round(eigen_centrality_signed(g, scale = TRUE),8)
ev_true <- c(0.68614066, 0.68614066, 1, 1, 1)
expect_equal(ev,ev_true)
})

test_that("evcent no scale works",{
g <- igraph::graph.full(5)
igraph::E(g)$sign <- 1
igraph::E(g)$sign[1] <- -1
ev <- round(eigen_centrality_signed(g, scale = FALSE),8)
ev_true <- c(0.34560347, 0.34560347, 0.50369186, 0.50369186, 0.50369186)
expect_equal(ev,ev_true)
})
32 changes: 28 additions & 4 deletions tests/testthat/test-complex_matrices.R
@@ -1,9 +1,33 @@
test_that("complex: error not graph works", {
expect_error(as_adj_complex(g=5))
test_that("incidence: error not graph works", {
expect_error(as_incidence_signed(g=5))
})

test_that("complex: error directed works", {
expect_error(as_adj_complex(igraph::graph.full(3,directed=TRUE)))
test_that("incidence: error no sign works", {
expect_error(as_incidence_signed(igraph::graph.full(3,directed=FALSE)))
})

test_that("incidence: error no type works", {
g <- igraph::graph.full(3,directed=FALSE)
igraph::E(g)$sign <- 1
expect_error(as_incidence_signed(g))
})

test_that("signed incidence works", {
A_true <- matrix(c(1,1,1,-1,-1,-1,-1,
1,1,1,-1,-1,-1,-1,
1,1,1,-1,-1,-1,-1,
-1,-1,-1,1,1,1,1,
-1,-1,-1,1,1,1,1),5,7,byrow = T)
rownames(A_true) <- letters[1:5]
colnames(A_true) <- 1:7
g <- igraph::graph_from_incidence_matrix(A_true,weighted = "sign")
A <- as_incidence_signed(g)
expect_equal(A,A_true)
})


test_that("complex: error not graph works", {
expect_error(as_adj_complex(g=5))
})

test_that("complex: error directed works", {
Expand Down

0 comments on commit 3ba6f29

Please sign in to comment.