From 2bd85d2f2b6c6bccf6c8732231052699a8a64ede Mon Sep 17 00:00:00 2001 From: Callum Arnold <24391445+arnold-c@users.noreply.github.com> Date: Mon, 6 Jul 2026 11:42:03 -0400 Subject: [PATCH] diagram: update links to r session 2 and code-fold diagram chunks --- _book/day-1-interactive-session.html | 801 ++++++++++-------- _book/r-session-02.html | 114 +-- .../figure-html/unnamed-chunk-10-1.png | Bin 85750 -> 85761 bytes .../figure-html/unnamed-chunk-11-1.png | Bin 65650 -> 65649 bytes .../figure-html/unnamed-chunk-15-1.png | Bin 170540 -> 170532 bytes .../figure-html/unnamed-chunk-17-1.png | Bin 99127 -> 99127 bytes .../figure-html/unnamed-chunk-19-1.png | Bin 51323 -> 51310 bytes .../figure-html/unnamed-chunk-22-1.png | Bin 120488 -> 120521 bytes .../figure-html/unnamed-chunk-29-1.png | Bin 110430 -> 110427 bytes .../figure-html/unnamed-chunk-3-1.png | Bin 50446 -> 50547 bytes .../figure-html/unnamed-chunk-33-1.png | Bin 72096 -> 72092 bytes .../figure-html/unnamed-chunk-35-1.png | Bin 60663 -> 60644 bytes .../figure-html/unnamed-chunk-62-1.png | Bin 54222 -> 52490 bytes .../figure-html/unnamed-chunk-68-1.png | Bin 124020 -> 124024 bytes .../figure-html/unnamed-chunk-71-1.png | Bin 258096 -> 258102 bytes .../figure-html/unnamed-chunk-72-1.png | Bin 321211 -> 321214 bytes .../figure-html/unnamed-chunk-74-1.png | Bin 167872 -> 167848 bytes .../figure-html/unnamed-chunk-76-1.png | Bin 312069 -> 312105 bytes .../figure-html/unnamed-chunk-78-1.png | Bin 111313 -> 111306 bytes .../figure-html/unnamed-chunk-8-1.png | Bin 123073 -> 123062 bytes .../figure-html/unnamed-chunk-80-1.png | Bin 61111 -> 61098 bytes _book/r-session-03.html | 166 ++-- _book/schedule.html | 2 +- _book/search.json | 97 ++- .../execute-results/html.json | 4 +- .../r-session-02/execute-results/html.json | 4 +- .../figure-html/unnamed-chunk-10-1.png | Bin 85750 -> 85761 bytes .../figure-html/unnamed-chunk-11-1.png | Bin 65650 -> 65649 bytes .../figure-html/unnamed-chunk-15-1.png | Bin 170540 -> 170532 bytes .../figure-html/unnamed-chunk-17-1.png | Bin 99127 -> 99127 bytes .../figure-html/unnamed-chunk-19-1.png | Bin 51323 -> 51310 bytes .../figure-html/unnamed-chunk-22-1.png | Bin 120488 -> 120521 bytes .../figure-html/unnamed-chunk-29-1.png | Bin 110430 -> 110427 bytes .../figure-html/unnamed-chunk-3-1.png | Bin 50446 -> 50547 bytes .../figure-html/unnamed-chunk-33-1.png | Bin 72096 -> 72092 bytes .../figure-html/unnamed-chunk-35-1.png | Bin 60663 -> 60644 bytes .../figure-html/unnamed-chunk-62-1.png | Bin 54222 -> 52490 bytes .../figure-html/unnamed-chunk-68-1.png | Bin 124020 -> 124024 bytes .../figure-html/unnamed-chunk-71-1.png | Bin 258096 -> 258102 bytes .../figure-html/unnamed-chunk-72-1.png | Bin 321211 -> 321214 bytes .../figure-html/unnamed-chunk-74-1.png | Bin 167872 -> 167848 bytes .../figure-html/unnamed-chunk-76-1.png | Bin 312069 -> 312105 bytes .../figure-html/unnamed-chunk-78-1.png | Bin 111313 -> 111306 bytes .../figure-html/unnamed-chunk-8-1.png | Bin 123073 -> 123062 bytes .../figure-html/unnamed-chunk-80-1.png | Bin 61111 -> 61098 bytes day-1-interactive-session.qmd | 21 +- r-session-02.qmd | 2 +- .../figure-html/unnamed-chunk-10-1.png | Bin 85750 -> 85761 bytes .../figure-html/unnamed-chunk-11-1.png | Bin 65650 -> 65649 bytes .../figure-html/unnamed-chunk-15-1.png | Bin 170540 -> 170532 bytes .../figure-html/unnamed-chunk-17-1.png | Bin 99127 -> 99127 bytes .../figure-html/unnamed-chunk-19-1.png | Bin 51323 -> 51310 bytes .../figure-html/unnamed-chunk-22-1.png | Bin 120488 -> 120521 bytes .../figure-html/unnamed-chunk-29-1.png | Bin 110430 -> 110427 bytes .../figure-html/unnamed-chunk-3-1.png | Bin 50446 -> 50547 bytes .../figure-html/unnamed-chunk-33-1.png | Bin 72096 -> 72092 bytes .../figure-html/unnamed-chunk-35-1.png | Bin 60663 -> 60644 bytes .../figure-html/unnamed-chunk-62-1.png | Bin 54222 -> 52490 bytes .../figure-html/unnamed-chunk-68-1.png | Bin 124020 -> 124024 bytes .../figure-html/unnamed-chunk-71-1.png | Bin 258096 -> 258102 bytes .../figure-html/unnamed-chunk-72-1.png | Bin 321211 -> 321214 bytes .../figure-html/unnamed-chunk-74-1.png | Bin 167872 -> 167848 bytes .../figure-html/unnamed-chunk-76-1.png | Bin 312069 -> 312105 bytes .../figure-html/unnamed-chunk-78-1.png | Bin 111313 -> 111306 bytes .../figure-html/unnamed-chunk-8-1.png | Bin 123073 -> 123062 bytes .../figure-html/unnamed-chunk-80-1.png | Bin 61111 -> 61098 bytes 66 files changed, 667 insertions(+), 544 deletions(-) diff --git a/_book/day-1-interactive-session.html b/_book/day-1-interactive-session.html index 97b5507..6433873 100644 --- a/_book/day-1-interactive-session.html +++ b/_book/day-1-interactive-session.html @@ -508,11 +508,15 @@
@@ -565,7 +569,7 @@
  • describe the expected qualitative effect of the intervention on epidemic dynamics.
  • 5.2 Setup

    -

    We will use the diagram package to draw box-and-arrow compartment diagrams, as in Section 6.6.1.2, and the purrr package to create uniform compartment boxes.

    +

    We will use the diagram package to draw box-and-arrow compartment diagrams, as in R Session 02, and the purrr package to create uniform compartment boxes.

    Code
    library(diagram)
     library(purrr)
    @@ -583,18 +587,64 @@ \(R\): recovered or removed individuals.
    -
    +
    Code
    elpos <- rbind(
    +  S = c(1, 1),
    +  I = c(2, 1),
    +  R = c(3, 1)
    +)
    +
    +elpos[, 1] <- (2 * elpos[, 1] - 1) / 6
    +elpos[, 2] <- 0.5
    +
    +fromto <- rbind(
    +  SI = c(1, 2),
    +  IR = c(2, 3)
    +)
    +
    +op <- par(mar = c(1, 1, 1, 1))
    +diagram::openplotmat(asp = 0.35)
    +
    +for (i in seq_len(nrow(fromto))) {
    +  diagram::straightarrow(
    +    to = elpos[fromto[i, 2], ],
    +    from = elpos[fromto[i, 1], ],
    +    lwd = 2,
    +    arr.pos = 0.65,
    +    arr.length = 0.5
    +  )
    +}
    +
    +purrr::walk(
    +  c("S", "I", "R"),
    +  .f = function(.x) {
    +    diagram::textrect(
    +      elpos[.x, ],
    +      0.08,
    +      0.10,
    +      lab = .x,
    +      box.col = gray(0.7),
    +      shadow.col = gray(0.4),
    +      shadow.size = 0.01,
    +      cex = 2
    +    )
    +  }
    +)
    +
    +text(mean(elpos[c("S", "I"), 1]), 0.62, expression(lambda), cex = 1.8)
    +text(mean(elpos[c("I", "R"), 1]), 0.62, expression(gamma), cex = 1.8)
    +

    +
    Code
    par(op)
    +

    For the standard SIR model, infection moves people from \(S\) to \(I\) at rate \(\lambda\), and recovery or removal moves people from \(I\) to \(R\) at rate \(\gamma\).

    -

    -5.4 Group activity

    -

    Choose one intervention or modeling feature that your group wants to represent. Then change the SIR diagram to include it.

    -

    Examples include:

    +

    +5.3.1 Exercise 1: Choose one intervention or modeling feature that your group wants to represent. Then change the SIR diagram to include it.

    +

    Examples include:

    -5.5 Example: adding vaccination

    + +

    +5.3.2 Example: adding vaccination

    One simple way to represent vaccination is to add a flow from \(S\) to \(R\). This assumes vaccination gives protection similar to recovery or removal. That is a strong assumption, but it is a useful starting point.

    -
    +
    Code
    elpos <- rbind(
    +  S = c(1, 2),
    +  I = c(3, 2),
    +  R = c(2, 1)
    +)
    +
    +elpos[, 1] <- (2 * elpos[, 1] - 1) / 6
    +elpos[, 2] <- (2 * elpos[, 2] - 1) / 4
    +
    +fromto <- rbind(
    +  SI = c(1, 2),
    +  IR = c(2, 3),
    +  SR = c(1, 3)
    +)
    +
    +op <- par(mar = c(1, 1, 1, 1))
    +diagram::openplotmat(asp = 0.65)
    +
    +for (i in seq_len(nrow(fromto))) {
    +  diagram::straightarrow(
    +    to = elpos[fromto[i, 2], ],
    +    from = elpos[fromto[i, 1], ],
    +    lwd = 2,
    +    arr.pos = 0.65,
    +    arr.length = 0.5
    +  )
    +}
    +
    +purrr::walk(
    +  c("S", "I", "R"),
    +  .f = function(.x) {
    +    diagram::textrect(
    +      elpos[.x, ],
    +      0.08,
    +      0.10,
    +      lab = .x,
    +      box.col = gray(0.7),
    +      shadow.col = gray(0.4),
    +      shadow.size = 0.01,
    +      cex = 2
    +    )
    +  }
    +)
    +
    +text(mean(elpos[c("S", "I"), 1]), 0.86, expression(lambda), cex = 1.8)
    +text(mean(elpos[c("I", "R"), 1]) + 0.04, 0.47, expression(gamma), cex = 1.8)
    +text(mean(elpos[c("S", "R"), 1]) - 0.04, 0.47, expression(v), cex = 1.8)
    +

    +
    Code
    par(op)
    +

    In this example, \(v\) is the vaccination rate. Before writing equations, ask what this diagram assumes. For example, does vaccination work immediately? Does everyone have the same access to vaccination? Is vaccine protection perfect? Does protection wane?

    -

    -5.6 Template for your diagram

    +

    +5.4 Template for your diagram

    You can copy and modify this code chunk to make your own diagram. Add compartments to elpos, add arrows to fromto, and label any new rates.

    -
    Code
    # Define the locations of each compartment.
    -# Each row is one compartment, and the two numbers give its x- and
    -# y-position before scaling.
    -elpos <- rbind(
    -  S = c(1, 1),
    -  I = c(2, 1),
    -  R = c(3, 1)
    -)
    -
    -# Rescale the compartment positions so they fit inside the plotting area.
    -elpos[, 1] <- (2 * elpos[, 1] - 1) / 6
    -elpos[, 2] <- 0.5
    -
    -# Define the arrows between compartments.
    -# The numbers refer to row positions in elpos, so SI = c(1, 2) draws an
    -# arrow from the first row, S, to the second row, I.
    -fromto <- rbind(
    -  SI = c(1, 2),
    -  IR = c(2, 3)
    -)
    -
    -# Set small plot margins and open a blank plotting area for the diagram.
    -op <- par(mar = c(1, 1, 1, 1))
    -diagram::openplotmat(asp = 0.35)
    -
    -# Draw one arrow for each row in fromto.
    -for (i in seq_len(nrow(fromto))) {
    -  diagram::straightarrow(
    -    to = elpos[fromto[i, 2], ],
    -    from = elpos[fromto[i, 1], ],
    -    lwd = 2,
    -    arr.pos = 0.65,
    -    arr.length = 0.5
    -  )
    -}
    -
    -# Draw a labeled box for each compartment.
    -purrr::walk(
    -  rownames(elpos),
    -  .f = function(.x) {
    -    diagram::textrect(
    -      elpos[.x, ],
    -      0.08,
    -      0.10,
    -      lab = .x,
    -      box.col = gray(0.7),
    -      shadow.col = gray(0.4),
    -      shadow.size = 0.01,
    -      cex = 2
    -    )
    -  }
    -)
    -
    -# Label the arrows with the rates that move individuals between states.
    -# Adjust the x- and y-positions if you add new compartments or arrows.
    -text(mean(elpos[c("S", "I"), 1]), 0.62, expression(lambda), cex = 1.8)
    -text(mean(elpos[c("I", "R"), 1]), 0.62, expression(gamma), cex = 1.8)
    -
    -# Restore the previous plotting settings.
    -par(op)
    +
    Code
    # Define the locations of each compartment.
    +# Each row is one compartment, and the two numbers give its x- and
    +# y-position before scaling.
    +elpos <- rbind(
    +  S = c(1, 1),
    +  I = c(2, 1),
    +  R = c(3, 1)
    +)
    +
    +# Rescale the compartment positions so they fit inside the plotting area.
    +elpos[, 1] <- (2 * elpos[, 1] - 1) / 6
    +elpos[, 2] <- 0.5
    +
    +# Define the arrows between compartments.
    +# The numbers refer to row positions in elpos, so SI = c(1, 2) draws an
    +# arrow from the first row, S, to the second row, I.
    +fromto <- rbind(
    +  SI = c(1, 2),
    +  IR = c(2, 3)
    +)
    +
    +# Set small plot margins and open a blank plotting area for the diagram.
    +op <- par(mar = c(1, 1, 1, 1))
    +diagram::openplotmat(asp = 0.35)
    +
    +# Draw one arrow for each row in fromto.
    +for (i in seq_len(nrow(fromto))) {
    +  diagram::straightarrow(
    +    to = elpos[fromto[i, 2], ],
    +    from = elpos[fromto[i, 1], ],
    +    lwd = 2,
    +    arr.pos = 0.65,
    +    arr.length = 0.5
    +  )
    +}
    +
    +# Draw a labeled box for each compartment.
    +purrr::walk(
    +  rownames(elpos),
    +  .f = function(.x) {
    +    diagram::textrect(
    +      elpos[.x, ],
    +      0.08,
    +      0.10,
    +      lab = .x,
    +      box.col = gray(0.7),
    +      shadow.col = gray(0.4),
    +      shadow.size = 0.01,
    +      cex = 2
    +    )
    +  }
    +)
    +
    +# Label the arrows with the rates that move individuals between states.
    +# Adjust the x- and y-positions if you add new compartments or arrows.
    +text(mean(elpos[c("S", "I"), 1]), 0.62, expression(lambda), cex = 1.8)
    +text(mean(elpos[c("I", "R"), 1]), 0.62, expression(gamma), cex = 1.8)
    +
    +# Restore the previous plotting settings.
    +par(op)
    -

    -5.7 Deliverable

    -

    Each group should be ready to share:

    +

    +5.5 Deliverables

    +

    By the end of the exercise you should be able to share:

    -8.8 Objective Function

    +

    +9.8 Objective Function

    Now we set up a function that will calculate the sum of the squared differences between the observations and the model at any parameterization (more commonly known as “sum of squared errors”). In general, this is called the objective function because it is the quantity that optimization seeks to minimize.

    Code
    #' Calculate the Sum of Squared Errors
    @@ -1301,8 +1307,8 @@
     

    Notice that the code for sse_sir() makes use of the following modeling trick. We know that \(\beta\), \(S_0\), and \(I_0\) must be positive, but our search to optimize these parameters will be over the entire number line. We could constrain the search using a more sophisticated algorithm, but this might introduce other problems (i.e., stability at the boundaries). Instead, we parameterize our objective function (sse_sir) in terms of some alternative variables \(\ln(\beta)\), \(\ln(S_0)\), and \(\ln(I_0)\). While these numbers range from \(-\infty\) to \(\infty\) (the range of our search) they map to our model parameters on a range from \(0\) to \(\infty\) (the range that is biologically meaningful).

    -

    -8.9 Optimization

    +

    +9.9 Optimization

    Our final step is to use the function optim to find the values of \(\beta\), \(S_0\), and \(I_0\) that minimize the sum of squared errors as calculated using our function.

    Finally, we plot these fits against the data.

    @@ -1964,16 +1970,16 @@
    -

    -8.9.1 Exercise 5

    +

    +9.9.1 Exercise 5

    To make things easier, we have assumed the infectious period is known to be 14 days. In terms of years, \(\text{D} = \frac{14}{365} \approx 0.0384\), and the recovery rate is the inverse i.e., \(\gamma = \frac{14}{365}\). Now, modify the code above to estimate \(\gamma\) and \(\beta\) simultaneously.

    -

    -8.9.2 Exercise 6

    +

    +9.9.2 Exercise 6

    What happens if one or both of the other unknowns (\(S_0\) and \(I_0\)) is fixed instead of \(\gamma\)?

    -

    -8.10 Solutions

    -

    -8.10.1 Exercise 1

    +

    +9.10 Solutions

    +

    +9.10.1 Exercise 1

    Code
    p_infec <- (seq(0, 1, by = 0.001))
     r0_p <- (log(1 - p_infec)) / (-p_infec)
    @@ -1991,8 +1997,8 @@
     
    -

    -8.10.2 Exercise 2

    +

    +9.10.2 Exercise 2

    Code
    #A: If the cases were isolated after 24 hours, then gamma would be 1/1 = 1, and if the cases were isolated after 12 hours, gamma would be 1/0.5 = 2. R0 would be calculated as the beta coefficient over gamma, below:
     
    @@ -2007,8 +2013,8 @@
     
    [1] 1.547457
    -

    -8.10.3 Exercise 3

    +

    +9.10.3 Exercise 3

    Code
    niamey[5, 3] <- 0 #replace a "NA"
     #the command below organizes the data so it can be plotted and analyzed
    @@ -2102,8 +2108,8 @@
     

    As we ran the model by weeks, the \(\gamma\) value is \(2^{-1}\) and \(\hat R_0 = \hat \beta_1 / \gamma +1\) giving \(\hat R_0=0.2196041/0.5+1 \approx 1.44\).

    -

    -8.10.4 Exercise 4

    +

    +9.10.4 Exercise 4

    Here, we can use a loop and repeat the regression procedure we used above for varying numbers of initial data points in our model.

    Code
    slope <- NULL
    @@ -2125,8 +2131,8 @@
     
    -

    -8.10.5 Exercise 5

    +

    +9.10.5 Exercise 5

    First, let’s add in \(\gamma\) estimation into the sse.sir function and create a new sse.sir function called sse.sir.g

    Code
    #' Basic SIR model
    @@ -2883,11 +2889,11 @@
     
    -

    -8.10.6 Exercise 6

    +

    +9.10.6 Exercise 6

    What happens if one or both of the other unknowns (\(X_0\) and \(Y_0\)) is fixed instead of \(\gamma\)?

    -

    -8.10.6.1 Solutions coming soon!

    +

    +9.10.6.1 Solutions coming soon!

    First we modify the sse_sir_g function again to fix \(X_0\) and \(Y_0\) (or both)

    Now we can run the optim algorithm and find the best parameters for the SIR model for each of the scenario.

    First, for S0 fixed:

    @@ -3383,12 +3389,12 @@ });