--- title: Post-process data in a trajectory author: Stefan Widgren ORCID logo output: html_vignette: toc: true toc_depth: 3 vignette: > %\VignetteIndexEntry{Post-process data in a trajectory} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- After a model is created, a simulation is started with a call to the `run()` function. The function returns a modified model object containing a single stochastic solution trajectory. This trajectory includes the state of each compartment recorded at every time-point specified in `tspan`. This vignette introduces the functionality in `SimInf` to post-process and explore this trajectory data. Let us first load the SimInf package. ```{r} library(SimInf) ``` ## Extract trajectory data with `trajectory()` Most modeling studies require custom data analysis beyond simple plotting. To support this, `SimInf` provides the `trajectory()` method to extract the raw data as a `data.frame`. This is useful if you need to: - Perform custom statistical calculations (e.g., time to peak). - Export data to CSV for use in other software. - Combine results from multiple simulation runs. Let's simulate 10 days of data from an SIR model with 6 nodes. For reproducibility, we set the seed and specify the number of threads. ```{r} set.seed(123) set_num_threads(1) u0 <- data.frame( S = c(100, 101, 102, 103, 104, 105), I = c(1, 2, 3, 4, 5, 6), R = c(0, 0, 0, 0, 0, 0) ) model <- SIR( u0 = u0, tspan = 1:10, beta = 0.16, gamma = 0.077 ) result <- run(model) ``` Extract the full trajectory data (all compartments, all nodes). ```{r} trajectory(result) ``` Extract the number of recovered individuals (R) in the first node only. ```{r} trajectory(result, compartments = "R", index = 1) ``` Extract the number of recovered individuals in the first and third nodes. ```{r} trajectory(result, compartments = "R", index = c(1, 3)) ``` ## Calculate prevalence from a trajectory using `prevalence()` The `prevalence()` function calculates the proportion of individuals with the disease. It takes a model object and a formula: - **Left-hand side (LHS):** Compartments representing "cases" (e.g., I). - **Right-hand side (RHS):** Compartments representing the "at-risk" population (e.g., S + I + R). The function also supports a `level` argument to change the aggregation level: - `level = 1` (default): Prevalence aggregated over all nodes (global). - `level = 2`: Proportion of nodes that have at least one case. - `level = 3`: Prevalence calculated within each node (returns a matrix). Let's determine the proportion of infected individuals in the total population. ```{r} prevalence(result, I ~ S + I + R) ``` Identical result is obtained with the shorthand `I ~ .` (where `.` means "all compartments"). ```{r} prevalence(result, I ~ .) ``` Calculate the proportion of nodes that are infected (at least one I individual). ```{r} prevalence(result, I ~ S + I + R, level = 2) ``` Calculate the prevalence **within each node** individually. ```{r} prevalence(result, I ~ S + I + R, level = 3) ``` ## Visualize a trajectory with `plot()` The `plot()` function provides a quick way to inspect the outcome. It can display: - The median and quantile range across all nodes. - Individual trajectories for specific nodes. - Prevalence curves. *Note: Since the simulation is stochastic, the exact lines shown below will vary unless set.seed() is used.* ### Aggregated View (Median and Range) Plot the median and interquartile range (IQR) of all compartments. ```{r, fig.width=7, fig.height=4, fig.align="left"} plot(result) ``` Plot the median and the middle 95\% quantile range. ```{r, fig.width=7, fig.height=4, fig.align="left"} plot(result, range = 0.95) ``` Plot only the infected individuals (I). ```{r, fig.width=7, fig.height=4, fig.align="left"} plot(result, "I") ``` Use formula notation to plot the infected individuals. ```{r, fig.width=7, fig.height=4, fig.align="left"} plot(result, ~I) ``` ### Individual Node View Plot the trajectories for the first three nodes. We use `range = FALSE` to suppress the shaded median/range bands and show the individual lines. ```{r, fig.width=7, fig.height=4, fig.align="left"} plot(result, index = 1:3, range = FALSE) ``` Use `type = "l"` to draw a line. ```{r, fig.width=7, fig.height=4, fig.align="left"} plot(result, index = 1:3, range = FALSE, type = "l") ``` Plot the infected individuals in the first node only. ```{r, fig.width=7, fig.height=4, fig.align="left"} plot(result, "I", index = 1, range = FALSE) ``` ### Prevalence Plots Plot the proportion of infected individuals in the population. ```{r, fig.width=7, fig.height=4, fig.align="left"} plot(result, I ~ S + I + R) ``` Plot the proportion of nodes with infected individuals (`level = 2`). ```{r, fig.width=7, fig.height=4, fig.align="left"} plot(result, I ~ S + I + R, level = 2) ``` Plot the median and IQR of the prevalence **within in each node** (`level = 3`). ```{r, fig.width=7, fig.height=4, fig.align="left"} plot(result, I ~ S + I + R, level = 3) ``` Plot the prevalence in the first three nodes. ```{r, fig.width=7, fig.height=4, fig.align="left"} plot(result, I ~ S + I + R, level = 3, index = 1:3, range = FALSE) ``` ## Summary - Use `trajectory()` to extract raw data for custom analysis. - Use `prevalence()` to calculate disease proportions at different aggregation levels. - Use `plot()` for quick visual inspection of medians, ranges, or individual trajectories. To find more details on the plot method for `SimInf_model` objects, run: ```{r, eval=FALSE} help("plot,SimInf_model-method", package = "SimInf") ```