Skip to content

Commit

Permalink
Notes on EpiModel (w/r Issue #22)
Browse files Browse the repository at this point in the history
  • Loading branch information
gvegayon committed Nov 2, 2017
1 parent f01dd41 commit 17a2cbf
Show file tree
Hide file tree
Showing 11 changed files with 592 additions and 0 deletions.
40 changes: 40 additions & 0 deletions playground/epimodel/epimodel.r
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
rm(list = ls())

library(EpiModel)

set.seed(12345)

nw <- network::network.initialize(n = 1e3, directed = FALSE)
nw <- network::set.vertex.attribute(nw, "risk", rep(0:1, each=500))

formation <- ~edges + nodefactor("risk") + nodematch("risk") + concurrent

target.stats <- c(250, 375, 225, 100)
coef.diss <- dissolution_coefs(dissolution = ~offset(edges), duration = 80)
coef.diss

# Step 1
est1 <- netest(nw, formation, target.stats, coef.diss)

# Step 2
dx <- netdx(est1, nsims=10, nsteps = 1000)
dx

# Step 3
init <- init.net(i.num = 50)
param <- param.net(inf.prob = 0.1, act.rate=5, rec.rate = .02)
control <- control.net(type = "SIS", nsteps=500, nsims = 10, epi.by = "risk")

sim1 <- netsim(est1, param, init, control)


plot(sim1)
summary(sim1, at = 500)

plot(sim1, y = c("i.num.risk0", "i.num.risk1"), legend=TRUE)

oldpar <- par(no.readonly = TRUE)
par(mfrow=c(1, 2))
plot(sim1, type="network", at=1, col.status = TRUE)
plot(sim1, type="network", at=500, col.status = TRUE)
par(oldpar)
258 changes: 258 additions & 0 deletions playground/epimodel/epimodel_notes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,258 @@
Notes on the EpiModel R package
================
George G. Vega Yon
November 1, 2017

Introduction
============

The following document presents a set of notes from "EpiModel: An R Package for Mathematical Modeling of Infectious Disease over Networks".

Another interesting example is provided [here](http://statnet.github.io/tut/NewNet.html#example_2:_migration)

The process is as follows:

1. Initialize and set the network calling `network::network.initialize`,

2. Estimate the network parameters calling `netest` passing network effects, this will be used to simulate the dynamic network, and initialize the simulation calling `init.net` (initial infected, recovered, etc.)

3. Define the set of parameters that will be used during the simulation using `param.net`

4. Define the control functions for the simulation (the modules) using `control.net`

5. Run your simulation by calling `netsim` and passing your estimates from `netest`, the output from `param.net` and from `control.net`

Internal work of `netsim` from `EpiModel`

``` r
# Loop through steps
for (at in max(2, control$start):control$nsteps) {

# Fetching the ordering in which modules happen
morder <- control$module.order
if (is.null(morder)) {
lim.bi.mods <- control$bi.mods[-which(control$bi.mods %in%
c("initialize.FUN", "verbose.FUN"))]
morder <- c(control$user.mods, lim.bi.mods)
}

# Applying the models (here is where the simulation actually happens)
for (i in seq_along(morder)) {
dat <- do.call(control[[morder[i]]], list(dat,
at))
}

# Printing results on screen
if (!is.null(control[["verbose.FUN"]])) {
do.call(control[["verbose.FUN"]], list(dat,
type = "progress", s, at))
}
}
```

Extended Example
================

In this section, I review some of the examples presented in the document. The subsections are titled as the subsection in the original document. We first need to load the R packages that we will be used.

``` r
library(EpiModel)
library(sna)
library(networkDynamic)
```

5.2. Example 1: Age-dependent mortality extension
-------------------------------------------------

The following lines of code introduce a new module that sets the mortality rate as a function of age (which is not included by default)

``` r
aging <- function(dat, at) {

if (at == 2) {
# Initializing age
n <- sum(dat$attr$active == 1)
dat$attr$age <- sample(
seq(from = 18, to = 69 + 11 / 12, by = 1 / 12),
n, replace = TRUE)

} else {
# Increasing age
dat$attr$age <- dat$attr$age + 1 / 12

}

# Saving some stats: Average age
if (at == 2) {
dat$epi$meanAge <- c(NA, mean(dat$attr$age, na.rm = TRUE))
} else {
dat$epi$meanAge[at] <- mean(dat$attr$age, na.rm = TRUE)
}

return(dat)
}
```

This example shows how to create a new mortality extension, which is to replace the default function passed to `control.net` as the argument `deaths.FUN`. Currently the default is `deaths.net`.

``` r
ages <- 18:69
death.rates <- 1/(70*12 - ages*12)


dfunc <- function(dat, at) {

# Finding active nodes
idsElig <- which(dat$attr$active == 1)
nElig <- length(idsElig)
nDeaths <- 0

# If there are active nodes
if (nElig > 0) {

# Fetching their ages (from attr)
ages <- dat$attr$age[idsElig]

# The maximun age is fetched from params
max.age <- dat$param$max.age

death.rates <- pmin(1, 1 / (max.age * 12 - ages * 12))
vecDeaths <- which(rbinom(nElig, 1, death.rates) == 1)
idsDeaths <- idsElig[vecDeaths]
nDeaths <- length(idsDeaths)

# If there are deads, then:
# - Set them as inactive,
# - Set the exit time,
# - And deactivate the vertices (and edges) in the network
if (nDeaths > 0) {

dat$attr$active[idsDeaths] <- 0
dat$attr$exitTime[idsDeaths] <- at

dat$nw <- deactivate.vertices(
dat$nw,
onset = at,
terminus = Inf,
v = idsDeaths,
deactivate.edges = TRUE
)

}
}

# Adding summary stats
if (at == 2) {
# In the first run, the statistic d.flow is created
dat$epi$d.flow <- c(0, nDeaths)
} else {
# Then it is just filled
dat$epi$d.flow[at] <- nDeaths
}

return(dat)
}
```

The following module replaces the `birth.FUN` (which by default is `births.net`):

``` r
bfunc <- function(dat, at) {

# Getting some parameters
growth.rate <- dat$param$growth.rate
exptPopSize <- dat$epi$num[1] * (1 + growth.rate * at)
n <- network.size(dat$nw)
numNeeded <- exptPopSize - sum(dat$attr$active == 1)

# If new births are needed, then simulate them using a poisson
if (numNeeded > 0) {
nBirths <- rpois(1, numNeeded)
} else {
nBirths <- 0
}

# If there were new birdths, then add them to the network and
# activate them
if (nBirths > 0) {
dat$nw <- add.vertices(dat$nw, nv = nBirths)
newNodes <- (n + 1):(n + nBirths)
dat$nw <- activate.vertices(
dat$nw, onset = at, terminus = Inf, v = newNodes
)
}

# Initializing vertices atributes (if any)
if (nBirths > 0) {
dat$attr$active <- c(dat$attr$active, rep(1, nBirths))
dat$attr$status <- c(dat$attr$status, rep("s", nBirths))
dat$attr$infTime <- c(dat$attr$infTime, rep(NA, nBirths))
dat$attr$age <- c(dat$attr$age, rep(18, nBirths))
}

# Saving stats
if (at == 2) {
dat$epi$b.flow <- c(0, nBirths)
} else {
dat$epi$b.flow[at] <- nBirths
}

return(dat)
}
```

The following new module computes and stores the mean degree through the simulation, notice that in the case of `networkDynamic`, the time starts at 0.

``` r
dfun <- function(dat, at) {

if (at == 2) {
dat$epi$meandeg <- c(0, mean(degree(network.extract(dat$nw, at = at - 1L))))
} else {
dat$epi$meandeg[at] <- mean(degree(network.extract(dat$nw, at = at - 1L)))
}

dat
}
```

``` r
# Step 1: Bernoulli network
nw <- network::network.initialize(500, directed = FALSE)

# Step 2
est3 <- netest(
nw, formation = ~edges, target.stats = 150,
coef.diss = dissolution_coefs(~offset(edges), 60, mean(death.rates))
)

# Step 3: Passing the parameters
param <- param.net(inf.prob = 0.15, growth.rate = 0.01/12, max.age = 70)
init <- init.net(i.num = 50)

# Step 4: Passing the new functions
control <- control.net(
type = "SI", nsims = 1, nsteps = 100, deaths.FUN = dfunc, births.FUN = bfunc,
aging.FUN = aging, depend = TRUE, verbose = FALSE,

# This function is new in -control.net-
deg.FUN = dfun
)

# Step 5: Running the simulation
sim3 <- netsim(est3, param, init, control)
```

``` r
# If our simulation worked, then we should be able to replicate the
# mean degree
degs <- sapply(1:100, function(i) {
mean(degree(network.extract(sim3$network$sim1, at = i-1)))
})
cor(
sim3$epi$meandeg[-1,1],
degs[-1]
) # This should be one!
```

## [1] 1
Loading

0 comments on commit 17a2cbf

Please sign in to comment.