Skip to content

Commit 13b2944

Browse files
authored
Merge pull request #310 from stocnet/develop
v1.5.6
2 parents cafe397 + 2d4683f commit 13b2944

6 files changed

Lines changed: 477 additions & 538 deletions

File tree

DESCRIPTION

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
Package: migraph
22
Title: Inferential Methods for Multimodal and Other Networks
3-
Version: 1.5.5
4-
Date: 2025-11-12
3+
Version: 1.5.6
4+
Date: 2025-11-19
55
Description: A set of tools for testing networks.
66
It includes functions for univariate and multivariate
77
conditional uniform graph and quadratic assignment procedure testing,

NEWS.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,12 @@
1+
# migraph 1.5.6
2+
3+
2025-11-19
4+
5+
## Tutorials
6+
7+
- Updated diffusion tutorial moving from `grapht()` to `graphs()` in places
8+
- Added gifs to diffusion tutorial
9+
110
# migraph 1.5.5
211

312
2025-11-12

cran-comments.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,4 @@
99

1010
0 errors | 0 warnings | 0 notes
1111

12-
- Fixed note in fedora and debian flavors about pixel widths in documentation
12+
- Fixed CRAN errors

inst/tutorials/tutorial7/diffusion.Rmd

Lines changed: 72 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,7 @@ description: >
1313

1414
```{r setup, include = FALSE}
1515
library(learnr)
16-
library(manynet)
17-
library(patchwork)
18-
library(ggplot2)
16+
library(migraph)
1917
clear_glossary()
2018
knitr::opts_chunk$set(echo = FALSE)
2119
```
@@ -47,6 +45,8 @@ is enough to cause infection/adoption.
4745
If the network is connected, the attribute will 'cascade' across the network
4846
until all nodes are 'infected'/have adopted.
4947

48+
<img src="https://media.giphy.com/media/v1.Y2lkPTc5MGI3NjExdXdqOXNjemNnZnhzOG50aTdlZzgzbWt2dTlmbGt4aGswNGRjdjd4eiZlcD12MV9naWZzX3NlYXJjaCZjdD1n/11IuynebQzNpjq/giphy.gif" alt="salt cascade gif" width = "500"/>
49+
5050
### Diffusing across a lattice
5151

5252
Let us begin with a lattice network that shows us exactly how such a cascade works.
@@ -65,92 +65,84 @@ graphr(lat)
6565

6666
Ok, great! That's made a nice little lattice network.
6767
Next we want to play a diffusion process _on_ this network.
68-
To do this, we just need to run `play_diffusion()`,
69-
and assign the result.
70-
Then we can investigate the resulting object in a few ways.
71-
The first way is simply to print the result by calling the object.
72-
The other way is to unpack the result by calling for its summary.
68+
To do this, we need to run `play_diffusion()`.
69+
This function plays a simple contagion process on a network,
70+
where any contact with an infected/adopter node
71+
is enough to cause susceptible nodes to adopt/become infected by the following timepoint.
72+
The function returns a network object that contains information about how the diffusion played out.
73+
Then we can investigate the diffusion process by extracting this information
74+
using `as_changelist()` and `as_diffusion()`.
75+
The first simply extracts the list of adoption/infection events,
76+
while the second summarises the diffusion process by time point.
7377

7478
```{r lat_diff, exercise = TRUE, exercise.setup = "clattice"}
75-
lat_diff <- play_diffusion(lat)
76-
lat_diff
77-
summary(lat_diff)
79+
lat_w_diff <- play_diffusion(lat)
80+
(diff_events <- as_changelist(lat_w_diff))
81+
(diff_summ <- as_diffusion(lat_w_diff))
7882
```
7983

80-
The main report from the `lat_diff` object shows the number of nodes that don't
81-
(yet) have the attribute (S for susceptible, but can also be non-adopter)
82-
and those that do have the attribute (I for infected, or adopter) at each time point (t).
83-
We can see a steady growth here, except for a slower initialisation and winding down.
84-
85-
The secondary report, `summary(lat_diff)`,
86-
presents a list of the events at each time point.
84+
The changelist presents a list of the events at each time point.
8785
In the event variable, the 'I' indicates that these are all infection/adoption events.
8886
Where 't' is 0, that means that these are the seeds producing the starting condition
8987
for the diffusion.
90-
The final column, 'exposure', records the number of infected nodes that the adopting
91-
node was exposed to when it adopted.
92-
Note that we have no information about the exposure for the seed nodes when they
93-
were infected, and so this is a missing value.
94-
The exposure at infection is recorded here to accelerate later analysis.
88+
The diffusion summary shows the number of nodes that don't
89+
(yet) have the attribute (S for susceptible, but can also be non-adopter)
90+
and those that do have the attribute (I for infected, or adopter) at each time point (t).
91+
We can see a steady growth here, except for a slower initialisation and winding down.
9592

9693
### Visualising cascades
9794

9895
We have several different options for visualising diffusions.
9996
The first visualisation option that we have is to plot the diffusion result itself.
10097

101-
```{r plotlat, exercise = TRUE, exercise.setup = "lat_diff", purl = FALSE, fig.width=9}
102-
plot(lat_diff)
103-
plot(lat_diff, all_steps = FALSE)
98+
```{r plotlat, exercise = TRUE, exercise.setup = "lat_diff", fig.width=9}
99+
plot(diff_summ)
100+
# plot(diff_summ, all_steps = FALSE) # To plot only 'where the action is', use the argument `all_steps = FALSE`.
104101
```
105102

106103
This plot effectively visualises what we observed from the print out of the
107-
`lat_diff` object above.
104+
`diff_summ` object above.
108105
The red line traces the proportion of infected;
109-
the blue line the (inverse) proportion of susceptible.
106+
the blue line the (inverse) proportion of susceptible nodes in the network.
110107
The grey histogram in the plot shows how many nodes are newly 'infected' at each
111108
time point, or the so-called 'force of infection' ($F = \beta I$).
112109

113-
We can see that by default the whole simulated period (32 steps) is shown,
114-
even though there is complete infection after only 10 steps.
115-
That's because the simulation runs over the number of nodes in the network
116-
by default.
117-
If the structure is amenable to diffusion, infection/diffusion will be completed
118-
before that.
119-
To plot only 'where the action is', use the argument `all_steps = FALSE`.
120-
121110
But maybe we want to also/instead view the diffusion on the actual network.
122-
Here we can use all the three main graphing techniques offered in `{manynet}`.
111+
Here we can use all the three main graphing techniques offered in `{autograph}`.
123112
First, `graphr()` will graph a static network where the nodes are coloured
124113
according to how far through the diffusion process the node adopted.
125114
Note also that any seeds are indicated with a triangle.
126115

127-
```{r graphrlat, exercise = TRUE, exercise.setup = "lat_diff", purl = FALSE, fig.width=9}
128-
graphr(lat_diff, node_size = 0.3)
116+
```{r graphrlat, exercise = TRUE, exercise.setup = "lat_diff", fig.width=9}
117+
graphr(lat_w_diff, node_size = 0.3)
129118
```
130119

131120
Second, `graphs()` visualises the stages of the diffusion on the network.
132121
By default it will graph the first and last wave,
133122
but we can change this by specifying which waves to graph.
134123

135-
```{r graphslat, exercise = TRUE, exercise.setup = "lat_diff", purl = FALSE, fig.width=9}
136-
graphs(lat_diff)
137-
graphs(lat_diff, waves = c(1,4,8))
124+
```{r graphslat, exercise = TRUE, exercise.setup = "lat_diff", fig.width=9}
125+
graphs(lat_w_diff)
126+
graphs(lat_w_diff, waves = c(1,4,7,10))
138127
```
139128

129+
We can see here exactly how the attribute in question (ideas, information, disease?)
130+
is diffusing across the network.
131+
It's like a cascade of red sweeping across the space!
132+
140133
Lastly, `grapht()` animates this diffusion process to a gif.
141134
It can take a little time to encode, but it is worth it to see exactly
142135
how the attribute is diffusing across the network!
143136
Note that if you run this code in the console, you get a calming progress bar;
144137
in the tutorial you will just need to be patient.
145138

139+
> NB: It is not currently working (for diffusions) at the moment,
140+
but a refactoring soon will fix this and other related bugs.
141+
146142
```{r graphtlat, exercise = TRUE, exercise.setup = "lat_diff", purl = FALSE, fig.width=9}
147-
grapht(lat_diff, node_size = 10)
143+
# grapht(lat_w_diff, node_size = 10)
148144
```
149145

150-
We can see here exactly how the attribute in question (ideas, information, disease?)
151-
is diffusing across the network.
152-
It's like a cascade of red sweeping across the space!
153-
154146
### Varying network structure
155147

156148
While a lattice structure is one way of representing spatially governed diffusion,
@@ -177,8 +169,8 @@ graphr(play_diffusion(generate_smallworld(32, 0.025)))
177169
Which diffusion process completed first?
178170
`graphr()` only colors nodes' relative adoption,
179171
and `graphs()` (at least by default) only graphs the first and last step.
180-
`grapht()` will show if and when there is complete infection,
181-
but we need to sit through each 'movie'.
172+
<!-- `grapht()` will show if and when there is complete infection, -->
173+
<!-- but we need to sit through each 'movie'. -->
182174
But there is an easier way.
183175
Play these same diffusions again, this time nesting the call within `net_infection_complete()`.
184176

@@ -211,7 +203,6 @@ You can start the infection in California by specifying `seeds = 5`.
211203
us_diff <- play_diffusion(irps_usgeo, seeds = 5)
212204
plot(us_diff)
213205
graphr(us_diff)
214-
grapht(us_diff)
215206
net_infection_complete(us_diff)
216207
```
217208

@@ -227,6 +218,8 @@ This is known as a `r gloss("linear threshold", "LTM")` model,
227218
where if infection/influence on a node through some (potentially weighted) network
228219
exceeds some threshold, then they will adopt/become infected.
229220

221+
<img src="https://media.giphy.com/media/v1.Y2lkPTc5MGI3NjExZ3luZjc1aWhnYnhwNG9zazkzcjA2c2hiYzBjZXVlaTlvZnh6MHl2OCZlcD12MV9naWZzX3NlYXJjaCZjdD1n/2zowpb8JizmCRHq0PR/giphy.gif" alt="morgan freeman breaking point gif" width = "500"/>
222+
230223
### Threshold rising
231224

232225
Let's use the ring network again this time to
@@ -243,9 +236,9 @@ Let's see what the results are if you play four different diffusions:
243236

244237
```{r complex, exercise = TRUE, fig.width=9}
245238
rg <- create_ring(32, width = 2)
246-
plot(play_diffusion(rg, seeds = 1, thresholds = 1))/
247-
plot(play_diffusion(rg, seeds = 1, thresholds = 2))/
248-
plot(play_diffusion(rg, seeds = 1:2, thresholds = 2))/
239+
plot(play_diffusion(rg, seeds = 1, thresholds = 1))
240+
plot(play_diffusion(rg, seeds = 1, thresholds = 2))
241+
plot(play_diffusion(rg, seeds = 1:2, thresholds = 2))
249242
plot(play_diffusion(rg, seeds = c(1,16), thresholds = 2))
250243
```
251244

@@ -303,14 +296,14 @@ on the scale-free networks we have been using here.
303296
```
304297

305298
```{r sfprop-hint, purl = FALSE}
306-
plot(play_diffusion(generate_scalefree(32, 0.025), seeds = 1:2, thresholds = ____, steps = ____))/
307-
plot(play_diffusion(generate_scalefree(32, 0.025), seeds = 1:2, thresholds = ____, steps = ____))/
299+
plot(play_diffusion(generate_scalefree(32, 0.025), seeds = 1:2, thresholds = ____, steps = ____))
300+
plot(play_diffusion(generate_scalefree(32, 0.025), seeds = 1:2, thresholds = ____, steps = ____))
308301
plot(play_diffusion(generate_scalefree(32, 0.025), seeds = 1:2, thresholds = ____, steps = ____))
309302
```
310303

311304
```{r sfprop-solution}
312-
plot(play_diffusion(generate_scalefree(32, 0.025), seeds = 1:2, thresholds = 0.1, steps = 10))/
313-
plot(play_diffusion(generate_scalefree(32, 0.025), seeds = 1:2, thresholds = 0.25, steps = 10))/
305+
plot(play_diffusion(generate_scalefree(32, 0.025), seeds = 1:2, thresholds = 0.1, steps = 10))
306+
plot(play_diffusion(generate_scalefree(32, 0.025), seeds = 1:2, thresholds = 0.25, steps = 10))
314307
plot(play_diffusion(generate_scalefree(32, 0.025), seeds = 1:2, thresholds = 0.5, steps = 10))
315308
```
316309

@@ -337,17 +330,14 @@ Something is going around Middle-Earth,
337330
but different races have different resistances (i.e. thresholds).
338331
Let us say that there is a clear ordering to this.
339332

340-
```{r lotr-resist, exercise=TRUE}
333+
```{r lotr-resist, exercise=TRUE, fig.width=9}
341334
lotr_resist <- fict_lotr %>% mutate(resistance = dplyr::case_when(Race == "Dwarf" ~ 2,
342335
Race == "Elf" ~ 4,
343336
Race == "Ent" ~ 5,
344337
Race == "Hobbit" ~ 3,
345338
Race == "Human" ~ 1,
346339
Race == "Maiar" ~ 6))
347-
```
348-
349-
```{r resistdiff, exercise=TRUE, exercise.setup = "lotr-resist", fig.width=9}
350-
grapht(play_diffusion(lotr_resist, thresholds = "resistance"))
340+
graphs(play_diffusion(lotr_resist, thresholds = "resistance"))
351341
```
352342

353343
Fun! Now how would you interpret what is going on here?
@@ -362,12 +352,14 @@ Can you rewrite the code above so that fractional thresholds are used?
362352

363353
<!-- ## Independent cascades -->
364354

365-
## Intervention
355+
## Make it start
366356

367357
Let's say that you have developed an exciting new policy and
368358
you are keen to maximise how quickly and thoroughly it is adopted.
369359
We are interested here in `r gloss("network intervention", "intervention")`.
370360

361+
<img src="https://media.giphy.com/media/v1.Y2lkPTc5MGI3NjExbm9rc2trbGd5b2tzZ2d4cDNqeGVvNzJ3ZjhvMHhmdDF3c2ljcDZ5ZSZlcD12MV9naWZzX3NlYXJjaCZjdD1n/5nsiFjdgylfK3csZ5T/giphy.gif" alt="flamethrower gif" width = "500"/>
362+
371363
### Choosing where to seed
372364

373365
Since the ring network we constructed is cyclical,
@@ -382,8 +374,8 @@ and see whether the result is any different.
382374
```
383375

384376
```{r ring2-solution}
385-
plot(play_diffusion(create_ring(32, width = 2), seeds = 1)) /
386-
plot(play_diffusion(create_ring(32, width = 2), seeds = 16))
377+
plot(play_diffusion(create_ring(32, width = 2), seeds = 1))
378+
plot(play_diffusion(create_ring(32, width = 2), seeds = 16))
387379
```
388380

389381
```{r ring2-interp, echo = FALSE, purl = FALSE}
@@ -458,14 +450,14 @@ one with the first node as seed and again one on the middle.
458450
```
459451

460452
```{r lattice-solution}
461-
plot(play_diffusion(lat, seeds = 1))/
453+
plot(play_diffusion(lat, seeds = 1))
462454
plot(play_diffusion(lat, seeds = 16))
463455
lat %>%
464456
add_node_attribute("color", c(1, rep(0, 14), 2, rep(0, 16))) %>%
465457
graphr(node_color = "color")
466458
467459
# visualise diffusion in lattice graph
468-
grapht(play_diffusion(lat, seeds = 16), layout = "grid", keep_isolates = FALSE)
460+
graphs(play_diffusion(lat, seeds = 16), layout = "grid")
469461
```
470462

471463
```{r lattice-interp, echo = FALSE, purl = FALSE}
@@ -501,14 +493,14 @@ sf %>%
501493
```
502494

503495
```{r scale-solution}
504-
plot(play_diffusion(sf, seeds = 10, steps = 10)) /
505-
plot(play_diffusion(sf, seeds = node_is_random(sf), steps = 10)) /
506-
plot(play_diffusion(sf, seeds = node_is_max(node_degree(sf)), steps = 10)) /
496+
plot(play_diffusion(sf, seeds = 10, steps = 10))
497+
plot(play_diffusion(sf, seeds = node_is_random(sf), steps = 10))
498+
plot(play_diffusion(sf, seeds = node_is_max(node_degree(sf)), steps = 10))
507499
plot(play_diffusion(sf, seeds = node_is_min(node_degree(sf)), steps = 10))
508500
509501
# visualise diffusion in scalefree network
510502
graphs(play_diffusion(sf, seeds = node_is_min(node_degree(sf)), steps = 10))
511-
grapht(play_diffusion(sf, seeds = 16, steps = 10))
503+
graphs(play_diffusion(sf, seeds = 16, steps = 10), waves = 1:3)
512504
```
513505

514506
```{r mindeg-interp, echo = FALSE, purl = FALSE}
@@ -566,6 +558,8 @@ But before we get into that,
566558
let's see how we can play and plot several simulations
567559
to see what the range of outcomes might be like.
568560

561+
<img src="https://media.giphy.com/media/v1.Y2lkPTc5MGI3NjExMXMwZ2xzcWJzNG1ubXp1YXhvbG12YmE0NzQzNDFwa3drdGd1bmdoYSZlcD12MV9naWZzX3NlYXJjaCZjdD1n/xUOxeZc41DVT2l9laU/giphy.gif" alt="curb your enthusiasm luggage compartment gif" width = "500"/>
562+
569563
### Running multiple simulations
570564

571565
To do this, we need to use `play_diffusions()` (note the plural).
@@ -698,14 +692,16 @@ set.seed(123)
698692
plot(play_diffusion(rando, seeds = 10, latency = 0.25, recovery = 0.2))
699693
700694
# visualise diffusion with latency and recovery
701-
grapht(play_diffusion(rando, seeds = 10, latency = 0.25, recovery = 0.2))
695+
graphs(play_diffusion(rando, seeds = 10, latency = 0.25, recovery = 0.2), waves = c(1,5,10))
702696
```
703697

704-
### Make it stop
698+
## Make it stop
705699

706700
In this section, we are interested in how to most effectively _halt_
707701
a diffusion process.
708702

703+
<img src="https://media.giphy.com/media/v1.Y2lkPWVjZjA1ZTQ3N3JsNWhvMjM2amkwMnhqZ2ltb2h2c3Zha3dwOGx1dHZnbngzNTQxZyZlcD12MV9naWZzX3NlYXJjaCZjdD1n/LbvdcrbTOk6eK1kQ6u/giphy.gif" alt="biden stop gif" width = "500"/>
704+
709705
An attribute's reproduction number, or $R_0$, is a measure of the rate of infection
710706
or how quickly that attribute will reproduce period over period.
711707
It is calculated as $R_0 = \min\left(\frac{T}{1/L}, \bar{k}\right)$,
@@ -762,7 +758,7 @@ But then...
762758
### How many people do we need to vaccinate?
763759

764760
We can identify how many people need to be vaccinated through
765-
the `gloss("Herd Immunity Threshold", "hit")` or HIT.
761+
the `r gloss("Herd Immunity Threshold", "hit")` or HIT.
766762
HIT indicates the threshold at which the reduction of susceptible members
767763
of the network means that infections will no longer keep increasing,
768764
allowing herd immunity to be achieved.
@@ -849,6 +845,8 @@ usually rely on nodes' voluntary participation:
849845
they must accept that vaccination, medication, or behavioral change is necessary
850846
to combat the contagion.
851847

848+
<img src="https://media.giphy.com/media/v1.Y2lkPWVjZjA1ZTQ3bXdtbGs1azJnZWUzdGZycTRsMGg0a2twM2RxYjdveWtuNzV3eWo1MiZlcD12MV9naWZzX3NlYXJjaCZjdD1n/xjQfDCSRr2jkH3SPab/giphy.gif" alt="learn every day gif" width = "500"/>
849+
852850
Lastly, we're going to consider a rather simple type of learning model:
853851
a DeGroot learning model.
854852
A question often asked of these kinds of models is whether,

0 commit comments

Comments
 (0)