Skip to content

Commit

Permalink
Merge pull request #260 from snlab-ch/develop
Browse files Browse the repository at this point in the history
v0.13.1
  • Loading branch information
jhollway authored Dec 5, 2022
2 parents a5a9fa5 + 2557238 commit 052b2a2
Show file tree
Hide file tree
Showing 18 changed files with 2,243 additions and 348 deletions.
4 changes: 2 additions & 2 deletions DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Package: migraph
Title: Tools for Multimodal Network Analysis
Version: 0.13.0
Date: 2022-11-30
Version: 0.13.1
Date: 2022-12-05
Description: A set of tools for analysing multimodal networks.
All functions operate with matrices, edge lists,
and 'igraph', 'network', and 'tidygraph' objects,
Expand Down
15 changes: 15 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,18 @@
# migraph 0.13.1

## Package

- Added hints, solutions, comments, and questions to equivalence and centrality tutorials
- Changed theme across all tutorials

## Makes

- Fixed bug in `read_pajek()` where multiple networks/ties were causing an issue for partition assignment

## Models

- Fixed bug in `play_diffusion()` relating to latency inversion

# migraph 0.13.0

## Package
Expand Down
61 changes: 40 additions & 21 deletions R/make_read.R
Original file line number Diff line number Diff line change
Expand Up @@ -150,31 +150,50 @@ write_nodelist <- function(object,
}

#' @describeIn read Reading pajek (.net/.paj) files
#' @param ties Where there are
#' @importFrom network read.paj
#' @importFrom utils read.delim
#' @export
read_pajek <- function(file = file.choose(), ...) {
out <- network::read.paj(file, ...)
if (!is.network(out)) out <- out[[1]][[1]]
out <- as_tidygraph(out)
if(grepl("Partition", utils::read.delim(file))){
clus <- strsplit(paste(utils::read.delim(file)), "\\*")[[1]]
clus <- clus[grepl("^Vertices|^Partition", clus)][-1]
if(length(clus) %% 2 != 0) stop("Unexpected .pajek file structure.")
namo <- clus[c(TRUE, FALSE)]
attr <- clus[c(FALSE, TRUE)]
for (i in seq_len(namo)){
vct <- strsplit(attr[i], ",")[[1]][-1]
vct <- gsub("\"", "", vct)
vct <- gsub(" ", "", vct, fixed = TRUE)
vct <- vct[!grepl("^$", vct)]
if(all(grepl("^-?[0-9.]+$", vct))) vct <- as.numeric(vct)
out <- add_node_attribute(out,
attr_name = strsplit(namo[i], " |\\.")[[1]][2],
vector = vct)
read_pajek <- function(file = file.choose(),
ties = NULL,
...) {
paj <- network::read.paj(file, ...)
if(!is.network(paj)){
if(is.null(ties))
stop(paste("This file contains multiple networks/ties.",
"Please choose a set of ties for the imported network among:\n",
paste0("- '", names(paj$networks), "'", collapse = "\n "),
"\n by adding the name as a character string to the `ties = ` argument"))
out <- paj[[1]][[ties]]
if("partitions" %in% names(paj)){
for(x in names(paj$partitions)){
out <- add_node_attribute(out,
attr_name = gsub(".clu","",x),
vector = paj$partitions[,x])
}
}
}
as_tidygraph(out)
out <- as_tidygraph(out)
} else {
out <- as_tidygraph(paj)
}
# if(grepl("Partition", utils::read.delim(file))){
# clus <- strsplit(paste(utils::read.delim(file)), "\\*")[[1]]
# clus <- clus[grepl("^Vertices|^Partition", clus)][-1]
# if(length(clus) %% 2 != 0) stop("Unexpected .pajek file structure.")
# namo <- clus[c(TRUE, FALSE)]
# attr <- clus[c(FALSE, TRUE)]
# for (i in seq_len(namo)){
# vct <- strsplit(attr[i], ",")[[1]][-1]
# vct <- gsub("\"", "", vct)
# vct <- gsub(" ", "", vct, fixed = TRUE)
# vct <- vct[!grepl("^$", vct)]
# if(all(grepl("^-?[0-9.]+$", vct))) vct <- as.numeric(vct)
# out <- add_node_attribute(out,
# attr_name = strsplit(namo[i], " |\\.")[[1]][2],
# vector = vct)
# }
# }
out
}

#' @describeIn read Writing pajek .net files
Expand Down
7 changes: 6 additions & 1 deletion R/make_play.R → R/model_play.R
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@
#' number of nodes in the network.
#' @param thresholds A numeric vector indicating the thresholds
#' each node has. By default 1.
#' A single number means a generic threshold;
#' for thresholds that vary among the population please use a vector
#' the length of the number of nodes in the network.
#' If 1 or larger, the threshold is interpreted as a simple count
#' of the number of contacts/exposures sufficient for infection.
#' If less than 1, the threshold is interpreted as complex,
#' where the threshold concerns the proportion of contacts.
#' @param transmissibility A proportion indicating the transmission rate,
Expand Down Expand Up @@ -112,7 +117,7 @@ play_diffusion <- function(object,
exposed <- c(exposed, new)

# new list of infected
infectious <- exposed[stats::rbinom(length(exposed), 1, latency)==1]
infectious <- exposed[stats::rbinom(length(exposed), 1, latency)==0]
exposed <- setdiff(exposed, infectious)
infected <- c(infected, infectious)
# tick time
Expand Down
6 changes: 6 additions & 0 deletions README.Rmd
Original file line number Diff line number Diff line change
Expand Up @@ -253,3 +253,9 @@ other excellent R packages such as
and [`{tnet}`](https://toreopsahl.com/tnet/),
and implements many additional features currently only available outside the R ecosystem
in packages such as [**UCINET**](https://sites.google.com/site/ucinetsoftware/download?authuser=0).

## Funding details

Subsequent work on this package has been funded by the Swiss National Science Foundation (SNSF)
[Grant Number 188976](https://data.snf.ch/grants/grant/188976):
"Power and Networks and the Rate of Change in Institutional Complexes" (PANARCHIC).
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -277,3 +277,10 @@ available in other excellent R packages such as
features currently only available outside the R ecosystem in packages
such as
[**UCINET**](https://sites.google.com/site/ucinetsoftware/download?authuser=0).

## Funding details

Subsequent work on this package has been funded by the Swiss National
Science Foundation (SNSF) [Grant Number
188976](https://data.snf.ch/grants/grant/188976): “Power and Networks
and the Rate of Change in Institutional Complexes” (PANARCHIC).
6 changes: 4 additions & 2 deletions inst/tutorials/tutorial2/visualisation.Rmd
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
---
title: "Visualisation"
author: "James Hollway"
output: learnr::tutorial
author: "by James Hollway"
output:
learnr::tutorial:
theme: journal
runtime: shiny_prerendered
---

Expand Down
145 changes: 137 additions & 8 deletions inst/tutorials/tutorial3/centrality.Rmd
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
---
title: "Centrality"
author: "James Hollway"
output: learnr::tutorial
author: "by James Hollway"
output:
learnr::tutorial:
theme: journal
runtime: shiny_prerendered
---

Expand All @@ -23,6 +25,20 @@ to be compatible with other packages.
```

```{r coercion-hint-1}
# Let's graph both datasets
autographr(____)
autographr(____)
```

```{r coercion-hint-2}
# Now, let's look at "ison_brandes" as a matrix
# using the as_matrix() function
(mat <- as_matrix(____))
```

```{r coercion-solution}
autographr(ison_brandes)
autographr(ison_brandes2)
Expand All @@ -38,6 +54,27 @@ This makes plotting the network just a wee bit more accessible and interpretable
```

```{r addingnames-hint-1}
# Let's use the to_named() function to assign
# the networks data to a relevant object name
ison_brandes <- to_named(____)
ison_brandes2 <- to_named(____)
```

```{r addingnames-hint-2}
ison_brandes <- to_named(ison_brandes)
ison_brandes2 <- to_named(ison_brandes2)
```

```{r addingnames-hint-3}
# Now, let's graph using the object names: "ison_brandes"
autographr(____)
```

```{r addingnames-solution}
ison_brandes <- to_named(ison_brandes)
ison_brandes2 <- to_named(ison_brandes2)
Expand All @@ -54,14 +91,36 @@ Just sum the rows or columns of the matrix!
```

```{r degreesum-hint-1}
# We can calculate degree centrality like this:
(degrees <- rowSums(mat))
rowSums(mat) == colSums(mat)
```

```{r degreesum-hint-2}
# Or by using a built in command in migraph like this:
node_degree(ison_brandes, normalized = FALSE)
```

```{r degreesum-solution}
(degrees <- rowSums(mat))
rowSums(mat) == colSums(mat)
# Are they all equal? Why?
# You can also just use a built in command in migraph though:
node_degree(ison_brandes, normalized = FALSE)
```

```{r degreesum-Q, echo=FALSE}
question("Are the row sums the same as the column sums?",
answer("Yes",
correct = TRUE,
message = "That's right, that's because this is an undirected network."),
answer("No"),
allow_retry = FALSE
)
```

Often we are interested in the distribution of (degree) centrality in a network.
`{migraph}` offers a way to get a pretty good first look at this distribution,
though there are more elaborate ways to do this in base and grid graphics.
Expand All @@ -79,9 +138,31 @@ Other measures of centrality can be a little trickier to calculate by hand.
Fortunately, we can use functions from `{migraph}` to help:

```{r micent, exercise = TRUE}
# Let's explore this using the "ison_brandes" dataset
```

```{r micent-hint-1}
# Use the node_betweenness() function to calculate the
# betweenness centralities of nodes in a network
node_betweenness(ison_brandes)
```

```{r micent-hint-2}
# Use the node_closeness() function to calculate the
# closeness centrality of nodes in a network
node_closeness(ison_brandes)
```

```{r micent-hint-3}
# Use the node_eigenvector() function to calculate
# the eigenvector centrality of nodes in a network
node_eigenvector(ison_brandes)
```

```{r micent-solution}
node_betweenness(ison_brandes)
node_closeness(ison_brandes)
Expand Down Expand Up @@ -112,12 +193,15 @@ we can highlight which node or nodes hold the maximum score in red.
ison_brandes %>%
add_node_attribute("color", node_is_max(node_degree(ison_brandes))) %>%
autographr(node_color = "color")
ison_brandes %>%
add_node_attribute("color", node_is_max(node_betweenness(ison_brandes))) %>%
autographr(node_color = "color")
ison_brandes %>%
add_node_attribute("color", node_is_max(node_closeness(ison_brandes))) %>%
autographr(node_color = "color")
ison_brandes %>%
add_node_attribute("color", node_is_max(node_eigenvector(ison_brandes))) %>%
autographr(node_color = "color")
Expand All @@ -126,13 +210,48 @@ ison_brandes %>%
How neat! Try it with the two-mode version.
What can you see?

```{r ggid_twomode, exercise = TRUE}
# Instead of "ison_brandes", use "ison_brandes2"
```

```{r ggid_twomode-solution}
ison_brandes2 %>%
add_node_attribute("color", node_is_max(node_degree(ison_brandes2))) %>%
autographr(node_color = "color")
ison_brandes2 %>%
add_node_attribute("color", node_is_max(node_betweenness(ison_brandes2))) %>%
autographr(node_color = "color")
ison_brandes2 %>%
add_node_attribute("color", node_is_max(node_closeness(ison_brandes2))) %>%
autographr(node_color = "color")
ison_brandes2 %>%
add_node_attribute("color", node_is_max(node_eigenvector(ison_brandes2))) %>%
autographr(node_color = "color")
```

```{r brandes2quiz}
question("Select all that are true.",
answer("Only one node is selected in each plot."),
answer("The maximum degree square has a higher degree than the maximum degree circle(s).",
correct = TRUE),
answer("No node is ever the most central according to two or more different centrality measures."),
allow_retry = TRUE,
random_answer_order = TRUE)
```

## Calculating centralization

`{migraph}` also implements centralization functions.
Here we are no longer interested in the level of the node,
but in the level of the whole graph, so the syntax is:

```{r centzn, exercise = TRUE}
# We will now look at the same centralization measures for the entire graph or network by
# calling the same functions as those used for nodes but instead of "node_", replace it with "network_"
```

Expand Down Expand Up @@ -178,13 +297,23 @@ ge <- autographr(ison_brandes, node_color = "eigenvector") +
# ggsave("brandes-centralities.pdf")
```

## Tasks

1. Try this with the `ison_brandes2` dataset in the package.
```{r centzdq}
question("How centralized is the ison_brandes network? Select all that apply.",
answer("It is more degree centralised than betweenness centralised.",
message = "Degree centralisation is at 0.18 for this network whereas betweenness centralisation is at 0.32."),
answer("It is more closeness centralised than betweenness centralised.",
message = "Closeness centralisation is at 0.23 for this network whereas betweenness centralisation is at 0.32."),
answer("It is more eigenvector centralised than betweenness centralised.",
correct = TRUE,
message = "That's right, eigenvector centralisation is at 0.48 for this network whereas betweenness centralisation is at 0.32."),
random_answer_order = TRUE,
allow_retry = TRUE)
```

2. Name a plausible research question you could ask of this data
## Tasks

1. Name a plausible research question you could ask of this data
for each of the four main centrality measures
(degree, betweenness, closeness, eigenvector)
You may want to add these as titles or subtitles to each plot.

3. How centralized is the network?
Loading

0 comments on commit 052b2a2

Please sign in to comment.