---
title: "Centrality"
author: "by James Hollway"
output: 
  learnr::tutorial:
    theme: journal
runtime: shiny_prerendered
---

```{r setup, include=FALSE}
library(learnr)
library(manynet)
library(migraph)
library(patchwork)
knitr::opts_chunk$set(echo = FALSE)
```

## Calculating different centrality measures

For this exercise, we'll use the `ison_brandes` and `ison_brandes2` datasets in `{manynet}`.
The `ison_brandes2` is a two-mode version of the `ison_brandes` dataset.
This dataset is in a 'tidygraph' format, 
but `manynet` makes it easy to coerce this into other forms
to be compatible with other packages.

```{r coercion, exercise = TRUE}

```

```{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)
(mat <- as_matrix(ison_brandes))
```

The network is anonymous, but I think it would be nice to add some names,
even if it's just pretend. 
Luckily, `{manynet}` has a function for this.
This makes plotting the network just a wee bit more accessible and interpretable:

```{r addingnames, exercise = TRUE}

```

```{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)
autographr(ison_brandes)
```

Note that you will likely get a different set of names,
as they are assigned randomly from a pool of (American) first names.

Let's start with calculating degree, as it is easy to calculate yourself.
Just sum the rows or columns of the matrix!

```{r degreesum, exercise = TRUE}

```

```{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)
# 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.

```{r distrib, exercise = TRUE}

```

```{r distrib-solution}
plot(node_degree(ison_brandes), "h") +
  plot(node_degree(ison_brandes), "d")
```

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)
node_eigenvector(ison_brandes)
# TASK: Can you create degree distributions for each of these?
```

Note that all centrality measures in `{migraph}` return normalized
scores by default -- 
for the raw scores, just add `normalized = FALSE` as an extra argument.

## Plotting different centrality measures

It is straightforward in `{migraph}` to highlight nodes and ties 
with maximum or minimum (e.g. degree) scores.
If the vector is numeric (i.e. a "measure"),
then this can be easily converted into a logical vector that
identifies the node/tie with the maximum/minimum score using
e.g. `node_is_max()` or `tie_is_min()`.
By passing this attribute to the `autographr()` argument "node_color"
we can highlight which node or nodes hold the maximum score in red.

```{r ggid, exercise = TRUE}

```

```{r ggid-solution}
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")
```

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_"

```

```{r centzn-solution}
network_degree(ison_brandes)
network_betweenness(ison_brandes)
network_closeness(ison_brandes)
network_eigenvector(ison_brandes)
```

By default, scores are printed to 3 decimal places,
but this can be modified and, in any case, 
the unrounded values are retained internally and passed on.

Note that for centralization in two-mode networks,
two values are given (as a named vector),
since normalization typically depends on the (asymmetric)
number of nodes in each mode.

What if we want to have a single image/figure with multiple plots?
This can be a little tricky with gg-based plots,
but fortunately the `{patchwork}` package is here to help.

```{r multiplot, exercise = TRUE}

```

```{r multiplot-solution}
ison_brandes <- ison_brandes %>%
  add_node_attribute("degree",
                              node_is_max(node_degree(ison_brandes))) %>%
  add_node_attribute("betweenness",
                              node_is_max(node_betweenness(ison_brandes))) %>%
  add_node_attribute("closeness",
                              node_is_max(node_closeness(ison_brandes))) %>%
  add_node_attribute("eigenvector",
                              node_is_max(node_eigenvector(ison_brandes)))
gd <- autographr(ison_brandes, node_color = "degree") + 
  ggtitle("Degree", subtitle = round(network_degree(ison_brandes), 2))
gc <- autographr(ison_brandes, node_color = "closeness") + 
  ggtitle("Closeness", subtitle = round(network_closeness(ison_brandes), 2))
gb <- autographr(ison_brandes, node_color = "betweenness") + 
  ggtitle("Betweenness", subtitle = round(network_betweenness(ison_brandes), 2))
ge <- autographr(ison_brandes, node_color = "eigenvector") + 
  ggtitle("Eigenvector", subtitle = round(network_eigenvector(ison_brandes), 2))
(gd | gb) / (gc | ge)
# ggsave("brandes-centralities.pdf")
```


```{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)
```

## 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.
