Skip to content

Commit

Permalink
Merge pull request #74 from jhollway/develop
Browse files Browse the repository at this point in the history
Readded basic centrality measures
  • Loading branch information
jhollway authored Jan 11, 2021
2 parents 709f518 + d8fc77e commit 44880df
Show file tree
Hide file tree
Showing 22 changed files with 365 additions and 168 deletions.
48 changes: 0 additions & 48 deletions .github/workflows/pkgdown.yaml

This file was deleted.

44 changes: 43 additions & 1 deletion .github/workflows/pushrelease.yml
Original file line number Diff line number Diff line change
Expand Up @@ -157,4 +157,46 @@ jobs:
upload_url: ${{ steps.create_release.outputs.upload_url }} # This pulls from the CREATE RELEASE step above, referencing it's ID to get its outputs object, which include a `upload_url`. See this blog post for more info: https://jasonet.co/posts/new-features-of-github-actions/#passing-data-to-future-steps
asset_path: migraph_winOS.zip
asset_name: migraph_winOS.zip
asset_content_type: application/zip
asset_content_type: application/zip

pkgdown:
name: Build and deploy website
needs: release
runs-on: macOS-latest
env:
GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}
steps:
- uses: actions/checkout@v2

- uses: r-lib/actions/setup-r@v1

- uses: r-lib/actions/setup-pandoc@v1

- name: Query dependencies
run: |
install.packages('remotes')
saveRDS(remotes::dev_package_deps(dependencies = TRUE), ".github/depends.Rds", version = 2)
writeLines(sprintf("R-%i.%i", getRversion()$major, getRversion()$minor), ".github/R-version")
shell: Rscript {0}

- name: Cache R packages
uses: actions/cache@v2
with:
path: ${{ env.R_LIBS_USER }}
key: ${{ runner.os }}-${{ hashFiles('.github/R-version') }}-1-${{ hashFiles('.github/depends.Rds') }}
restore-keys: ${{ runner.os }}-${{ hashFiles('.github/R-version') }}-1-

- name: Install dependencies
run: |
remotes::install_deps(dependencies = TRUE)
install.packages("pkgdown", type = "binary")
shell: Rscript {0}

- 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)'
10 changes: 7 additions & 3 deletions DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Package: migraph
Title: Multimodal and multilevel network analysis
Version: 0.4.0
Version: 0.4.1
Description: This package assembles a number of utility functions for
loading, visualising, and analysing multimode, multiplex, and multilevel networks.
URL: https://github.com/jhollway/migraph
Expand All @@ -10,14 +10,18 @@ License: MIT + file LICENSE
Encoding: UTF-8
LazyData: true
RoxygenNote: 7.1.1
Imports: igraph,
Imports:
igraph,
tidygraph,
geosphere,
threejs,
roxygen2,
ggplot2,
tidyverse,
covr
covr,
R6,
magrittr,
rlang
Suggests:
testthat,
knitr,
Expand Down
23 changes: 23 additions & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
@@ -1,17 +1,40 @@
# Generated by roxygen2: do not edit by hand

export("%>%")
export(.E)
export(.G)
export(.N)
export(as_incidence_matrix)
export(centrality_betweenness)
export(centrality_closeness)
export(centrality_degree)
export(create_chain)
export(create_match)
export(create_nest)
export(create_silos)
export(is.igraph)
export(is.tbl_graph)
export(is_bipartite)
export(play_twomode)
export(project_cols)
export(project_rows)
export(with_graph)
import(tidygraph)
importFrom(igraph,V)
importFrom(igraph,as.igraph)
importFrom(igraph,as_adjacency_matrix)
importFrom(igraph,bipartite.projection)
importFrom(igraph,degree)
importFrom(igraph,graph_from_incidence_matrix)
importFrom(igraph,is.igraph)
importFrom(igraph,is_bipartite)
importFrom(magrittr,"%>%")
importFrom(rlang,enquo)
importFrom(rlang,eval_tidy)
importFrom(tidygraph,.E)
importFrom(tidygraph,.G)
importFrom(tidygraph,.N)
importFrom(tidygraph,as_tbl_graph)
importFrom(tidygraph,is.tbl_graph)
importFrom(tidygraph,play_bipartite)
importFrom(tidygraph,with_graph)
19 changes: 19 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,22 @@
# migraph 0.4.1

## Package

* pkgdown now deploys after release
* Reexported a number of `{igraph}` and `{tidygraph}` functions for internal use
* Completed some `convert_` and `project_` documentation

## Data

* Updated mpn_ data source references

## Analysis

* Added centrality measures that take (and if necessary return) matrix, igraph, or tidygraph objects, and offer a correct normalization for two-mode networks
* Added `centrality_degree()`
* Added `centrality_closeness()`
* Added `centrality_betweenness()`

# migraph 0.4.0

2021-01-08
Expand Down
145 changes: 145 additions & 0 deletions R/centrality.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
#' Centrality for one- and two-mode networks
#'
#' These functions calculate common centrality measures for both one- and two-mode networks.
#' They accept as objects matrices and `igraph` graphs,
#' and can be used within a tidygraph workflow.
#' Importantly, these functions also offer correct normalization for two-mode networks.
#' @name centrality
#' @family two-mode functions
#' @param object Either an igraph graph object or a matrix.
#' @param weights The weight of the edges to use for the calculation.
#' Will be evaluated in the context of the edge data.
#' @param mode How should edges be followed. Ignored for undirected graphs
#' @param loops Should loops be included in the calculation
#' @param normalized Should the output be normalized for one or two-modes networks
#' @importFrom rlang enquo eval_tidy
#' @importFrom igraph graph_from_incidence_matrix is_bipartite degree V
#' @references Borgatti, Stephen P., and Martin G. Everett. "Network analysis of 2-mode data." Social networks 19.3 (1997): 243-270.
#' @examples
#' centrality_degree(southern_women)
#' mpn_powerelite %>% tidygraph::mutate(degree = centrality_degree())
#' @return Depending on how and what kind of an object is passed to the function,
#' the function will return a `tidygraph` object where the nodes have been updated
#' @export
centrality_degree <- function (object, weights = NULL, mode = "out", loops = TRUE, normalized = FALSE){

# Converge inputs
if(missing(object)){
expect_nodes()
graph <- .G()
weights <- rlang::enquo(weights)
weights <- rlang::eval_tidy(weights, .E())
} else if (is.igraph(object)) {
graph <- object
} else if (is.matrix(object)) {
graph <- igraph::graph_from_incidence_matrix(object)
}

# Do the calculations
if (is.null(weights)) {
weights <- NA
}
if (igraph::is_bipartite(graph) & normalized){
degrees <- igraph::degree(graph = graph, v = igraph::V(graph), mode = mode, loops = loops)
other_set_size <- ifelse(igraph::V(graph)$type, sum(!igraph::V(graph)$type), sum(igraph::V(graph)$type))
degrees/other_set_size
} else {
if (is.null(weights)) {
igraph::degree(graph = graph, V = igraph::V(graph), mode = mode, loops = loops,
normalized = normalized)
}
else {
igraph::strength(graph = graph, vids = igraph::V(graph), mode = mode,
loops = loops, weights = weights)
}
}
}

#' @rdname centrality
#' @family two-mode functions
#' @param cutoff maximum path length to use during calculations
#' @import tidygraph
#' @examples
#' centrality_closeness(southern_women)
#' mpn_powerelite %>% tidygraph::mutate(closeness = centrality_closeness())
#' @export
centrality_closeness <- function (object, weights = NULL, mode = "out", normalized = FALSE, cutoff = NULL){

# Converge inputs
if(missing(object)){
expect_nodes()
graph <- .G()
weights <- rlang::enquo(weights)
weights <- rlang::eval_tidy(weights, .E())
} else if (is.igraph(object)) {
graph <- object
} else if (is.matrix(object)) {
graph <- igraph::graph_from_incidence_matrix(object)
}

# Do the calculations
if (is.null(weights)) {
weights <- NA
}
if (igraph::is_bipartite(graph) & normalized){
# farness <- rowSums(igraph::distances(graph = graph))
closeness <- igraph::closeness(graph = graph, vids = igraph::V(graph), mode = mode)
other_set_size <- ifelse(igraph::V(graph)$type, sum(!igraph::V(graph)$type), sum(igraph::V(graph)$type))
set_size <- ifelse(igraph::V(graph)$type, sum(igraph::V(graph)$type), sum(!igraph::V(graph)$type))
closeness/(1/(other_set_size+2*set_size-2))
} else {
if (is.null(cutoff)) {
igraph::closeness(graph = graph, vids = igraph::V(graph), mode = mode,
weights = weights, normalized = normalized)
} else {
igraph::estimate_closeness(graph = graph, vids = igraph::V(graph), mode = mode,
cutoff = cutoff, weights = weights, normalized = normalized)
}
}
}

#' @rdname centrality
#' @family two-mode functions
#' @param directed Should direction of edges be used for the calculations
#' @param cutoff maximum path length to use during calculations
#' @param nobigint Should big integers be avoided during calculations
#' @import tidygraph
#' @references Borgatti, Stephen P., and Martin G. Everett. "Network analysis of 2-mode data." Social networks 19.3 (1997): 243-270.
#' @examples
#' centrality_betweenness(southern_women)
#' mpn_powerelite %>% tidygraph::mutate(betweenness = centrality_betweenness())
#' @return A numeric vector giving the betweenness centrality measure of each node.
#' @export
centrality_betweenness <- function(object, weights = NULL, directed = TRUE, cutoff = NULL, nobigint = TRUE, normalized = FALSE){

# Converge inputs
if(missing(object)){
expect_nodes()
graph <- .G()
weights <- rlang::enquo(weights)
weights <- rlang::eval_tidy(weights, .E())
} else if (is.igraph(object)) {
graph <- object
} else if (is.matrix(object)) {
graph <- igraph::graph_from_incidence_matrix(object)
}

# Do the calculations
if (is.null(weights)) {
weights <- NA
}
if (igraph::is_bipartite(graph) & normalized){
betw_scores <- igraph::betweenness(graph = graph, v = igraph::V(graph), directed = directed, nobigint = nobigint)
other_set_size <- ifelse(igraph::V(graph)$type, sum(!igraph::V(graph)$type), sum(igraph::V(graph)$type))
set_size <- ifelse(igraph::V(graph)$type, sum(igraph::V(graph)$type), sum(!igraph::V(graph)$type))
ifelse(set_size > other_set_size,
betw_scores/(2*(set_size-1)*(other_set_size-1)),
betw_scores/(1/2*other_set_size*(other_set_size-1)+1/2*(set_size-1)*(set_size-2)+(set_size-1)*(other_set_size-1)))
} else {
if (is.null(cutoff)) {
igraph::betweenness(graph = graph, v = igraph::V(graph), directed = directed, weights = weights, nobigint = nobigint, normalized = normalized)
} else {
igraph::estimate_betweenness(graph = graph, vids = igraph::V(graph), directed = directed, cutoff = cutoff, weights = weights, nobigint = nobigint)
}
}
}
11 changes: 11 additions & 0 deletions R/context.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
expect_nodes <- function() {
if (!.graph_context$free() && .graph_context$active() != 'nodes') {
stop('This call requires nodes to be active', call. = FALSE)
}
}

expect_edges <- function() {
if (!.graph_context$free() && .graph_context$active() != 'edges') {
stop('This call requires edges to be active', call. = FALSE)
}
}
19 changes: 18 additions & 1 deletion R/convert.R
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,28 @@
#' either as a data frame version of a matrix
#' or as an edgelist,
#' and returns an incidence matrix.
#' @name convert
#' @name convert
#' @param df A data frame containing an edgelist or
#' dataframe version of a matrix.
#'
#' If the data frame is a 2 column edgelist,
#' the first column will become the rows
#' and the second column will become the columns.
#'
#' If the data frame is a 3 column edgelist,
#' then the third column will be used as
#' the cell values or tie weights.
#'
#' If the data frame is more than 3 columns,
#' the first column is full of character strings (i.e. is named)
#' and the second column is numeric (e.g. 0 and 1)
#' then it will be assumed that this is a matrix
#' embedded in a data frame.
#' @examples
#' test <- data.frame(id1 = c("A","B","B","C","C"),
#' id2 = c("I","G","I","G","H"))
#' as_incidence_matrix(test)
#' @return An incidence matrix, named if possible.
#' @export
as_incidence_matrix <- function(df){
if(!is.data.frame(df)) stop("This function expects a data frame as input.")
Expand Down
2 changes: 1 addition & 1 deletion R/data_bristol.R
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,5 @@
#' \item{2_}{97 Bristol Civic Organizations}
#' \item{3_}{17 Major Protest and Civic Events in Bristol, 1990-2002}
#' }
#' @source Knoke, Christopoulos, Diani, and Hollway. 2020. Multimodal Political Networks. Cambridge University Press: Cambridge.
#' @source Knoke, Diani, Hollway, and Christopoulos. 2021. \emph{Multimodal Political Networks}. Cambridge University Press: Cambridge.
"mpn_bristol"
2 changes: 1 addition & 1 deletion R/data_evs.R
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
#' \item{Peace}{1 if individual associated}
#' \item{Health}{1 if individual associated}
#' }
#' @source Knoke, Christopoulos, Diani, and Hollway. 2020. Multimodal Political Networks. Cambridge University Press: Cambridge.
#' @source Knoke, Diani, Hollway, and Christopoulos. 2021. \emph{Multimodal Political Networks}. Cambridge University Press: Cambridge.
"mpn_IT_1990"

#' @rdname mpn_evs
Expand Down
Loading

0 comments on commit 44880df

Please sign in to comment.