diff --git a/previews/PR107/.documenter-siteinfo.json b/previews/PR107/.documenter-siteinfo.json index e79ed76fc..bce164678 100644 --- a/previews/PR107/.documenter-siteinfo.json +++ b/previews/PR107/.documenter-siteinfo.json @@ -1 +1 @@ -{"documenter":{"julia_version":"1.10.1","generation_timestamp":"2024-03-06T11:28:33","documenter_version":"1.3.0"}} \ No newline at end of file +{"documenter":{"julia_version":"1.10.2","generation_timestamp":"2024-03-06T18:28:50","documenter_version":"1.3.0"}} \ No newline at end of file diff --git a/previews/PR107/assets/documenter.js b/previews/PR107/assets/documenter.js index c6562b558..8a829d865 100644 --- a/previews/PR107/assets/documenter.js +++ b/previews/PR107/assets/documenter.js @@ -4,10 +4,8 @@ requirejs.config({ 'highlight-julia': 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.8.0/languages/julia.min', 'headroom': 'https://cdnjs.cloudflare.com/ajax/libs/headroom/0.12.0/headroom.min', 'jqueryui': 'https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.13.2/jquery-ui.min', - 'katex-auto-render': 'https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.16.8/contrib/auto-render.min', 'jquery': 'https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.0/jquery.min', 'headroom-jquery': 'https://cdnjs.cloudflare.com/ajax/libs/headroom/0.12.0/jQuery.headroom.min', - 'katex': 'https://cdnjs.cloudflare.com/ajax/libs/KaTeX/0.16.8/katex.min', 'highlight': 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.8.0/highlight.min', 'highlight-julia-repl': 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.8.0/languages/julia-repl.min', }, @@ -17,11 +15,6 @@ requirejs.config({ "highlight" ] }, - "katex-auto-render": { - "deps": [ - "katex" - ] - }, "headroom-jquery": { "deps": [ "jquery", @@ -36,32 +29,39 @@ requirejs.config({ } }); //////////////////////////////////////////////////////////////////////////////// -require(['jquery', 'katex', 'katex-auto-render'], function($, katex, renderMathInElement) { -$(document).ready(function() { - renderMathInElement( - document.body, - { - "delimiters": [ - { - "left": "$", - "right": "$", - "display": false - }, - { - "left": "$$", - "right": "$$", - "display": true - }, - { - "left": "\\[", - "right": "\\]", - "display": true - } - ] +require([], function() { +window.MathJax = { + "tex": { + "packages": [ + "base", + "ams", + "autoload" + ], + "inlineMath": [ + [ + "$", + "$" + ], + [ + "\\(", + "\\)" + ] + ], + "tags": "ams" + }, + "options": { + "ignoreHtmlClass": "tex2jax_ignore", + "processHtmlClass": "tex2jax_process" + } } - - ); -}) +; + +(function () { + var script = document.createElement('script'); + script.src = 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/3.2.2/es5/tex-svg.js'; + script.async = true; + document.head.appendChild(script); +})(); }) //////////////////////////////////////////////////////////////////////////////// diff --git a/previews/PR107/checklist/index.html b/previews/PR107/checklist/index.html index 1af796001..0a48edf64 100644 --- a/previews/PR107/checklist/index.html +++ b/previews/PR107/checklist/index.html @@ -1,5 +1,5 @@ -Release checklist · EpiAware.jl

Checklists

The purpose of this page is to collate a series of checklists for commonly performed changes to the source code of EpiAware. It has been adapted from Documenter.jl.

In each case, copy the checklist into the description of the pull request.

Making a release

In preparation for a release, use the following checklist. These steps should be performed on a branch with an open pull request, either for a topic branch, or for a new branch release-1.y.z ("Release version 1.y.z") if multiple changes have accumulated on the master branch since the last release.

## Pre-release
+Release checklist · EpiAware.jl

Checklists

The purpose of this page is to collate a series of checklists for commonly performed changes to the source code of EpiAware. It has been adapted from Documenter.jl.

In each case, copy the checklist into the description of the pull request.

Making a release

In preparation for a release, use the following checklist. These steps should be performed on a branch with an open pull request, either for a topic branch, or for a new branch release-1.y.z ("Release version 1.y.z") if multiple changes have accumulated on the master branch since the last release.

## Pre-release
 
  - [ ] Change the version number in `Project.toml`
    * If the release is breaking, increment MAJOR
@@ -20,4 +20,4 @@
 
    Either of those should automatically publish a new version to the Julia registry.
  - Once registered, the `TagBot.yml` workflow should create a tag, and rebuild the documentation for this tag.
- - These steps can take quite a bit of time (1 hour or more), so don't be surprised if the new documentation takes a while to appear.
+ - These steps can take quite a bit of time (1 hour or more), so don't be surprised if the new documentation takes a while to appear.
diff --git a/previews/PR107/contributing/index.html b/previews/PR107/contributing/index.html index 0b26110b8..c524895ea 100644 --- a/previews/PR107/contributing/index.html +++ b/previews/PR107/contributing/index.html @@ -1,2 +1,2 @@ -Contributing · EpiAware.jl

Contributing

This page details the some of the guidelines that should be followed when contributing to this package. It is adapted from Documenter.jl.

Branches

release-* branches are used for tagged minor versions of this package. This follows the same approach used in the main Julia repository, albeit on a much more modest scale.

Please open pull requests against the master branch rather than any of the release-* branches whenever possible.

Backports

Bug fixes are backported to the release-* branches using git cherry-pick -x by a EpiAware member and will become available in point releases of that particular minor version of the package.

Feel free to nominate commits that should be backported by opening an issue. Requests for new point releases to be tagged in METADATA.jl can also be made in the same way.

release-* branches

  • Each new minor version x.y.0 gets a branch called release-x.y (a protected branch).
  • New versions are usually tagged only from the release-x.y branches.
  • For patch releases, changes get backported to the release-x.y branch via a single PR with the standard name "Backports for x.y.z" and label "Type: Backport". The PR message links to all the PRs that are providing commits to the backport. The PR gets merged as a merge commit (i.e. not squashed).
  • The old release-* branches may be removed once they have outlived their usefulness.
  • Patch version milestones are used to keep track of which PRs get backported etc.

Style Guide

Follow the style of the surrounding text when making changes. When adding new features please try to stick to the following points whenever applicable. This project follows the SciML style guide.

Tests

Unit tests

As is conventional for Julia packages, unit tests are located at test/*.jl with the entrypoint test/runtests.jl.

End to end testing

Tests that build example package docs from source and inspect the results (end to end tests) are located in /test/examples. The main entry points are test/examples/make.jl for building and test/examples/test.jl for doing some basic checks on the generated outputs.

+Contributing · EpiAware.jl

Contributing

This page details the some of the guidelines that should be followed when contributing to this package. It is adapted from Documenter.jl.

Branches

release-* branches are used for tagged minor versions of this package. This follows the same approach used in the main Julia repository, albeit on a much more modest scale.

Please open pull requests against the master branch rather than any of the release-* branches whenever possible.

Backports

Bug fixes are backported to the release-* branches using git cherry-pick -x by a EpiAware member and will become available in point releases of that particular minor version of the package.

Feel free to nominate commits that should be backported by opening an issue. Requests for new point releases to be tagged in METADATA.jl can also be made in the same way.

release-* branches

  • Each new minor version x.y.0 gets a branch called release-x.y (a protected branch).
  • New versions are usually tagged only from the release-x.y branches.
  • For patch releases, changes get backported to the release-x.y branch via a single PR with the standard name "Backports for x.y.z" and label "Type: Backport". The PR message links to all the PRs that are providing commits to the backport. The PR gets merged as a merge commit (i.e. not squashed).
  • The old release-* branches may be removed once they have outlived their usefulness.
  • Patch version milestones are used to keep track of which PRs get backported etc.

Style Guide

Follow the style of the surrounding text when making changes. When adding new features please try to stick to the following points whenever applicable. This project follows the SciML style guide.

Tests

Unit tests

As is conventional for Julia packages, unit tests are located at test/*.jl with the entrypoint test/runtests.jl.

End to end testing

Tests that build example package docs from source and inspect the results (end to end tests) are located in /test/examples. The main entry points are test/examples/make.jl for building and test/examples/test.jl for doing some basic checks on the generated outputs.

diff --git a/previews/PR107/examples/getting_started.jl b/previews/PR107/examples/getting_started.jl new file mode 100644 index 000000000..a1bac0e00 --- /dev/null +++ b/previews/PR107/examples/getting_started.jl @@ -0,0 +1,394 @@ +### A Pluto.jl notebook ### +# v0.19.39 + +using Markdown +using InteractiveUtils + +# ╔═╡ c593a2a0-d7f5-11ee-0931-d9f65ae84a72 +# hideall +let + docs_dir = dirname(dirname(@__DIR__)) + pkg_dir = dirname(docs_dir) + + using Pkg: Pkg + Pkg.activate(docs_dir) + Pkg.develop(; path = pkg_dir) + Pkg.instantiate() +end; + +# ╔═╡ da479d8d-1312-4b98-b0af-5be52dffaf3f +begin + using EpiAware + using Turing + using Distributions + using StatsPlots + using Random + using DynamicPPL + using Statistics + using DataFramesMeta + using LinearAlgebra +end + +# ╔═╡ 3ebc8384-f73d-4597-83a7-07a3744fed61 +md" +# Getting stated with `EpiAware` + +This tutorial introduces the basic functionality of `EpiAware`. `EpiAware` is a package for making inferences on epidemiological case/determined infection data using a model-based approach. + +## `EpiAware` models +The models we consider are discrete-time $t = 1,\dots, T$ with a latent random process, $Z_t$ generating stochasticity in the number of new infections $I_t$ at each time step. Observations are treated as downstream random variables determined by the actual infections and a model of infection to observation delay. + +#### Mathematical definition +```math +\begin{align} +Z_\cdot &\sim \mathcal{P}(\mathbb{R}^T) | \theta_Z, \\ +I_0 &\sim f_0(\theta_I), \\ +I_t &\sim g_I(\{I_s, Z_s\}_{s < t}, \theta_{I}), \\ +y_t &\sim f_O(\{I_s\}_{s \leq t}, \theta_{O}). +\end{align} +``` +Where, $\mathcal{P}(\mathbb{R}^T) | \theta_Z$ is a parametric process on $\mathbb{R}^T$. $f_0$ and $f_O$ are parametric distributions on, respectively, the initial number of infections and the observed case data conditional on underlying infections. $g_I$ is distribution of new infections conditional on infections and latent process in the past. Note that we assume that new infections are conditional on the strict past, whereas new observations can depend on infections on the same time step. + +#### Code structure outline + +An `EpiAware` model in code is created from three modular components: + +- A `LatentModel`: This defines the model for $Z_\cdot$. This chooses $\mathcal{P}(\mathbb{R}^T) | \theta_Z$. +- An `EpiModel`: This defines a generative process for infections conditional on the latent process. This chooses $f_0(\theta_I)$, and $g_I(\{I_s, Z_s\}_{s < t}, \theta_{I})$. +- An `ObservationModel`: This defines the observation model. This chooses $f_O({I_s}_{s \leq t}, \theta_{O})$ + +#### Reproductive number +`EpiAware` models do not need to specify a time-varying reproductive number $\mathcal{R}_t$ to generate $I_\cdot$, however, this is often a quantity of interest. When not directly used we will typically back-calculate $\mathcal{R}_t$ from the generated infections: + +```math +\mathcal{R}_t = {I_t \over \sum_{s \geq 1} g_s I_{t-s} }. +``` + +Where $g_s$ is a discrete generation interval. For this reason, even when not using a reproductive number approach directly, we ask for a generation interval. +" + +# ╔═╡ 5a0d5ab8-e985-4126-a1ac-58fe08beee38 +md" +## Random walk `LatentModel` + +As an example, we choose the latent process as a random walk with parameters $\theta_Z$: + +- ``Z_0``: Initial position. +- ``\sigma^2_{Z}``: The step-size variance. + +Conditional on the parameters the random walk is then generated by white noise: +```math +\begin{align} +Z_t &= Z_0 + \sigma_{RW} \sum_{t= 1}^T \epsilon_t, \\ +\epsilon_t &\sim \mathcal{N}(0,1). +\end{align} +``` + +In `EpiAware` we provide a constructor for random walk latent models with priors for $\theta_Z$. We choose priors, +```math +\begin{align} +Z_0 &\sim \mathcal{N}(0,1),\\ +\sigma^2_Z &\sim \text{HalfNormal}(0.01). +\end{align} +``` +" + +# ╔═╡ 56ae496b-0094-460b-89cb-526627991717 +rwp = EpiAware.RandomWalk(Normal(), + truncated(Normal(0.0, 0.02), 0.0, Inf)) + +# ╔═╡ 767beffd-1ef5-4e6c-9ac6-edb52e60fb44 +md" +## Direct infection `EpiModel` + +This is a simple model where the unobserved log-infections are directly generated by the latent process $Z$. +```math +\log I_t = \log I_0 + Z_t. +``` + +As discussed above, we still ask for a defined generation interval, which can be used to calculate $\mathcal{R}_t$. + +" + +# ╔═╡ 9e43cbe3-94de-44fc-a788-b9c7adb34218 +truth_GI = Gamma(2, 5) + +# ╔═╡ f067284f-a1a6-44a6-9b79-f8c2de447673 +md" +The `EpiData` constructor performs double interval censoring to convert our _continuous_ estimate of the generation interval into a discretized version. We also implement right truncation using the keyword `D_gen`. +" + +# ╔═╡ c0662d48-4b54-4b6d-8c91-ddf4b0e3aa43 +model_data = EpiData(truth_GI, D_gen = 10.0) + +# ╔═╡ fd72094f-1b95-4d07-a8b0-ef47dc560dfc +md" +We can supply a prior for the initial log_infections. +" + +# ╔═╡ 6639e66f-7725-4976-81b2-6472419d1a62 +log_I0_prior = Normal(log(100.0), 1.0) + +# ╔═╡ df5e59f8-3185-4bed-9cca-7c266df17cec +md" +And construct the `EpiModel`. +" + +# ╔═╡ 6fbdd8e6-2323-4352-9185-1f31a9cf9012 +epi_model = DirectInfections(model_data, log_I0_prior) + +# ╔═╡ 5e62a50a-71f4-4902-b1c9-fdf51fe145fa +md" + + +### Delayed Observations `ObservationModel` + +The observation model is a negative binomial distribution with mean `μ` and cluster factor `1 / r`. Delays are implemented +as the action of a sparse kernel on the infections $I(t)$. + +```math +\begin{align} +y_t &\sim \text{NegBinomial}(\mu = \sum_{s\geq 0} K[t, t-s] I(s), r), \\ +1 / r &\sim \text{Gamma}(3, 0.05/3). +\end{align} +``` +" + +# ╔═╡ e813d547-6100-4c43-b84c-8cebe306bda8 +md" +We also set up the inference to occur over 100 days. +" + +# ╔═╡ c7580ae6-0db5-448e-8b20-4dd6fcdb1ae0 +time_horizon = 100 + +# ╔═╡ 0aa3fcbd-0831-45b8-9a2c-7ffbabf5895f +md" +We choose a simple observation model where infections are observed 0, 1, 2, 3 days later with equal probability. +" + +# ╔═╡ 448669bc-99f4-4823-b15e-fcc9040ba31b +obs_model = EpiAware.DelayObservations( + fill(0.25, 4), + time_horizon, + truncated(Gamma(5, 0.05 / 5), 1e-3, 1.0) +) + +# ╔═╡ e49713e8-4840-4083-8e3f-fc52d791be7b +md" +## Generate cases from the `EpiAware` model + +Having chosen an `EpiModel`, `LatentModel` and `ObservationModel`, we can implement the model as a [`Turing`](https://turinglang.org/dev/) model using `make_epi_aware`. + +By giving `missing` to the first argument, we indicate that case data will be _generated_ from the model rather than treated as fixed. +" + +# ╔═╡ abeff860-58c3-4644-9325-66ffd4446b6d +full_epi_aware_mdl = make_epi_aware(missing, time_horizon; + epi_model = epi_model, + latent_model = rwp, + observation_model = obs_model) + +# ╔═╡ 821628fb-8044-48b0-aa4f-0b7b57a2f45a +md" +We choose some fixed parameters: +- Initial incidence is 100. +- In the direct infection model, the initial incidence and in the initial value of the random walk form a non-identifiable pair. Therefore, we fix $Z_0 = 0$. +" + +# ╔═╡ 36b34fd2-2891-42ca-b5dc-abb482e516ee +fixed_parameters = (rw_init = 0.0, init_incidence = log(100.0)) + +# ╔═╡ 0aadd9e3-7f91-4b45-9663-67d11335f0d0 +md" +We fix these parameters using `fix`, and generate a random epidemic. +" + +# ╔═╡ 7e0e6012-8648-4f84-a25a-8b0138c4b72a +cond_generative_model = fix(full_epi_aware_mdl, fixed_parameters) + +# ╔═╡ b20c28be-7b07-410c-a33b-ea5ad6828c12 +random_epidemic = rand(cond_generative_model) + +# ╔═╡ d073e63b-62da-4743-ace0-78ef7806bc0b +true_infections = generated_quantities(cond_generative_model, random_epidemic).I_t + +# ╔═╡ a04f3c1b-7e11-4800-9c2a-9fc0021de6e7 +generated_obs = generated_quantities(cond_generative_model, random_epidemic).generated_y_t + +# ╔═╡ f68b4e41-ac5c-42cd-a8c2-8761d66f7543 +let + plot(true_infections, + label = "I_t", + xlabel = "Time", + ylabel = "Infections", + title = "Generated Infections") + scatter!(generated_obs, lab = "generated cases") +end + +# ╔═╡ b5bc8f05-b538-4abf-aa84-450bf2dff3d9 +md" +## Inference +Fixing $Z_0 = 0$ for the random walk was based on inference principles; in this model $Z_0$ and $\log I_0$ are non-identifiable. + +However, we now treat the generated data as `truth_data` and make inference without fixing any other parameters. + +We do the inference by MCMC/NUTS using the `Turing` NUTS sampler with default warm-up steps. +" + +# ╔═╡ c8ce0d46-a160-4c40-a055-69b3d10d1770 +truth_data = generated_obs + +# ╔═╡ b4033728-b321-4100-8194-1fd9fe2d268d +inference_mdl = fix( + make_epi_aware(truth_data, time_horizon; epi_model = epi_model, + latent_model = rwp, observation_model = obs_model), + (rw_init = 0.0,) +) + +# ╔═╡ 3eb5ec5e-aae7-478e-84fb-80f2e9f85b4c +chn = sample(inference_mdl, + NUTS(; adtype = AutoReverseDiff(true)), + MCMCThreads(), + 250, + 4; + drop_warmup = true) + +# ╔═╡ 30498cc7-16a5-441a-b8cd-c19b220c60c1 +md" +### Predictive plotting + +We can spaghetti plot generated case data from the version of the model _which hasn't conditioned on case data_ using posterior parameters inferred from the version conditioned on observed data. This is known as _posterior predictive checking_, and is a useful diagnostic tool for Bayesian inference (see [here](http://www.stat.columbia.edu/~gelman/book/BDA3.pdf)). + +Because we are using synthetic data we can also plot the model predictions for the _unobserved_ infections and check that (at least in this example) we were able to capture some unobserved/latent variables in the process accurate. +" + +# ╔═╡ e9df22b8-8e4d-4ab7-91ea-c01f2239b3e5 +let + post_check_mdl = fix(full_epi_aware_mdl, (rw_init = 0.0,)) + post_check_y_t = mapreduce(hcat, generated_quantities(post_check_mdl, chn)) do gen + gen.generated_y_t + end + + predicted_I_t = mapreduce(hcat, generated_quantities(inference_mdl, chn)) do gen + gen.I_t + end + + p1 = plot(post_check_y_t, c = :grey, alpha = 0.05, lab = "") + scatter!(p1, truth_data, + lab = "Observed cases", + xlabel = "Time", + ylabel = "Cases", + title = "Post. predictive checking: cases", + ylims = (-0.5, maximum(truth_data) * 1.5), + c = :green) + + p2 = plot(predicted_I_t, c = :grey, alpha = 0.05, lab = "") + scatter!(p2, true_infections, + lab = "Actual infections", + xlabel = "Time", + ylabel = "Unobserved Infections", + title = "Post. predictions: infections", + ylims = (-0.5, maximum(true_infections) * 1.5), + c = :red) + + plot(p1, p2, + layout = (1, 2), + size = (700, 400)) +end + +# ╔═╡ fd6321b1-4c3a-4123-b0dc-c45b951e0b80 +md" +As well as checking the posterior predictions for latent infections, we can also check how well inference recovered unknown parameters, such as the random walk variance or the cluster factor of the negative binomial observations. +" + +# ╔═╡ 10d8fe24-83a6-47ac-97b7-a374481473d3 +let + parameters_to_plot = (:σ²_RW, :neg_bin_cluster_factor) + + plts = map(parameters_to_plot) do name + var_samples = chn[name] |> vec + histogram(var_samples, + bins = 50, + norm = :pdf, + lw = 0, + fillalpha = 0.5, + lab = "MCMC") + vline!([getfield(random_epidemic, name)], lab = "True value") + title!(string(name)) + end + plot(plts..., layout = (2, 1)) +end + +# ╔═╡ 81efe8ca-b753-4a12-bafc-a887a999377b +md" +## Reproductive number back-calculation + +As mentioned at the top, we _don't_ directly use the concept of reproductive numbers in this note. However, we can back-calculate the implied $\mathcal{R}(t)$ values, conditional on the specified generation interval being correct. + +Here we spaghetti plot posterior sampled time-varying reproductive numbers against the actual. +" + +# ╔═╡ 15b9f37f-8d5f-460d-8c28-d7f2271fd099 +let + n = epi_model.data.len_gen_int + Rt_denom = [dot(reverse(epi_model.data.gen_int), true_infections[(t - n):(t - 1)]) + for t in (n + 1):length(true_infections)] + true_Rt = true_infections[(n + 1):end] ./ Rt_denom + + predicted_Rt = mapreduce(hcat, generated_quantities(inference_mdl, chn)) do gen + _It = gen.I_t + _Rt_denom = [dot(reverse(epi_model.data.gen_int), _It[(t - n):(t - 1)]) + for t in (n + 1):length(_It)] + Rt = _It[(n + 1):end] ./ _Rt_denom + end + + plt = plot((n + 1):time_horizon, predicted_Rt, c = :grey, alpha = 0.05, lab = "") + plot!(plt, (n + 1):time_horizon, true_Rt, + lab = "true Rt", + xlabel = "Time", + ylabel = "Rt", + title = "Post. predictions: reproductive number", + c = :red, + lw = 2) +end + +# ╔═╡ Cell order: +# ╟─c593a2a0-d7f5-11ee-0931-d9f65ae84a72 +# ╟─3ebc8384-f73d-4597-83a7-07a3744fed61 +# ╠═da479d8d-1312-4b98-b0af-5be52dffaf3f +# ╟─5a0d5ab8-e985-4126-a1ac-58fe08beee38 +# ╠═56ae496b-0094-460b-89cb-526627991717 +# ╟─767beffd-1ef5-4e6c-9ac6-edb52e60fb44 +# ╠═9e43cbe3-94de-44fc-a788-b9c7adb34218 +# ╟─f067284f-a1a6-44a6-9b79-f8c2de447673 +# ╠═c0662d48-4b54-4b6d-8c91-ddf4b0e3aa43 +# ╟─fd72094f-1b95-4d07-a8b0-ef47dc560dfc +# ╠═6639e66f-7725-4976-81b2-6472419d1a62 +# ╟─df5e59f8-3185-4bed-9cca-7c266df17cec +# ╠═6fbdd8e6-2323-4352-9185-1f31a9cf9012 +# ╟─5e62a50a-71f4-4902-b1c9-fdf51fe145fa +# ╟─e813d547-6100-4c43-b84c-8cebe306bda8 +# ╠═c7580ae6-0db5-448e-8b20-4dd6fcdb1ae0 +# ╟─0aa3fcbd-0831-45b8-9a2c-7ffbabf5895f +# ╠═448669bc-99f4-4823-b15e-fcc9040ba31b +# ╟─e49713e8-4840-4083-8e3f-fc52d791be7b +# ╠═abeff860-58c3-4644-9325-66ffd4446b6d +# ╟─821628fb-8044-48b0-aa4f-0b7b57a2f45a +# ╠═36b34fd2-2891-42ca-b5dc-abb482e516ee +# ╟─0aadd9e3-7f91-4b45-9663-67d11335f0d0 +# ╠═7e0e6012-8648-4f84-a25a-8b0138c4b72a +# ╠═b20c28be-7b07-410c-a33b-ea5ad6828c12 +# ╠═d073e63b-62da-4743-ace0-78ef7806bc0b +# ╠═a04f3c1b-7e11-4800-9c2a-9fc0021de6e7 +# ╟─f68b4e41-ac5c-42cd-a8c2-8761d66f7543 +# ╟─b5bc8f05-b538-4abf-aa84-450bf2dff3d9 +# ╠═c8ce0d46-a160-4c40-a055-69b3d10d1770 +# ╠═b4033728-b321-4100-8194-1fd9fe2d268d +# ╠═3eb5ec5e-aae7-478e-84fb-80f2e9f85b4c +# ╟─30498cc7-16a5-441a-b8cd-c19b220c60c1 +# ╠═e9df22b8-8e4d-4ab7-91ea-c01f2239b3e5 +# ╟─fd6321b1-4c3a-4123-b0dc-c45b951e0b80 +# ╠═10d8fe24-83a6-47ac-97b7-a374481473d3 +# ╟─81efe8ca-b753-4a12-bafc-a887a999377b +# ╠═15b9f37f-8d5f-460d-8c28-d7f2271fd099 diff --git a/previews/PR107/examples/getting_started/index.html b/previews/PR107/examples/getting_started/index.html new file mode 100644 index 000000000..a108a45ed --- /dev/null +++ b/previews/PR107/examples/getting_started/index.html @@ -0,0 +1,295 @@ + +Getting started · EpiAware.jl
+ + + + + + + +

Getting stated with EpiAware

This tutorial introduces the basic functionality of EpiAware. EpiAware is a package for making inferences on epidemiological case/determined infection data using a model-based approach.

EpiAware models

The models we consider are discrete-time $t = 1,\dots, T$ with a latent random process, $Z_t$ generating stochasticity in the number of new infections $I_t$ at each time step. Observations are treated as downstream random variables determined by the actual infections and a model of infection to observation delay.

Mathematical definition

$$\begin{align} +Z_\cdot &\sim \mathcal{P}(\mathbb{R}^T) | \theta_Z, \\ +I_0 &\sim f_0(\theta_I), \\ +I_t &\sim g_I(\{I_s, Z_s\}_{s < t}, \theta_{I}), \\ +y_t &\sim f_O(\{I_s\}_{s \leq t}, \theta_{O}). +\end{align}$$

Where, $\mathcal{P}(\mathbb{R}^T) | \theta_Z$ is a parametric process on $\mathbb{R}^T$. $f_0$ and $f_O$ are parametric distributions on, respectively, the initial number of infections and the observed case data conditional on underlying infections. $g_I$ is distribution of new infections conditional on infections and latent process in the past. Note that we assume that new infections are conditional on the strict past, whereas new observations can depend on infections on the same time step.

Code structure outline

An EpiAware model in code is created from three modular components:

  • A LatentModel: This defines the model for $Z_\cdot$. This chooses $\mathcal{P}(\mathbb{R}^T) | \theta_Z$.

  • An EpiModel: This defines a generative process for infections conditional on the latent process. This chooses $f_0(\theta_I)$, and $g_I(\{I_s, Z_s\}_{s < t}, \theta_{I})$.

  • An ObservationModel: This defines the observation model. This chooses $f_O({I_s}_{s \leq t}, \theta_{O})$

Reproductive number

EpiAware models do not need to specify a time-varying reproductive number $\mathcal{R}_t$ to generate $I_\cdot$, however, this is often a quantity of interest. When not directly used we will typically back-calculate $\mathcal{R}_t$ from the generated infections:

$$\mathcal{R}_t = {I_t \over \sum_{s \geq 1} g_s I_{t-s} }.$$

Where $g_s$ is a discrete generation interval. For this reason, even when not using a reproductive number approach directly, we ask for a generation interval.

+ +
begin
+    using EpiAware
+    using Turing
+    using Distributions
+    using StatsPlots
+    using Random
+    using DynamicPPL
+    using Statistics
+    using DataFramesMeta
+    using LinearAlgebra
+end
+ + + +

Random walk LatentModel

As an example, we choose the latent process as a random walk with parameters $\theta_Z$:

  • $Z_0$: Initial position.

  • $\sigma^2_{Z}$: The step-size variance.

Conditional on the parameters the random walk is then generated by white noise:

$$\begin{align} +Z_t &= Z_0 + \sigma_{RW} \sum_{t= 1}^T \epsilon_t, \\ +\epsilon_t &\sim \mathcal{N}(0,1). +\end{align}$$

In EpiAware we provide a constructor for random walk latent models with priors for $\theta_Z$. We choose priors,

$$\begin{align} +Z_0 &\sim \mathcal{N}(0,1),\\ +\sigma^2_Z &\sim \text{HalfNormal}(0.01). +\end{align}$$

+ +
rwp = EpiAware.RandomWalk(Normal(),
+    truncated(Normal(0.0, 0.02), 0.0, Inf))
+
RandomWalk{Normal{Float64}, Truncated{Normal{Float64}, Continuous, Float64, Float64, Float64}}(Distributions.Normal{Float64}(μ=0.0, σ=1.0), Truncated(Distributions.Normal{Float64}(μ=0.0, σ=0.02); lower=0.0, upper=Inf))
+ + +

Direct infection EpiModel

This is a simple model where the unobserved log-infections are directly generated by the latent process $Z$.

$$\log I_t = \log I_0 + Z_t.$$

As discussed above, we still ask for a defined generation interval, which can be used to calculate $\mathcal{R}_t$.

+ +
truth_GI = Gamma(2, 5)
+
Distributions.Gamma{Float64}(α=2.0, θ=5.0)
+ + +

The EpiData constructor performs double interval censoring to convert our continuous estimate of the generation interval into a discretized version. We also implement right truncation using the keyword D_gen.

+ +
model_data = EpiData(truth_GI, D_gen = 10.0)
+
EpiData{Float64, typeof(exp)}([0.056722565637507084, 0.0944813112842689, 0.11668723746973605, 0.12773814268679387, 0.130948497124998, 0.1287976856448402, 0.12312384970427602, 0.11527489380975135, 0.10622581663782862], 9, exp)
+ + +

We can supply a prior for the initial log_infections.

+ +
log_I0_prior = Normal(log(100.0), 1.0)
+
Distributions.Normal{Float64}(μ=4.605170185988092, σ=1.0)
+ + +

And construct the EpiModel.

+ +
epi_model = DirectInfections(model_data, log_I0_prior)
+
DirectInfections{Normal{Float64}}(EpiData{Float64, typeof(exp)}([0.056722565637507084, 0.0944813112842689, 0.11668723746973605, 0.12773814268679387, 0.130948497124998, 0.1287976856448402, 0.12312384970427602, 0.11527489380975135, 0.10622581663782862], 9, exp), Distributions.Normal{Float64}(μ=4.605170185988092, σ=1.0))
+ + +

Delayed Observations ObservationModel

The observation model is a negative binomial distribution with mean μ and cluster factor 1 / r. Delays are implemented as the action of a sparse kernel on the infections $I(t)$.

$$\begin{align} +y_t &\sim \text{NegBinomial}(\mu = \sum_{s\geq 0} K[t, t-s] I(s), r), \\ +1 / r &\sim \text{Gamma}(3, 0.05/3). +\end{align}$$

+ + +

We also set up the inference to occur over 100 days.

+ +
time_horizon = 100
+
100
+ + +

We choose a simple observation model where infections are observed 0, 1, 2, 3 days later with equal probability.

+ +
obs_model = EpiAware.DelayObservations(
+    fill(0.25, 4),
+    time_horizon,
+    truncated(Gamma(5, 0.05 / 5), 1e-3, 1.0)
+)
+
DelayObservations{Float64, Truncated{Gamma{Float64}, Continuous, Float64, Float64, Float64}}(sparse(Integer[1, 2, 3, 4, 2, 3, 4, 5, 3, 4  …  97, 98, 99, 100, 98, 99, 100, 99, 100, 100], Integer[1, 1, 1, 1, 2, 2, 2, 2, 3, 3  …  97, 97, 97, 97, 98, 98, 98, 99, 99, 100], [0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25  …  0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25], 100, 100), Truncated(Distributions.Gamma{Float64}(α=5.0, θ=0.01); lower=0.001, upper=1.0))
+ + +

Generate cases from the EpiAware model

Having chosen an EpiModel, LatentModel and ObservationModel, we can implement the model as a Turing model using make_epi_aware.

By giving missing to the first argument, we indicate that case data will be generated from the model rather than treated as fixed.

+ +
full_epi_aware_mdl = make_epi_aware(missing, time_horizon;
+    epi_model = epi_model,
+    latent_model = rwp,
+    observation_model = obs_model)
+
Model{typeof(make_epi_aware), (:y_t, :time_steps), (:epi_model, :latent_model, :observation_model, :pos_shift), (:y_t,), Tuple{Missing, Int64}, Tuple{DirectInfections{Normal{Float64}}, RandomWalk{Normal{Float64}, Truncated{Normal{Float64}, Continuous, Float64, Float64, Float64}}, DelayObservations{Float64, Truncated{Gamma{Float64}, Continuous, Float64, Float64, Float64}}, Float64}, DefaultContext}(EpiAware.make_epi_aware, (y_t = missing, time_steps = 100), (epi_model = DirectInfections{Normal{Float64}}(EpiData{Float64, typeof(exp)}([0.056722565637507084, 0.0944813112842689, 0.11668723746973605, 0.12773814268679387, 0.130948497124998, 0.1287976856448402, 0.12312384970427602, 0.11527489380975135, 0.10622581663782862], 9, exp), Distributions.Normal{Float64}(μ=4.605170185988092, σ=1.0)), latent_model = RandomWalk{Normal{Float64}, Truncated{Normal{Float64}, Continuous, Float64, Float64, Float64}}(Distributions.Normal{Float64}(μ=0.0, σ=1.0), Truncated(Distributions.Normal{Float64}(μ=0.0, σ=0.02); lower=0.0, upper=Inf)), observation_model = DelayObservations{Float64, Truncated{Gamma{Float64}, Continuous, Float64, Float64, Float64}}(sparse(Integer[1, 2, 3, 4, 2, 3, 4, 5, 3, 4  …  97, 98, 99, 100, 98, 99, 100, 99, 100, 100], Integer[1, 1, 1, 1, 2, 2, 2, 2, 3, 3  …  97, 97, 97, 97, 98, 98, 98, 99, 99, 100], [0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25  …  0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25], 100, 100), Truncated(Distributions.Gamma{Float64}(α=5.0, θ=0.01); lower=0.001, upper=1.0)), pos_shift = 1.0e-6), DefaultContext())
+ + +

We choose some fixed parameters:

  • Initial incidence is 100.

  • In the direct infection model, the initial incidence and in the initial value of the random walk form a non-identifiable pair. Therefore, we fix $Z_0 = 0$.

+ +
fixed_parameters = (rw_init = 0.0, init_incidence = log(100.0))
+
(rw_init = 0.0, init_incidence = 4.605170185988092)
+ + +

We fix these parameters using fix, and generate a random epidemic.

+ +
cond_generative_model = fix(full_epi_aware_mdl, fixed_parameters)
+
Model{typeof(make_epi_aware), (:y_t, :time_steps), (:epi_model, :latent_model, :observation_model, :pos_shift), (:y_t,), Tuple{Missing, Int64}, Tuple{DirectInfections{Normal{Float64}}, RandomWalk{Normal{Float64}, Truncated{Normal{Float64}, Continuous, Float64, Float64, Float64}}, DelayObservations{Float64, Truncated{Gamma{Float64}, Continuous, Float64, Float64, Float64}}, Float64}, DynamicPPL.FixedContext{@NamedTuple{rw_init::Float64, init_incidence::Float64}, DefaultContext}}(EpiAware.make_epi_aware, (y_t = missing, time_steps = 100), (epi_model = DirectInfections{Normal{Float64}}(EpiData{Float64, typeof(exp)}([0.056722565637507084, 0.0944813112842689, 0.11668723746973605, 0.12773814268679387, 0.130948497124998, 0.1287976856448402, 0.12312384970427602, 0.11527489380975135, 0.10622581663782862], 9, exp), Distributions.Normal{Float64}(μ=4.605170185988092, σ=1.0)), latent_model = RandomWalk{Normal{Float64}, Truncated{Normal{Float64}, Continuous, Float64, Float64, Float64}}(Distributions.Normal{Float64}(μ=0.0, σ=1.0), Truncated(Distributions.Normal{Float64}(μ=0.0, σ=0.02); lower=0.0, upper=Inf)), observation_model = DelayObservations{Float64, Truncated{Gamma{Float64}, Continuous, Float64, Float64, Float64}}(sparse(Integer[1, 2, 3, 4, 2, 3, 4, 5, 3, 4  …  97, 98, 99, 100, 98, 99, 100, 99, 100, 100], Integer[1, 1, 1, 1, 2, 2, 2, 2, 3, 3  …  97, 97, 97, 97, 98, 98, 98, 99, 99, 100], [0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25  …  0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25], 100, 100), Truncated(Distributions.Gamma{Float64}(α=5.0, θ=0.01); lower=0.001, upper=1.0)), pos_shift = 1.0e-6), FixedContext((rw_init = 0.0, init_incidence = 4.605170185988092), DynamicPPL.DefaultContext()))
+ +
random_epidemic = rand(cond_generative_model)
+
(ϵ_t = [-0.3245105752051425, 0.5841792768429854, 0.837698738466377, 0.04991144411048826, 0.43709682639657876, 1.5113816873648402, 0.5183464762750044, -1.2951234902508262, -0.8475356353822158, 0.5731492794677614  …  -0.9718005452114998, 0.2986685041305902, 0.8778707060932857, 0.2779274792378409, -1.3849265413496943, 0.9094660271125533, 1.2532018893752603, 1.5863018030422251, -0.04686725620120323, 0.4638612794817786], σ²_RW = 0.026203541122590525, neg_bin_cluster_factor = 0.0472919247750861, var"y_t[1]" = 24, var"y_t[2]" = 53, var"y_t[3]" = 106, var"y_t[4]" = 105, var"y_t[5]" = 121, var"y_t[6]" = 115, var"y_t[7]" = 149, var"y_t[8]" = 185, var"y_t[9]" = 121, var"y_t[10]" = 134, var"y_t[11]" = 124, var"y_t[12]" = 75, var"y_t[13]" = 105, var"y_t[14]" = 95, var"y_t[15]" = 118, var"y_t[16]" = 126, var"y_t[17]" = 125, var"y_t[18]" = 94, var"y_t[19]" = 124, var"y_t[20]" = 64, var"y_t[21]" = 59, var"y_t[22]" = 137, var"y_t[23]" = 75, var"y_t[24]" = 83, var"y_t[25]" = 157, var"y_t[26]" = 136, var"y_t[27]" = 104, var"y_t[28]" = 131, var"y_t[29]" = 228, var"y_t[30]" = 173, var"y_t[31]" = 156, var"y_t[32]" = 165, var"y_t[33]" = 183, var"y_t[34]" = 182, var"y_t[35]" = 112, var"y_t[36]" = 145, var"y_t[37]" = 161, var"y_t[38]" = 117, var"y_t[39]" = 169, var"y_t[40]" = 173, var"y_t[41]" = 108, var"y_t[42]" = 123, var"y_t[43]" = 69, var"y_t[44]" = 52, var"y_t[45]" = 105, var"y_t[46]" = 132, var"y_t[47]" = 117, var"y_t[48]" = 99, var"y_t[49]" = 119, var"y_t[50]" = 93, var"y_t[51]" = 80, var"y_t[52]" = 96, var"y_t[53]" = 77, var"y_t[54]" = 89, var"y_t[55]" = 166, var"y_t[56]" = 104, var"y_t[57]" = 74, var"y_t[58]" = 115, var"y_t[59]" = 126, var"y_t[60]" = 102, var"y_t[61]" = 146, var"y_t[62]" = 102, var"y_t[63]" = 157, var"y_t[64]" = 96, var"y_t[65]" = 156, var"y_t[66]" = 159, var"y_t[67]" = 92, var"y_t[68]" = 143, var"y_t[69]" = 83, var"y_t[70]" = 130, var"y_t[71]" = 121, var"y_t[72]" = 133, var"y_t[73]" = 208, var"y_t[74]" = 171, var"y_t[75]" = 186, var"y_t[76]" = 227, var"y_t[77]" = 218, var"y_t[78]" = 205, var"y_t[79]" = 189, var"y_t[80]" = 111, var"y_t[81]" = 182, var"y_t[82]" = 301, var"y_t[83]" = 199, var"y_t[84]" = 234, var"y_t[85]" = 194, var"y_t[86]" = 208, var"y_t[87]" = 393, var"y_t[88]" = 381, var"y_t[89]" = 406, var"y_t[90]" = 429, var"y_t[91]" = 169, var"y_t[92]" = 405, var"y_t[93]" = 256, var"y_t[94]" = 300, var"y_t[95]" = 330, var"y_t[96]" = 240, var"y_t[97]" = 245, var"y_t[98]" = 287, var"y_t[99]" = 439, var"y_t[100]" = 610)
+ +
true_infections = generated_quantities(cond_generative_model, random_epidemic).I_t
+
100-element Vector{Float64}:
+  94.88256898856683
+ 104.29298246375241
+ 119.43910104412493
+ 120.4080084136958
+ 129.23612130314984
+ 165.0578731221061
+ 179.50508482958563
+   ⋮
+ 304.0060267388828
+ 352.2240260411064
+ 431.4404807480081
+ 557.7508150421348
+ 553.5353707088188
+ 596.6992603591709
+ +
generated_obs = generated_quantities(cond_generative_model, random_epidemic).generated_y_t
+
100-element Vector{Int64}:
+  24
+  53
+ 106
+ 105
+ 121
+ 115
+ 149
+   ⋮
+ 330
+ 240
+ 245
+ 287
+ 439
+ 610
+ + + + +

Inference

+

Fixing $Z_0 = 0$ for the random walk was based on inference principles; in this model $Z_0$ and $\log I_0$ are non-identifiable.

However, we now treat the generated data as truth_data and make inference without fixing any other parameters.

We do the inference by MCMC/NUTS using the Turing NUTS sampler with default warm-up steps.

+ +
truth_data = generated_obs
+
100-element Vector{Int64}:
+  24
+  53
+ 106
+ 105
+ 121
+ 115
+ 149
+   ⋮
+ 330
+ 240
+ 245
+ 287
+ 439
+ 610
+ +
inference_mdl = fix(
+    make_epi_aware(truth_data, time_horizon; epi_model = epi_model,
+        latent_model = rwp, observation_model = obs_model),
+    (rw_init = 0.0,)
+)
+
Model{typeof(make_epi_aware), (:y_t, :time_steps), (:epi_model, :latent_model, :observation_model, :pos_shift), (), Tuple{Vector{Int64}, Int64}, Tuple{DirectInfections{Normal{Float64}}, RandomWalk{Normal{Float64}, Truncated{Normal{Float64}, Continuous, Float64, Float64, Float64}}, DelayObservations{Float64, Truncated{Gamma{Float64}, Continuous, Float64, Float64, Float64}}, Float64}, DynamicPPL.FixedContext{@NamedTuple{rw_init::Float64}, DefaultContext}}(EpiAware.make_epi_aware, (y_t = [24, 53, 106, 105, 121, 115, 149, 185, 121, 134  …  169, 405, 256, 300, 330, 240, 245, 287, 439, 610], time_steps = 100), (epi_model = DirectInfections{Normal{Float64}}(EpiData{Float64, typeof(exp)}([0.056722565637507084, 0.0944813112842689, 0.11668723746973605, 0.12773814268679387, 0.130948497124998, 0.1287976856448402, 0.12312384970427602, 0.11527489380975135, 0.10622581663782862], 9, exp), Distributions.Normal{Float64}(μ=4.605170185988092, σ=1.0)), latent_model = RandomWalk{Normal{Float64}, Truncated{Normal{Float64}, Continuous, Float64, Float64, Float64}}(Distributions.Normal{Float64}(μ=0.0, σ=1.0), Truncated(Distributions.Normal{Float64}(μ=0.0, σ=0.02); lower=0.0, upper=Inf)), observation_model = DelayObservations{Float64, Truncated{Gamma{Float64}, Continuous, Float64, Float64, Float64}}(sparse(Integer[1, 2, 3, 4, 2, 3, 4, 5, 3, 4  …  97, 98, 99, 100, 98, 99, 100, 99, 100, 100], Integer[1, 1, 1, 1, 2, 2, 2, 2, 3, 3  …  97, 97, 97, 97, 98, 98, 98, 99, 99, 100], [0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25  …  0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25], 100, 100), Truncated(Distributions.Gamma{Float64}(α=5.0, θ=0.01); lower=0.001, upper=1.0)), pos_shift = 1.0e-6), FixedContext((rw_init = 0.0,), DynamicPPL.DefaultContext()))
+ +
chn = sample(inference_mdl,
+    NUTS(; adtype = AutoReverseDiff(true)),
+    MCMCThreads(),
+    250,
+    4;
+    drop_warmup = true)
+
iterationchainϵ_t[1]ϵ_t[2]ϵ_t[3]ϵ_t[4]ϵ_t[5]ϵ_t[6]...
112611.158851.659530.922999-1.322090.3266220.821712
21271-0.694295-0.88852-0.3311721.855460.626332-0.37274
31281-0.0574015-0.7028910.626630.451822-0.675678-0.462883
412910.0387053-0.7032662.080680.446057-1.19198-0.867791
513010.615644-0.6864031.55179-1.01553-0.09418611.28523
613112.55703-1.009341.21827-0.1504631.61262-0.0782016
713210.08887392.164940.04079230.5806720.3559921.49668
81331-0.4101452.1424-0.5912011.615730.5469741.2129
91341-0.41357-1.262480.538455-0.889234-1.056741.4544
101351-0.7451.218350.834888-0.0217767-0.3205411.40438
...
+ + +

Predictive plotting

We can spaghetti plot generated case data from the version of the model which hasn't conditioned on case data using posterior parameters inferred from the version conditioned on observed data. This is known as posterior predictive checking, and is a useful diagnostic tool for Bayesian inference (see here).

Because we are using synthetic data we can also plot the model predictions for the unobserved infections and check that (at least in this example) we were able to capture some unobserved/latent variables in the process accurate.

+ +
let
+    post_check_mdl = fix(full_epi_aware_mdl, (rw_init = 0.0,))
+    post_check_y_t = mapreduce(hcat, generated_quantities(post_check_mdl, chn)) do gen
+        gen.generated_y_t
+    end
+
+    predicted_I_t = mapreduce(hcat, generated_quantities(inference_mdl, chn)) do gen
+        gen.I_t
+    end
+
+    p1 = plot(post_check_y_t, c = :grey, alpha = 0.05, lab = "")
+    scatter!(p1, truth_data,
+        lab = "Observed cases",
+        xlabel = "Time",
+        ylabel = "Cases",
+        title = "Post. predictive checking: cases",
+        ylims = (-0.5, maximum(truth_data) * 1.5),
+        c = :green)
+
+    p2 = plot(predicted_I_t, c = :grey, alpha = 0.05, lab = "")
+    scatter!(p2, true_infections,
+        lab = "Actual infections",
+        xlabel = "Time",
+        ylabel = "Unobserved Infections",
+        title = "Post. predictions: infections",
+        ylims = (-0.5, maximum(true_infections) * 1.5),
+        c = :red)
+
+    plot(p1, p2,
+        layout = (1, 2),
+        size = (700, 400))
+end
+ + + +

As well as checking the posterior predictions for latent infections, we can also check how well inference recovered unknown parameters, such as the random walk variance or the cluster factor of the negative binomial observations.

+ +
let
+    parameters_to_plot = (:σ²_RW, :neg_bin_cluster_factor)
+
+    plts = map(parameters_to_plot) do name
+        var_samples = chn[name] |> vec
+        histogram(var_samples,
+            bins = 50,
+            norm = :pdf,
+            lw = 0,
+            fillalpha = 0.5,
+            lab = "MCMC")
+        vline!([getfield(random_epidemic, name)], lab = "True value")
+        title!(string(name))
+    end
+    plot(plts..., layout = (2, 1))
+end
+ + +

Reproductive number back-calculation

+

As mentioned at the top, we don't directly use the concept of reproductive numbers in this note. However, we can back-calculate the implied $\mathcal{R}(t)$ values, conditional on the specified generation interval being correct.

Here we spaghetti plot posterior sampled time-varying reproductive numbers against the actual.

+ +
let
+    n = epi_model.data.len_gen_int
+    Rt_denom = [dot(reverse(epi_model.data.gen_int), true_infections[(t - n):(t - 1)])
+                for t in (n + 1):length(true_infections)]
+    true_Rt = true_infections[(n + 1):end] ./ Rt_denom
+
+    predicted_Rt = mapreduce(hcat, generated_quantities(inference_mdl, chn)) do gen
+        _It = gen.I_t
+        _Rt_denom = [dot(reverse(epi_model.data.gen_int), _It[(t - n):(t - 1)])
+                     for t in (n + 1):length(_It)]
+        Rt = _It[(n + 1):end] ./ _Rt_denom
+    end
+
+    plt = plot((n + 1):time_horizon, predicted_Rt, c = :grey, alpha = 0.05, lab = "")
+    plot!(plt, (n + 1):time_horizon, true_Rt,
+        lab = "true Rt",
+        xlabel = "Time",
+        ylabel = "Rt",
+        title = "Post. predictions: reproductive number",
+        c = :red,
+        lw = 2)
+end
+ + +
diff --git a/previews/PR107/index.html b/previews/PR107/index.html index 4ba2481fa..d0760fa2c 100644 --- a/previews/PR107/index.html +++ b/previews/PR107/index.html @@ -1,2 +1,2 @@ -EpiAware.jl: Real-time epidemic monitoring · EpiAware.jl

EpiAware.jl

Infectious disease situational awareness modelling toolkit for Julia.

A package for building and fitting situational awareness models for infectious diseases. The package is designed to be flexible and extensible, and to provide a consistent interface for fitting and simulating models.

Package Features

  • Flexible: The package is designed to be flexible and extensible, and to provide a consistent interface for fitting and simulating models.
  • Modular: The package is designed to be modular, with a clear separation between the model and the data.
  • Extensible: The package is designed to be extensible, with a clear separation between the model and the data.
  • Consistent: The package is designed to provide a consistent interface for fitting and simulating models.
  • Efficient: The package is designed to be efficient, with a clear separation between the model and the data.

See the Index for the complete list of documented functions and types.

Manual Outline

    Library Outline

    Index

      +EpiAware.jl: Real-time epidemic monitoring · EpiAware.jl

      EpiAware.jl

      Infectious disease situational awareness modelling toolkit for Julia.

      A package for building and fitting situational awareness models for infectious diseases. The package is designed to be flexible and extensible, and to provide a consistent interface for fitting and simulating models.

      Package Features

      • Flexible: The package is designed to be flexible and extensible, and to provide a consistent interface for fitting and simulating models.
      • Modular: The package is designed to be modular, with a clear separation between the model and the data.
      • Extensible: The package is designed to be extensible, with a clear separation between the model and the data.
      • Consistent: The package is designed to provide a consistent interface for fitting and simulating models.
      • Efficient: The package is designed to be efficient, with a clear separation between the model and the data.

      See the Index for the complete list of documented functions and types.

      Manual Outline

        Library Outline

        Index

          diff --git a/previews/PR107/lib/internals/index.html b/previews/PR107/lib/internals/index.html index 8143e52d2..53f483133 100644 --- a/previews/PR107/lib/internals/index.html +++ b/previews/PR107/lib/internals/index.html @@ -1,8 +1,8 @@ -Internals · EpiAware.jl

          internal Documentation

          Documentation for EpiAware.jl's internal interface.

          Contents

            Index

            Internal API

            EpiAware.NegativeBinomialMeanClustMethod

            NegativeBinomialMeanClust NegativeBinomialMeanClust(μ, α)

            Compute the mean-cluster factor negative binomial distribution.

            Arguments

            • μ: The mean of the distribution.
            • α: The clustering factor parameter.

            Returns

            A NegativeBinomial distribution object.


            Signatures

            NegativeBinomialMeanClust(
            +Internals · EpiAware.jl

            internal Documentation

            Documentation for EpiAware.jl's internal interface.

            Contents

              Index

              Internal API

              EpiAware.NegativeBinomialMeanClustMethod

              NegativeBinomialMeanClust NegativeBinomialMeanClust(μ, α)

              Compute the mean-cluster factor negative binomial distribution.

              Arguments

              • μ: The mean of the distribution.
              • α: The clustering factor parameter.

              Returns

              A NegativeBinomial distribution object.


              Signatures

              NegativeBinomialMeanClust(
                   μ,
                   α
               ) -> Distributions.NegativeBinomial
              -

              Methods

              NegativeBinomialMeanClust(μ, α)

              defined at /home/runner/work/Rt-without-renewal/Rt-without-renewal/src/utilities.jl:210.

              source
              EpiAware.generate_observation_kernelMethod

              generateobservationkernel generateobservationkernel(delayint, timehorizon)

              Generate an observation kernel matrix based on the given delay interval and time horizon.

              Arguments

              • delay_int::Vector{Float64}: The delay PMF vector.
              • time_horizon::Int: The number of time steps of the observation period.

              Returns

              • K::SparseMatrixCSC{Float64, Int}: The observation kernel matrix.

              Signatures

              generate_observation_kernel(delay_int, time_horizon) -> Any
              -

              Methods

              generate_observation_kernel(delay_int, time_horizon)

              defined at /home/runner/work/Rt-without-renewal/Rt-without-renewal/src/utilities.jl:229.

              source
              +

              Methods

              NegativeBinomialMeanClust(μ, α)

              defined at /home/runner/work/Rt-without-renewal/Rt-without-renewal/src/utilities.jl:210.

              source
              EpiAware.generate_observation_kernelMethod

              generateobservationkernel generateobservationkernel(delayint, timehorizon)

              Generate an observation kernel matrix based on the given delay interval and time horizon.

              Arguments

              • delay_int::Vector{Float64}: The delay PMF vector.
              • time_horizon::Int: The number of time steps of the observation period.

              Returns

              • K::SparseMatrixCSC{Float64, Int}: The observation kernel matrix.

              Signatures

              generate_observation_kernel(delay_int, time_horizon) -> Any
              +

              Methods

              generate_observation_kernel(delay_int, time_horizon)

              defined at /home/runner/work/Rt-without-renewal/Rt-without-renewal/src/utilities.jl:229.

              source
              diff --git a/previews/PR107/lib/public/index.html b/previews/PR107/lib/public/index.html index 0c6a294dd..35c4daaf3 100644 --- a/previews/PR107/lib/public/index.html +++ b/previews/PR107/lib/public/index.html @@ -1,2 +1,2 @@ -Public API · EpiAware.jl
              +Public API · EpiAware.jl
              diff --git a/previews/PR107/man/getting-started/index.html b/previews/PR107/man/getting-started/index.html deleted file mode 100644 index 1887230c1..000000000 --- a/previews/PR107/man/getting-started/index.html +++ /dev/null @@ -1,2 +0,0 @@ - -Getting started · EpiAware.jl
              diff --git a/previews/PR107/man/guide/index.html b/previews/PR107/man/guide/index.html index 9e3cd19f6..d0b00b549 100644 --- a/previews/PR107/man/guide/index.html +++ b/previews/PR107/man/guide/index.html @@ -1,2 +1,2 @@ -Guide · EpiAware.jl
              +Guide · EpiAware.jl
              diff --git a/previews/PR107/objects.inv b/previews/PR107/objects.inv index 061693d70..ab7c86d76 100644 Binary files a/previews/PR107/objects.inv and b/previews/PR107/objects.inv differ diff --git a/previews/PR107/release-notes/index.html b/previews/PR107/release-notes/index.html index 5adfb4d21..6ba3b6d6c 100644 --- a/previews/PR107/release-notes/index.html +++ b/previews/PR107/release-notes/index.html @@ -1,2 +1,2 @@ -Release notes · EpiAware.jl
              +Release notes · EpiAware.jl
              diff --git a/previews/PR107/search_index.js b/previews/PR107/search_index.js index 77da60f3f..fb66bc5a5 100644 --- a/previews/PR107/search_index.js +++ b/previews/PR107/search_index.js @@ -1,3 +1,3 @@ var documenterSearchIndex = {"docs": -[{"location":"checklist/#Checklists","page":"Release checklist","title":"Checklists","text":"","category":"section"},{"location":"checklist/","page":"Release checklist","title":"Release checklist","text":"The purpose of this page is to collate a series of checklists for commonly performed changes to the source code of EpiAware. It has been adapted from Documenter.jl.","category":"page"},{"location":"checklist/","page":"Release checklist","title":"Release checklist","text":"In each case, copy the checklist into the description of the pull request.","category":"page"},{"location":"checklist/#Making-a-release","page":"Release checklist","title":"Making a release","text":"","category":"section"},{"location":"checklist/","page":"Release checklist","title":"Release checklist","text":"In preparation for a release, use the following checklist. These steps should be performed on a branch with an open pull request, either for a topic branch, or for a new branch release-1.y.z (\"Release version 1.y.z\") if multiple changes have accumulated on the master branch since the last release.","category":"page"},{"location":"checklist/","page":"Release checklist","title":"Release checklist","text":"## Pre-release\n\n - [ ] Change the version number in `Project.toml`\n * If the release is breaking, increment MAJOR\n * If the release adds a new user-visible feature, increment MINOR\n * Otherwise (bug-fixes, documentation improvements), increment PATCH\n - [ ] Update `CHANGELOG.md`, following the existing style (in particular, make sure that the change log for this version has the correct version number and date).\n - [ ] Run `make changelog`, to make sure that all the issue references in `CHANGELOG.md` are up to date.\n - [ ] Check that the commit messages in this PR do not contain `[ci skip]`\n - [ ] Run https://github.com/JuliaDocs/Documenter.jl/actions/workflows/regression-tests.yml\n using a `workflow_dispatch` trigger to check for any changes that broke extensions.\n\n## The release\n\n - [ ] After merging the pull request, tag the release. There are two options for this:\n\n 1. [Comment `[at]JuliaRegistrator register` on the GitHub commit.](https://github.com/JuliaRegistries/Registrator.jl#via-the-github-app)\n 2. Use [JuliaHub's package registration feature](https://help.juliahub.com/juliahub/stable/contribute/#registrator) to trigger the registration.\n\n Either of those should automatically publish a new version to the Julia registry.\n - Once registered, the `TagBot.yml` workflow should create a tag, and rebuild the documentation for this tag.\n - These steps can take quite a bit of time (1 hour or more), so don't be surprised if the new documentation takes a while to appear.","category":"page"},{"location":"release-notes/","page":"Release notes","title":"Release notes","text":"EditURL = \"https://github.com/JuliaDocs/Documenter.jl/blob/master/CHANGELOG.md\"","category":"page"},{"location":"release-notes/#Release-notes","page":"Release notes","title":"Release notes","text":"","category":"section"},{"location":"release-notes/","page":"Release notes","title":"Release notes","text":"The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.","category":"page"},{"location":"release-notes/#Unreleased","page":"Release notes","title":"Unreleased","text":"","category":"section"},{"location":"release-notes/#Added","page":"Release notes","title":"Added","text":"","category":"section"},{"location":"release-notes/#Changed","page":"Release notes","title":"Changed","text":"","category":"section"},{"location":"release-notes/#Fixed","page":"Release notes","title":"Fixed","text":"","category":"section"},{"location":"lib/internals/#internal-Documentation","page":"Internals","title":"internal Documentation","text":"","category":"section"},{"location":"lib/internals/","page":"Internals","title":"Internals","text":"Documentation for EpiAware.jl's internal interface.","category":"page"},{"location":"lib/internals/#Contents","page":"Internals","title":"Contents","text":"","category":"section"},{"location":"lib/internals/","page":"Internals","title":"Internals","text":"Pages = [\"internal.md\"]\nDepth = 2:2","category":"page"},{"location":"lib/internals/#Index","page":"Internals","title":"Index","text":"","category":"section"},{"location":"lib/internals/","page":"Internals","title":"Internals","text":"Pages = [\"internals.md\"]","category":"page"},{"location":"lib/internals/#Internal-API","page":"Internals","title":"Internal API","text":"","category":"section"},{"location":"lib/internals/","page":"Internals","title":"Internals","text":"Modules = [EpiAware]\nPublic = false","category":"page"},{"location":"lib/internals/#EpiAware.NegativeBinomialMeanClust-Tuple{Any, Any}","page":"Internals","title":"EpiAware.NegativeBinomialMeanClust","text":"NegativeBinomialMeanClust NegativeBinomialMeanClust(μ, α)\n\nCompute the mean-cluster factor negative binomial distribution.\n\nArguments\n\nμ: The mean of the distribution.\nα: The clustering factor parameter.\n\nReturns\n\nA NegativeBinomial distribution object.\n\n\n\nSignatures\n\nNegativeBinomialMeanClust(\n μ,\n α\n) -> Distributions.NegativeBinomial\n\n\n\n\nMethods\n\nNegativeBinomialMeanClust(μ, α)\n\ndefined at /home/runner/work/Rt-without-renewal/Rt-without-renewal/src/utilities.jl:210.\n\n\n\n\n\n","category":"method"},{"location":"lib/internals/#EpiAware.generate_observation_kernel-Tuple{Any, Any}","page":"Internals","title":"EpiAware.generate_observation_kernel","text":"generateobservationkernel generateobservationkernel(delayint, timehorizon)\n\nGenerate an observation kernel matrix based on the given delay interval and time horizon.\n\nArguments\n\ndelay_int::Vector{Float64}: The delay PMF vector.\ntime_horizon::Int: The number of time steps of the observation period.\n\nReturns\n\nK::SparseMatrixCSC{Float64, Int}: The observation kernel matrix.\n\n\n\nSignatures\n\ngenerate_observation_kernel(delay_int, time_horizon) -> Any\n\n\n\n\nMethods\n\ngenerate_observation_kernel(delay_int, time_horizon)\n\ndefined at /home/runner/work/Rt-without-renewal/Rt-without-renewal/src/utilities.jl:229.\n\n\n\n\n\n","category":"method"},{"location":"lib/internals/#EpiAware.neg_MGF-Tuple{Any, AbstractVector}","page":"Internals","title":"EpiAware.neg_MGF","text":"negMGF negMGF(r, w::AbstractVector)\n\nCompute the negative moment generating function (MGF) for a given rate r and weights w.\n\nArguments\n\nr: The rate parameter.\nw: An abstract vector of weights.\n\nReturns\n\nThe value of the negative MGF.\n\n\n\nSignatures\n\nneg_MGF(r, w::AbstractVector) -> Any\n\n\n\n\nMethods\n\nneg_MGF(r, w)\n\ndefined at /home/runner/work/Rt-without-renewal/Rt-without-renewal/src/utilities.jl:132.\n\n\n\n\n\n","category":"method"},{"location":"contributing/#Contributing","page":"Contributing","title":"Contributing","text":"","category":"section"},{"location":"contributing/","page":"Contributing","title":"Contributing","text":"This page details the some of the guidelines that should be followed when contributing to this package. It is adapted from Documenter.jl.","category":"page"},{"location":"contributing/#Branches","page":"Contributing","title":"Branches","text":"","category":"section"},{"location":"contributing/","page":"Contributing","title":"Contributing","text":"release-* branches are used for tagged minor versions of this package. This follows the same approach used in the main Julia repository, albeit on a much more modest scale.","category":"page"},{"location":"contributing/","page":"Contributing","title":"Contributing","text":"Please open pull requests against the master branch rather than any of the release-* branches whenever possible.","category":"page"},{"location":"contributing/#Backports","page":"Contributing","title":"Backports","text":"","category":"section"},{"location":"contributing/","page":"Contributing","title":"Contributing","text":"Bug fixes are backported to the release-* branches using git cherry-pick -x by a EpiAware member and will become available in point releases of that particular minor version of the package.","category":"page"},{"location":"contributing/","page":"Contributing","title":"Contributing","text":"Feel free to nominate commits that should be backported by opening an issue. Requests for new point releases to be tagged in METADATA.jl can also be made in the same way.","category":"page"},{"location":"contributing/#release-*-branches","page":"Contributing","title":"release-* branches","text":"","category":"section"},{"location":"contributing/","page":"Contributing","title":"Contributing","text":"Each new minor version x.y.0 gets a branch called release-x.y (a protected branch).\nNew versions are usually tagged only from the release-x.y branches.\nFor patch releases, changes get backported to the release-x.y branch via a single PR with the standard name \"Backports for x.y.z\" and label \"Type: Backport\". The PR message links to all the PRs that are providing commits to the backport. The PR gets merged as a merge commit (i.e. not squashed).\nThe old release-* branches may be removed once they have outlived their usefulness.\nPatch version milestones are used to keep track of which PRs get backported etc.","category":"page"},{"location":"contributing/#Style-Guide","page":"Contributing","title":"Style Guide","text":"","category":"section"},{"location":"contributing/","page":"Contributing","title":"Contributing","text":"Follow the style of the surrounding text when making changes. When adding new features please try to stick to the following points whenever applicable. This project follows the SciML style guide.","category":"page"},{"location":"contributing/#Tests","page":"Contributing","title":"Tests","text":"","category":"section"},{"location":"contributing/#Unit-tests","page":"Contributing","title":"Unit tests","text":"","category":"section"},{"location":"contributing/","page":"Contributing","title":"Contributing","text":"As is conventional for Julia packages, unit tests are located at test/*.jl with the entrypoint test/runtests.jl.","category":"page"},{"location":"contributing/#End-to-end-testing","page":"Contributing","title":"End to end testing","text":"","category":"section"},{"location":"contributing/","page":"Contributing","title":"Contributing","text":"Tests that build example package docs from source and inspect the results (end to end tests) are located in /test/examples. The main entry points are test/examples/make.jl for building and test/examples/test.jl for doing some basic checks on the generated outputs.","category":"page"},{"location":"lib/public/#Public-Documentation","page":"Public API","title":"Public Documentation","text":"","category":"section"},{"location":"lib/public/","page":"Public API","title":"Public API","text":"Documentation for EpiAware.jl's public interface.","category":"page"},{"location":"lib/public/","page":"Public API","title":"Public API","text":"See the Internals section of the manual for internal package docs covering all submodules.","category":"page"},{"location":"lib/public/#Contents","page":"Public API","title":"Contents","text":"","category":"section"},{"location":"lib/public/","page":"Public API","title":"Public API","text":"Pages = [\"public.md\"]\nDepth = 2:2","category":"page"},{"location":"lib/public/#Index","page":"Public API","title":"Index","text":"","category":"section"},{"location":"lib/public/","page":"Public API","title":"Public API","text":"Pages = [\"public.md\"]","category":"page"},{"location":"lib/public/#Public-API","page":"Public API","title":"Public API","text":"","category":"section"},{"location":"#EpiAware.jl","page":"EpiAware.jl: Real-time epidemic monitoring","title":"EpiAware.jl","text":"","category":"section"},{"location":"","page":"EpiAware.jl: Real-time epidemic monitoring","title":"EpiAware.jl: Real-time epidemic monitoring","text":"Infectious disease situational awareness modelling toolkit for Julia.","category":"page"},{"location":"","page":"EpiAware.jl: Real-time epidemic monitoring","title":"EpiAware.jl: Real-time epidemic monitoring","text":"A package for building and fitting situational awareness models for infectious diseases. The package is designed to be flexible and extensible, and to provide a consistent interface for fitting and simulating models.","category":"page"},{"location":"#Package-Features","page":"EpiAware.jl: Real-time epidemic monitoring","title":"Package Features","text":"","category":"section"},{"location":"","page":"EpiAware.jl: Real-time epidemic monitoring","title":"EpiAware.jl: Real-time epidemic monitoring","text":"Flexible: The package is designed to be flexible and extensible, and to provide a consistent interface for fitting and simulating models.\nModular: The package is designed to be modular, with a clear separation between the model and the data.\nExtensible: The package is designed to be extensible, with a clear separation between the model and the data.\nConsistent: The package is designed to provide a consistent interface for fitting and simulating models.\nEfficient: The package is designed to be efficient, with a clear separation between the model and the data.","category":"page"},{"location":"","page":"EpiAware.jl: Real-time epidemic monitoring","title":"EpiAware.jl: Real-time epidemic monitoring","text":"See the Index for the complete list of documented functions and types.","category":"page"},{"location":"#Manual-Outline","page":"EpiAware.jl: Real-time epidemic monitoring","title":"Manual Outline","text":"","category":"section"},{"location":"","page":"EpiAware.jl: Real-time epidemic monitoring","title":"EpiAware.jl: Real-time epidemic monitoring","text":"Pages = [\n \"man/contributing.md\",\n]\nDepth = 1","category":"page"},{"location":"#Library-Outline","page":"EpiAware.jl: Real-time epidemic monitoring","title":"Library Outline","text":"","category":"section"},{"location":"","page":"EpiAware.jl: Real-time epidemic monitoring","title":"EpiAware.jl: Real-time epidemic monitoring","text":"Pages = [\"lib/public.md\", \"lib/internals.md\"]","category":"page"},{"location":"#main-index","page":"EpiAware.jl: Real-time epidemic monitoring","title":"Index","text":"","category":"section"},{"location":"","page":"EpiAware.jl: Real-time epidemic monitoring","title":"EpiAware.jl: Real-time epidemic monitoring","text":"Pages = [\"lib/public.md\"]","category":"page"}] +[{"location":"checklist/#Checklists","page":"Release checklist","title":"Checklists","text":"","category":"section"},{"location":"checklist/","page":"Release checklist","title":"Release checklist","text":"The purpose of this page is to collate a series of checklists for commonly performed changes to the source code of EpiAware. It has been adapted from Documenter.jl.","category":"page"},{"location":"checklist/","page":"Release checklist","title":"Release checklist","text":"In each case, copy the checklist into the description of the pull request.","category":"page"},{"location":"checklist/#Making-a-release","page":"Release checklist","title":"Making a release","text":"","category":"section"},{"location":"checklist/","page":"Release checklist","title":"Release checklist","text":"In preparation for a release, use the following checklist. These steps should be performed on a branch with an open pull request, either for a topic branch, or for a new branch release-1.y.z (\"Release version 1.y.z\") if multiple changes have accumulated on the master branch since the last release.","category":"page"},{"location":"checklist/","page":"Release checklist","title":"Release checklist","text":"## Pre-release\n\n - [ ] Change the version number in `Project.toml`\n * If the release is breaking, increment MAJOR\n * If the release adds a new user-visible feature, increment MINOR\n * Otherwise (bug-fixes, documentation improvements), increment PATCH\n - [ ] Update `CHANGELOG.md`, following the existing style (in particular, make sure that the change log for this version has the correct version number and date).\n - [ ] Run `make changelog`, to make sure that all the issue references in `CHANGELOG.md` are up to date.\n - [ ] Check that the commit messages in this PR do not contain `[ci skip]`\n - [ ] Run https://github.com/JuliaDocs/Documenter.jl/actions/workflows/regression-tests.yml\n using a `workflow_dispatch` trigger to check for any changes that broke extensions.\n\n## The release\n\n - [ ] After merging the pull request, tag the release. There are two options for this:\n\n 1. [Comment `[at]JuliaRegistrator register` on the GitHub commit.](https://github.com/JuliaRegistries/Registrator.jl#via-the-github-app)\n 2. Use [JuliaHub's package registration feature](https://help.juliahub.com/juliahub/stable/contribute/#registrator) to trigger the registration.\n\n Either of those should automatically publish a new version to the Julia registry.\n - Once registered, the `TagBot.yml` workflow should create a tag, and rebuild the documentation for this tag.\n - These steps can take quite a bit of time (1 hour or more), so don't be surprised if the new documentation takes a while to appear.","category":"page"},{"location":"release-notes/","page":"Release notes","title":"Release notes","text":"EditURL = \"https://github.com/JuliaDocs/Documenter.jl/blob/master/CHANGELOG.md\"","category":"page"},{"location":"release-notes/#Release-notes","page":"Release notes","title":"Release notes","text":"","category":"section"},{"location":"release-notes/","page":"Release notes","title":"Release notes","text":"The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.","category":"page"},{"location":"release-notes/#Unreleased","page":"Release notes","title":"Unreleased","text":"","category":"section"},{"location":"release-notes/#Added","page":"Release notes","title":"Added","text":"","category":"section"},{"location":"release-notes/#Changed","page":"Release notes","title":"Changed","text":"","category":"section"},{"location":"release-notes/#Fixed","page":"Release notes","title":"Fixed","text":"","category":"section"},{"location":"lib/internals/#internal-Documentation","page":"Internals","title":"internal Documentation","text":"","category":"section"},{"location":"lib/internals/","page":"Internals","title":"Internals","text":"Documentation for EpiAware.jl's internal interface.","category":"page"},{"location":"lib/internals/#Contents","page":"Internals","title":"Contents","text":"","category":"section"},{"location":"lib/internals/","page":"Internals","title":"Internals","text":"Pages = [\"internal.md\"]\nDepth = 2:2","category":"page"},{"location":"lib/internals/#Index","page":"Internals","title":"Index","text":"","category":"section"},{"location":"lib/internals/","page":"Internals","title":"Internals","text":"Pages = [\"internals.md\"]","category":"page"},{"location":"lib/internals/#Internal-API","page":"Internals","title":"Internal API","text":"","category":"section"},{"location":"lib/internals/","page":"Internals","title":"Internals","text":"Modules = [EpiAware]\nPublic = false","category":"page"},{"location":"lib/internals/#EpiAware.NegativeBinomialMeanClust-Tuple{Any, Any}","page":"Internals","title":"EpiAware.NegativeBinomialMeanClust","text":"NegativeBinomialMeanClust NegativeBinomialMeanClust(μ, α)\n\nCompute the mean-cluster factor negative binomial distribution.\n\nArguments\n\nμ: The mean of the distribution.\nα: The clustering factor parameter.\n\nReturns\n\nA NegativeBinomial distribution object.\n\n\n\nSignatures\n\nNegativeBinomialMeanClust(\n μ,\n α\n) -> Distributions.NegativeBinomial\n\n\n\n\nMethods\n\nNegativeBinomialMeanClust(μ, α)\n\ndefined at /home/runner/work/Rt-without-renewal/Rt-without-renewal/src/utilities.jl:210.\n\n\n\n\n\n","category":"method"},{"location":"lib/internals/#EpiAware.generate_observation_kernel-Tuple{Any, Any}","page":"Internals","title":"EpiAware.generate_observation_kernel","text":"generateobservationkernel generateobservationkernel(delayint, timehorizon)\n\nGenerate an observation kernel matrix based on the given delay interval and time horizon.\n\nArguments\n\ndelay_int::Vector{Float64}: The delay PMF vector.\ntime_horizon::Int: The number of time steps of the observation period.\n\nReturns\n\nK::SparseMatrixCSC{Float64, Int}: The observation kernel matrix.\n\n\n\nSignatures\n\ngenerate_observation_kernel(delay_int, time_horizon) -> Any\n\n\n\n\nMethods\n\ngenerate_observation_kernel(delay_int, time_horizon)\n\ndefined at /home/runner/work/Rt-without-renewal/Rt-without-renewal/src/utilities.jl:229.\n\n\n\n\n\n","category":"method"},{"location":"lib/internals/#EpiAware.neg_MGF-Tuple{Any, AbstractVector}","page":"Internals","title":"EpiAware.neg_MGF","text":"negMGF negMGF(r, w::AbstractVector)\n\nCompute the negative moment generating function (MGF) for a given rate r and weights w.\n\nArguments\n\nr: The rate parameter.\nw: An abstract vector of weights.\n\nReturns\n\nThe value of the negative MGF.\n\n\n\nSignatures\n\nneg_MGF(r, w::AbstractVector) -> Any\n\n\n\n\nMethods\n\nneg_MGF(r, w)\n\ndefined at /home/runner/work/Rt-without-renewal/Rt-without-renewal/src/utilities.jl:132.\n\n\n\n\n\n","category":"method"},{"location":"contributing/#Contributing","page":"Contributing","title":"Contributing","text":"","category":"section"},{"location":"contributing/","page":"Contributing","title":"Contributing","text":"This page details the some of the guidelines that should be followed when contributing to this package. It is adapted from Documenter.jl.","category":"page"},{"location":"contributing/#Branches","page":"Contributing","title":"Branches","text":"","category":"section"},{"location":"contributing/","page":"Contributing","title":"Contributing","text":"release-* branches are used for tagged minor versions of this package. This follows the same approach used in the main Julia repository, albeit on a much more modest scale.","category":"page"},{"location":"contributing/","page":"Contributing","title":"Contributing","text":"Please open pull requests against the master branch rather than any of the release-* branches whenever possible.","category":"page"},{"location":"contributing/#Backports","page":"Contributing","title":"Backports","text":"","category":"section"},{"location":"contributing/","page":"Contributing","title":"Contributing","text":"Bug fixes are backported to the release-* branches using git cherry-pick -x by a EpiAware member and will become available in point releases of that particular minor version of the package.","category":"page"},{"location":"contributing/","page":"Contributing","title":"Contributing","text":"Feel free to nominate commits that should be backported by opening an issue. Requests for new point releases to be tagged in METADATA.jl can also be made in the same way.","category":"page"},{"location":"contributing/#release-*-branches","page":"Contributing","title":"release-* branches","text":"","category":"section"},{"location":"contributing/","page":"Contributing","title":"Contributing","text":"Each new minor version x.y.0 gets a branch called release-x.y (a protected branch).\nNew versions are usually tagged only from the release-x.y branches.\nFor patch releases, changes get backported to the release-x.y branch via a single PR with the standard name \"Backports for x.y.z\" and label \"Type: Backport\". The PR message links to all the PRs that are providing commits to the backport. The PR gets merged as a merge commit (i.e. not squashed).\nThe old release-* branches may be removed once they have outlived their usefulness.\nPatch version milestones are used to keep track of which PRs get backported etc.","category":"page"},{"location":"contributing/#Style-Guide","page":"Contributing","title":"Style Guide","text":"","category":"section"},{"location":"contributing/","page":"Contributing","title":"Contributing","text":"Follow the style of the surrounding text when making changes. When adding new features please try to stick to the following points whenever applicable. This project follows the SciML style guide.","category":"page"},{"location":"contributing/#Tests","page":"Contributing","title":"Tests","text":"","category":"section"},{"location":"contributing/#Unit-tests","page":"Contributing","title":"Unit tests","text":"","category":"section"},{"location":"contributing/","page":"Contributing","title":"Contributing","text":"As is conventional for Julia packages, unit tests are located at test/*.jl with the entrypoint test/runtests.jl.","category":"page"},{"location":"contributing/#End-to-end-testing","page":"Contributing","title":"End to end testing","text":"","category":"section"},{"location":"contributing/","page":"Contributing","title":"Contributing","text":"Tests that build example package docs from source and inspect the results (end to end tests) are located in /test/examples. The main entry points are test/examples/make.jl for building and test/examples/test.jl for doing some basic checks on the generated outputs.","category":"page"},{"location":"examples/getting_started/","page":"Getting started","title":"Getting started","text":"\n\n\n\n\n\n\n\n

              Getting stated with EpiAware

              This tutorial introduces the basic functionality of EpiAware. EpiAware is a package for making inferences on epidemiological case/determined infection data using a model-based approach.

              EpiAware models

              The models we consider are discrete-time $t = 1,\\dots, T$ with a latent random process, $Z_t$ generating stochasticity in the number of new infections $I_t$ at each time step. Observations are treated as downstream random variables determined by the actual infections and a model of infection to observation delay.

              Mathematical definition

              $$\\begin{align}\nZ_\\cdot &\\sim \\mathcal{P}(\\mathbb{R}^T) | \\theta_Z, \\\\\nI_0 &\\sim f_0(\\theta_I), \\\\\nI_t &\\sim g_I(\\{I_s, Z_s\\}_{s < t}, \\theta_{I}), \\\\\ny_t &\\sim f_O(\\{I_s\\}_{s \\leq t}, \\theta_{O}).\n\\end{align}$$

              Where, $\\mathcal{P}(\\mathbb{R}^T) | \\theta_Z$ is a parametric process on $\\mathbb{R}^T$. $f_0$ and $f_O$ are parametric distributions on, respectively, the initial number of infections and the observed case data conditional on underlying infections. $g_I$ is distribution of new infections conditional on infections and latent process in the past. Note that we assume that new infections are conditional on the strict past, whereas new observations can depend on infections on the same time step.

              Code structure outline

              An EpiAware model in code is created from three modular components:

              Reproductive number

              EpiAware models do not need to specify a time-varying reproductive number $\\mathcal{R}_t$ to generate $I_\\cdot$, however, this is often a quantity of interest. When not directly used we will typically back-calculate $\\mathcal{R}_t$ from the generated infections:

              $$\\mathcal{R}_t = {I_t \\over \\sum_{s \\geq 1} g_s I_{t-s} }.$$

              Where $g_s$ is a discrete generation interval. For this reason, even when not using a reproductive number approach directly, we ask for a generation interval.

              \n\n
              begin\n    using EpiAware\n    using Turing\n    using Distributions\n    using StatsPlots\n    using Random\n    using DynamicPPL\n    using Statistics\n    using DataFramesMeta\n    using LinearAlgebra\nend
              \n\n\n\n

              Random walk LatentModel

              As an example, we choose the latent process as a random walk with parameters $\\theta_Z$:

              Conditional on the parameters the random walk is then generated by white noise:

              $$\\begin{align}\nZ_t &= Z_0 + \\sigma_{RW} \\sum_{t= 1}^T \\epsilon_t, \\\\\n\\epsilon_t &\\sim \\mathcal{N}(0,1).\n\\end{align}$$

              In EpiAware we provide a constructor for random walk latent models with priors for $\\theta_Z$. We choose priors,

              $$\\begin{align}\nZ_0 &\\sim \\mathcal{N}(0,1),\\\\\n\\sigma^2_Z &\\sim \\text{HalfNormal}(0.01).\n\\end{align}$$

              \n\n
              rwp = EpiAware.RandomWalk(Normal(),\n    truncated(Normal(0.0, 0.02), 0.0, Inf))
              \n
              RandomWalk{Normal{Float64}, Truncated{Normal{Float64}, Continuous, Float64, Float64, Float64}}(Distributions.Normal{Float64}(μ=0.0, σ=1.0), Truncated(Distributions.Normal{Float64}(μ=0.0, σ=0.02); lower=0.0, upper=Inf))
              \n\n\n

              Direct infection EpiModel

              This is a simple model where the unobserved log-infections are directly generated by the latent process $Z$.

              $$\\log I_t = \\log I_0 + Z_t.$$

              As discussed above, we still ask for a defined generation interval, which can be used to calculate $\\mathcal{R}_t$.

              \n\n
              truth_GI = Gamma(2, 5)
              \n
              Distributions.Gamma{Float64}(α=2.0, θ=5.0)
              \n\n\n

              The EpiData constructor performs double interval censoring to convert our continuous estimate of the generation interval into a discretized version. We also implement right truncation using the keyword D_gen.

              \n\n
              model_data = EpiData(truth_GI, D_gen = 10.0)
              \n
              EpiData{Float64, typeof(exp)}([0.056722565637507084, 0.0944813112842689, 0.11668723746973605, 0.12773814268679387, 0.130948497124998, 0.1287976856448402, 0.12312384970427602, 0.11527489380975135, 0.10622581663782862], 9, exp)
              \n\n\n

              We can supply a prior for the initial log_infections.

              \n\n
              log_I0_prior = Normal(log(100.0), 1.0)
              \n
              Distributions.Normal{Float64}(μ=4.605170185988092, σ=1.0)
              \n\n\n

              And construct the EpiModel.

              \n\n
              epi_model = DirectInfections(model_data, log_I0_prior)
              \n
              DirectInfections{Normal{Float64}}(EpiData{Float64, typeof(exp)}([0.056722565637507084, 0.0944813112842689, 0.11668723746973605, 0.12773814268679387, 0.130948497124998, 0.1287976856448402, 0.12312384970427602, 0.11527489380975135, 0.10622581663782862], 9, exp), Distributions.Normal{Float64}(μ=4.605170185988092, σ=1.0))
              \n\n\n

              Delayed Observations ObservationModel

              The observation model is a negative binomial distribution with mean μ and cluster factor 1 / r. Delays are implemented as the action of a sparse kernel on the infections $I(t)$.

              $$\\begin{align}\ny_t &\\sim \\text{NegBinomial}(\\mu = \\sum_{s\\geq 0} K[t, t-s] I(s), r), \\\\\n1 / r &\\sim \\text{Gamma}(3, 0.05/3).\n\\end{align}$$

              \n\n\n

              We also set up the inference to occur over 100 days.

              \n\n
              time_horizon = 100
              \n
              100
              \n\n\n

              We choose a simple observation model where infections are observed 0, 1, 2, 3 days later with equal probability.

              \n\n
              obs_model = EpiAware.DelayObservations(\n    fill(0.25, 4),\n    time_horizon,\n    truncated(Gamma(5, 0.05 / 5), 1e-3, 1.0)\n)
              \n
              DelayObservations{Float64, Truncated{Gamma{Float64}, Continuous, Float64, Float64, Float64}}(sparse(Integer[1, 2, 3, 4, 2, 3, 4, 5, 3, 4  …  97, 98, 99, 100, 98, 99, 100, 99, 100, 100], Integer[1, 1, 1, 1, 2, 2, 2, 2, 3, 3  …  97, 97, 97, 97, 98, 98, 98, 99, 99, 100], [0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25  …  0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25], 100, 100), Truncated(Distributions.Gamma{Float64}(α=5.0, θ=0.01); lower=0.001, upper=1.0))
              \n\n\n

              Generate cases from the EpiAware model

              Having chosen an EpiModel, LatentModel and ObservationModel, we can implement the model as a Turing model using make_epi_aware.

              By giving missing to the first argument, we indicate that case data will be generated from the model rather than treated as fixed.

              \n\n
              full_epi_aware_mdl = make_epi_aware(missing, time_horizon;\n    epi_model = epi_model,\n    latent_model = rwp,\n    observation_model = obs_model)
              \n
              Model{typeof(make_epi_aware), (:y_t, :time_steps), (:epi_model, :latent_model, :observation_model, :pos_shift), (:y_t,), Tuple{Missing, Int64}, Tuple{DirectInfections{Normal{Float64}}, RandomWalk{Normal{Float64}, Truncated{Normal{Float64}, Continuous, Float64, Float64, Float64}}, DelayObservations{Float64, Truncated{Gamma{Float64}, Continuous, Float64, Float64, Float64}}, Float64}, DefaultContext}(EpiAware.make_epi_aware, (y_t = missing, time_steps = 100), (epi_model = DirectInfections{Normal{Float64}}(EpiData{Float64, typeof(exp)}([0.056722565637507084, 0.0944813112842689, 0.11668723746973605, 0.12773814268679387, 0.130948497124998, 0.1287976856448402, 0.12312384970427602, 0.11527489380975135, 0.10622581663782862], 9, exp), Distributions.Normal{Float64}(μ=4.605170185988092, σ=1.0)), latent_model = RandomWalk{Normal{Float64}, Truncated{Normal{Float64}, Continuous, Float64, Float64, Float64}}(Distributions.Normal{Float64}(μ=0.0, σ=1.0), Truncated(Distributions.Normal{Float64}(μ=0.0, σ=0.02); lower=0.0, upper=Inf)), observation_model = DelayObservations{Float64, Truncated{Gamma{Float64}, Continuous, Float64, Float64, Float64}}(sparse(Integer[1, 2, 3, 4, 2, 3, 4, 5, 3, 4  …  97, 98, 99, 100, 98, 99, 100, 99, 100, 100], Integer[1, 1, 1, 1, 2, 2, 2, 2, 3, 3  …  97, 97, 97, 97, 98, 98, 98, 99, 99, 100], [0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25  …  0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25], 100, 100), Truncated(Distributions.Gamma{Float64}(α=5.0, θ=0.01); lower=0.001, upper=1.0)), pos_shift = 1.0e-6), DefaultContext())
              \n\n\n

              We choose some fixed parameters:

              \n\n
              fixed_parameters = (rw_init = 0.0, init_incidence = log(100.0))
              \n
              (rw_init = 0.0, init_incidence = 4.605170185988092)
              \n\n\n

              We fix these parameters using fix, and generate a random epidemic.

              \n\n
              cond_generative_model = fix(full_epi_aware_mdl, fixed_parameters)
              \n
              Model{typeof(make_epi_aware), (:y_t, :time_steps), (:epi_model, :latent_model, :observation_model, :pos_shift), (:y_t,), Tuple{Missing, Int64}, Tuple{DirectInfections{Normal{Float64}}, RandomWalk{Normal{Float64}, Truncated{Normal{Float64}, Continuous, Float64, Float64, Float64}}, DelayObservations{Float64, Truncated{Gamma{Float64}, Continuous, Float64, Float64, Float64}}, Float64}, DynamicPPL.FixedContext{@NamedTuple{rw_init::Float64, init_incidence::Float64}, DefaultContext}}(EpiAware.make_epi_aware, (y_t = missing, time_steps = 100), (epi_model = DirectInfections{Normal{Float64}}(EpiData{Float64, typeof(exp)}([0.056722565637507084, 0.0944813112842689, 0.11668723746973605, 0.12773814268679387, 0.130948497124998, 0.1287976856448402, 0.12312384970427602, 0.11527489380975135, 0.10622581663782862], 9, exp), Distributions.Normal{Float64}(μ=4.605170185988092, σ=1.0)), latent_model = RandomWalk{Normal{Float64}, Truncated{Normal{Float64}, Continuous, Float64, Float64, Float64}}(Distributions.Normal{Float64}(μ=0.0, σ=1.0), Truncated(Distributions.Normal{Float64}(μ=0.0, σ=0.02); lower=0.0, upper=Inf)), observation_model = DelayObservations{Float64, Truncated{Gamma{Float64}, Continuous, Float64, Float64, Float64}}(sparse(Integer[1, 2, 3, 4, 2, 3, 4, 5, 3, 4  …  97, 98, 99, 100, 98, 99, 100, 99, 100, 100], Integer[1, 1, 1, 1, 2, 2, 2, 2, 3, 3  …  97, 97, 97, 97, 98, 98, 98, 99, 99, 100], [0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25  …  0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25], 100, 100), Truncated(Distributions.Gamma{Float64}(α=5.0, θ=0.01); lower=0.001, upper=1.0)), pos_shift = 1.0e-6), FixedContext((rw_init = 0.0, init_incidence = 4.605170185988092), DynamicPPL.DefaultContext()))
              \n\n
              random_epidemic = rand(cond_generative_model)
              \n
              (ϵ_t = [-0.3245105752051425, 0.5841792768429854, 0.837698738466377, 0.04991144411048826, 0.43709682639657876, 1.5113816873648402, 0.5183464762750044, -1.2951234902508262, -0.8475356353822158, 0.5731492794677614  …  -0.9718005452114998, 0.2986685041305902, 0.8778707060932857, 0.2779274792378409, -1.3849265413496943, 0.9094660271125533, 1.2532018893752603, 1.5863018030422251, -0.04686725620120323, 0.4638612794817786], σ²_RW = 0.026203541122590525, neg_bin_cluster_factor = 0.0472919247750861, var\"y_t[1]\" = 24, var\"y_t[2]\" = 53, var\"y_t[3]\" = 106, var\"y_t[4]\" = 105, var\"y_t[5]\" = 121, var\"y_t[6]\" = 115, var\"y_t[7]\" = 149, var\"y_t[8]\" = 185, var\"y_t[9]\" = 121, var\"y_t[10]\" = 134, var\"y_t[11]\" = 124, var\"y_t[12]\" = 75, var\"y_t[13]\" = 105, var\"y_t[14]\" = 95, var\"y_t[15]\" = 118, var\"y_t[16]\" = 126, var\"y_t[17]\" = 125, var\"y_t[18]\" = 94, var\"y_t[19]\" = 124, var\"y_t[20]\" = 64, var\"y_t[21]\" = 59, var\"y_t[22]\" = 137, var\"y_t[23]\" = 75, var\"y_t[24]\" = 83, var\"y_t[25]\" = 157, var\"y_t[26]\" = 136, var\"y_t[27]\" = 104, var\"y_t[28]\" = 131, var\"y_t[29]\" = 228, var\"y_t[30]\" = 173, var\"y_t[31]\" = 156, var\"y_t[32]\" = 165, var\"y_t[33]\" = 183, var\"y_t[34]\" = 182, var\"y_t[35]\" = 112, var\"y_t[36]\" = 145, var\"y_t[37]\" = 161, var\"y_t[38]\" = 117, var\"y_t[39]\" = 169, var\"y_t[40]\" = 173, var\"y_t[41]\" = 108, var\"y_t[42]\" = 123, var\"y_t[43]\" = 69, var\"y_t[44]\" = 52, var\"y_t[45]\" = 105, var\"y_t[46]\" = 132, var\"y_t[47]\" = 117, var\"y_t[48]\" = 99, var\"y_t[49]\" = 119, var\"y_t[50]\" = 93, var\"y_t[51]\" = 80, var\"y_t[52]\" = 96, var\"y_t[53]\" = 77, var\"y_t[54]\" = 89, var\"y_t[55]\" = 166, var\"y_t[56]\" = 104, var\"y_t[57]\" = 74, var\"y_t[58]\" = 115, var\"y_t[59]\" = 126, var\"y_t[60]\" = 102, var\"y_t[61]\" = 146, var\"y_t[62]\" = 102, var\"y_t[63]\" = 157, var\"y_t[64]\" = 96, var\"y_t[65]\" = 156, var\"y_t[66]\" = 159, var\"y_t[67]\" = 92, var\"y_t[68]\" = 143, var\"y_t[69]\" = 83, var\"y_t[70]\" = 130, var\"y_t[71]\" = 121, var\"y_t[72]\" = 133, var\"y_t[73]\" = 208, var\"y_t[74]\" = 171, var\"y_t[75]\" = 186, var\"y_t[76]\" = 227, var\"y_t[77]\" = 218, var\"y_t[78]\" = 205, var\"y_t[79]\" = 189, var\"y_t[80]\" = 111, var\"y_t[81]\" = 182, var\"y_t[82]\" = 301, var\"y_t[83]\" = 199, var\"y_t[84]\" = 234, var\"y_t[85]\" = 194, var\"y_t[86]\" = 208, var\"y_t[87]\" = 393, var\"y_t[88]\" = 381, var\"y_t[89]\" = 406, var\"y_t[90]\" = 429, var\"y_t[91]\" = 169, var\"y_t[92]\" = 405, var\"y_t[93]\" = 256, var\"y_t[94]\" = 300, var\"y_t[95]\" = 330, var\"y_t[96]\" = 240, var\"y_t[97]\" = 245, var\"y_t[98]\" = 287, var\"y_t[99]\" = 439, var\"y_t[100]\" = 610)
              \n\n
              true_infections = generated_quantities(cond_generative_model, random_epidemic).I_t
              \n
              100-element Vector{Float64}:\n  94.88256898856683\n 104.29298246375241\n 119.43910104412493\n 120.4080084136958\n 129.23612130314984\n 165.0578731221061\n 179.50508482958563\n   ⋮\n 304.0060267388828\n 352.2240260411064\n 431.4404807480081\n 557.7508150421348\n 553.5353707088188\n 596.6992603591709
              \n\n
              generated_obs = generated_quantities(cond_generative_model, random_epidemic).generated_y_t
              \n
              100-element Vector{Int64}:\n  24\n  53\n 106\n 105\n 121\n 115\n 149\n   ⋮\n 330\n 240\n 245\n 287\n 439\n 610
              \n\n\n\n\n","category":"page"},{"location":"examples/getting_started/#Inference","page":"Getting started","title":"Inference","text":"","category":"section"},{"location":"examples/getting_started/","page":"Getting started","title":"Getting started","text":"
              \n

              Fixing $Z_0 = 0$ for the random walk was based on inference principles; in this model $Z_0$ and $\\log I_0$ are non-identifiable.

              However, we now treat the generated data as truth_data and make inference without fixing any other parameters.

              We do the inference by MCMC/NUTS using the Turing NUTS sampler with default warm-up steps.

              \n\n
              truth_data = generated_obs
              \n
              100-element Vector{Int64}:\n  24\n  53\n 106\n 105\n 121\n 115\n 149\n   ⋮\n 330\n 240\n 245\n 287\n 439\n 610
              \n\n
              inference_mdl = fix(\n    make_epi_aware(truth_data, time_horizon; epi_model = epi_model,\n        latent_model = rwp, observation_model = obs_model),\n    (rw_init = 0.0,)\n)
              \n
              Model{typeof(make_epi_aware), (:y_t, :time_steps), (:epi_model, :latent_model, :observation_model, :pos_shift), (), Tuple{Vector{Int64}, Int64}, Tuple{DirectInfections{Normal{Float64}}, RandomWalk{Normal{Float64}, Truncated{Normal{Float64}, Continuous, Float64, Float64, Float64}}, DelayObservations{Float64, Truncated{Gamma{Float64}, Continuous, Float64, Float64, Float64}}, Float64}, DynamicPPL.FixedContext{@NamedTuple{rw_init::Float64}, DefaultContext}}(EpiAware.make_epi_aware, (y_t = [24, 53, 106, 105, 121, 115, 149, 185, 121, 134  …  169, 405, 256, 300, 330, 240, 245, 287, 439, 610], time_steps = 100), (epi_model = DirectInfections{Normal{Float64}}(EpiData{Float64, typeof(exp)}([0.056722565637507084, 0.0944813112842689, 0.11668723746973605, 0.12773814268679387, 0.130948497124998, 0.1287976856448402, 0.12312384970427602, 0.11527489380975135, 0.10622581663782862], 9, exp), Distributions.Normal{Float64}(μ=4.605170185988092, σ=1.0)), latent_model = RandomWalk{Normal{Float64}, Truncated{Normal{Float64}, Continuous, Float64, Float64, Float64}}(Distributions.Normal{Float64}(μ=0.0, σ=1.0), Truncated(Distributions.Normal{Float64}(μ=0.0, σ=0.02); lower=0.0, upper=Inf)), observation_model = DelayObservations{Float64, Truncated{Gamma{Float64}, Continuous, Float64, Float64, Float64}}(sparse(Integer[1, 2, 3, 4, 2, 3, 4, 5, 3, 4  …  97, 98, 99, 100, 98, 99, 100, 99, 100, 100], Integer[1, 1, 1, 1, 2, 2, 2, 2, 3, 3  …  97, 97, 97, 97, 98, 98, 98, 99, 99, 100], [0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25  …  0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25], 100, 100), Truncated(Distributions.Gamma{Float64}(α=5.0, θ=0.01); lower=0.001, upper=1.0)), pos_shift = 1.0e-6), FixedContext((rw_init = 0.0,), DynamicPPL.DefaultContext()))
              \n\n
              chn = sample(inference_mdl,\n    NUTS(; adtype = AutoReverseDiff(true)),\n    MCMCThreads(),\n    250,\n    4;\n    drop_warmup = true)
              \n
              iterationchainϵ_t[1]ϵ_t[2]ϵ_t[3]ϵ_t[4]ϵ_t[5]ϵ_t[6]...
              112611.158851.659530.922999-1.322090.3266220.821712
              21271-0.694295-0.88852-0.3311721.855460.626332-0.37274
              31281-0.0574015-0.7028910.626630.451822-0.675678-0.462883
              412910.0387053-0.7032662.080680.446057-1.19198-0.867791
              513010.615644-0.6864031.55179-1.01553-0.09418611.28523
              613112.55703-1.009341.21827-0.1504631.61262-0.0782016
              713210.08887392.164940.04079230.5806720.3559921.49668
              81331-0.4101452.1424-0.5912011.615730.5469741.2129
              91341-0.41357-1.262480.538455-0.889234-1.056741.4544
              101351-0.7451.218350.834888-0.0217767-0.3205411.40438
              ...
              \n\n\n

              Predictive plotting

              We can spaghetti plot generated case data from the version of the model which hasn't conditioned on case data using posterior parameters inferred from the version conditioned on observed data. This is known as posterior predictive checking, and is a useful diagnostic tool for Bayesian inference (see here).

              Because we are using synthetic data we can also plot the model predictions for the unobserved infections and check that (at least in this example) we were able to capture some unobserved/latent variables in the process accurate.

              \n\n
              let\n    post_check_mdl = fix(full_epi_aware_mdl, (rw_init = 0.0,))\n    post_check_y_t = mapreduce(hcat, generated_quantities(post_check_mdl, chn)) do gen\n        gen.generated_y_t\n    end\n\n    predicted_I_t = mapreduce(hcat, generated_quantities(inference_mdl, chn)) do gen\n        gen.I_t\n    end\n\n    p1 = plot(post_check_y_t, c = :grey, alpha = 0.05, lab = \"\")\n    scatter!(p1, truth_data,\n        lab = \"Observed cases\",\n        xlabel = \"Time\",\n        ylabel = \"Cases\",\n        title = \"Post. predictive checking: cases\",\n        ylims = (-0.5, maximum(truth_data) * 1.5),\n        c = :green)\n\n    p2 = plot(predicted_I_t, c = :grey, alpha = 0.05, lab = \"\")\n    scatter!(p2, true_infections,\n        lab = \"Actual infections\",\n        xlabel = \"Time\",\n        ylabel = \"Unobserved Infections\",\n        title = \"Post. predictions: infections\",\n        ylims = (-0.5, maximum(true_infections) * 1.5),\n        c = :red)\n\n    plot(p1, p2,\n        layout = (1, 2),\n        size = (700, 400))\nend
              \n\n\n\n

              As well as checking the posterior predictions for latent infections, we can also check how well inference recovered unknown parameters, such as the random walk variance or the cluster factor of the negative binomial observations.

              \n\n
              let\n    parameters_to_plot = (:σ²_RW, :neg_bin_cluster_factor)\n\n    plts = map(parameters_to_plot) do name\n        var_samples = chn[name] |> vec\n        histogram(var_samples,\n            bins = 50,\n            norm = :pdf,\n            lw = 0,\n            fillalpha = 0.5,\n            lab = \"MCMC\")\n        vline!([getfield(random_epidemic, name)], lab = \"True value\")\n        title!(string(name))\n    end\n    plot(plts..., layout = (2, 1))\nend
              \n\n\n","category":"page"},{"location":"examples/getting_started/#Reproductive-number-back-calculation","page":"Getting started","title":"Reproductive number back-calculation","text":"","category":"section"},{"location":"examples/getting_started/","page":"Getting started","title":"Getting started","text":"
              \n

              As mentioned at the top, we don't directly use the concept of reproductive numbers in this note. However, we can back-calculate the implied $\\mathcal{R}(t)$ values, conditional on the specified generation interval being correct.

              Here we spaghetti plot posterior sampled time-varying reproductive numbers against the actual.

              \n\n
              let\n    n = epi_model.data.len_gen_int\n    Rt_denom = [dot(reverse(epi_model.data.gen_int), true_infections[(t - n):(t - 1)])\n                for t in (n + 1):length(true_infections)]\n    true_Rt = true_infections[(n + 1):end] ./ Rt_denom\n\n    predicted_Rt = mapreduce(hcat, generated_quantities(inference_mdl, chn)) do gen\n        _It = gen.I_t\n        _Rt_denom = [dot(reverse(epi_model.data.gen_int), _It[(t - n):(t - 1)])\n                     for t in (n + 1):length(_It)]\n        Rt = _It[(n + 1):end] ./ _Rt_denom\n    end\n\n    plt = plot((n + 1):time_horizon, predicted_Rt, c = :grey, alpha = 0.05, lab = \"\")\n    plot!(plt, (n + 1):time_horizon, true_Rt,\n        lab = \"true Rt\",\n        xlabel = \"Time\",\n        ylabel = \"Rt\",\n        title = \"Post. predictions: reproductive number\",\n        c = :red,\n        lw = 2)\nend
              \n\n\n","category":"page"},{"location":"examples/getting_started/","page":"Getting started","title":"Getting started","text":"EditURL = \"https://github.com/CDCgov/Rt-without-renewal/blob/main/docs/src/examples/getting_started.jl\"","category":"page"},{"location":"lib/public/#Public-Documentation","page":"Public API","title":"Public Documentation","text":"","category":"section"},{"location":"lib/public/","page":"Public API","title":"Public API","text":"Documentation for EpiAware.jl's public interface.","category":"page"},{"location":"lib/public/","page":"Public API","title":"Public API","text":"See the Internals section of the manual for internal package docs covering all submodules.","category":"page"},{"location":"lib/public/#Contents","page":"Public API","title":"Contents","text":"","category":"section"},{"location":"lib/public/","page":"Public API","title":"Public API","text":"Pages = [\"public.md\"]\nDepth = 2:2","category":"page"},{"location":"lib/public/#Index","page":"Public API","title":"Index","text":"","category":"section"},{"location":"lib/public/","page":"Public API","title":"Public API","text":"Pages = [\"public.md\"]","category":"page"},{"location":"lib/public/#Public-API","page":"Public API","title":"Public API","text":"","category":"section"},{"location":"#EpiAware.jl","page":"EpiAware.jl: Real-time epidemic monitoring","title":"EpiAware.jl","text":"","category":"section"},{"location":"","page":"EpiAware.jl: Real-time epidemic monitoring","title":"EpiAware.jl: Real-time epidemic monitoring","text":"Infectious disease situational awareness modelling toolkit for Julia.","category":"page"},{"location":"","page":"EpiAware.jl: Real-time epidemic monitoring","title":"EpiAware.jl: Real-time epidemic monitoring","text":"A package for building and fitting situational awareness models for infectious diseases. The package is designed to be flexible and extensible, and to provide a consistent interface for fitting and simulating models.","category":"page"},{"location":"#Package-Features","page":"EpiAware.jl: Real-time epidemic monitoring","title":"Package Features","text":"","category":"section"},{"location":"","page":"EpiAware.jl: Real-time epidemic monitoring","title":"EpiAware.jl: Real-time epidemic monitoring","text":"Flexible: The package is designed to be flexible and extensible, and to provide a consistent interface for fitting and simulating models.\nModular: The package is designed to be modular, with a clear separation between the model and the data.\nExtensible: The package is designed to be extensible, with a clear separation between the model and the data.\nConsistent: The package is designed to provide a consistent interface for fitting and simulating models.\nEfficient: The package is designed to be efficient, with a clear separation between the model and the data.","category":"page"},{"location":"","page":"EpiAware.jl: Real-time epidemic monitoring","title":"EpiAware.jl: Real-time epidemic monitoring","text":"See the Index for the complete list of documented functions and types.","category":"page"},{"location":"#Manual-Outline","page":"EpiAware.jl: Real-time epidemic monitoring","title":"Manual Outline","text":"","category":"section"},{"location":"","page":"EpiAware.jl: Real-time epidemic monitoring","title":"EpiAware.jl: Real-time epidemic monitoring","text":"Pages = [\n \"man/contributing.md\",\n]\nDepth = 1","category":"page"},{"location":"#Library-Outline","page":"EpiAware.jl: Real-time epidemic monitoring","title":"Library Outline","text":"","category":"section"},{"location":"","page":"EpiAware.jl: Real-time epidemic monitoring","title":"EpiAware.jl: Real-time epidemic monitoring","text":"Pages = [\"lib/public.md\", \"lib/internals.md\"]","category":"page"},{"location":"#main-index","page":"EpiAware.jl: Real-time epidemic monitoring","title":"Index","text":"","category":"section"},{"location":"","page":"EpiAware.jl: Real-time epidemic monitoring","title":"EpiAware.jl: Real-time epidemic monitoring","text":"Pages = [\"lib/public.md\"]","category":"page"}] }