--- title: "Left truncation with delay_min" description: | "How to use the delay_min argument to account for delays that are only observed above a minimum value." output: bookdown::html_vignette2: fig_caption: yes code_folding: show number_sections: true pkgdown: as_is: true link-citations: true vignette: > %\VignetteIndexEntry{Left truncation with delay_min} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} bibliography: references.bib --- Some delays are only ever recorded above a minimum value. Generation intervals are often defined to exclude same day transmission. Case definitions sometimes require a minimum time between two events before a pair is recorded. In both settings the observed delays are a left truncated sample from the delay distribution of interest. Write $L$ for that minimum and $F$ for the delay distribution. Only delays above $L$ can enter the sample, so the observed delays follow the conditional distribution $$ F(t \mid T > L) = \frac{F(t) - F(L)}{1 - F(L)}, \qquad t > L . $$ Fitting $F$ directly to such a sample biases the estimate. The model has to explain the missing short delays by shifting the whole distribution. With right truncation at $D$ as well, the likelihood renormalises over $[L, D]$ $$ \frac{F(t) - F(L)}{F(D) - F(L)} . $$ The `delay_min` argument of `as_epidist_marginal_model()` sets $L$. It is passed to the [`primarycensored`](https://primarycensored.epinowcast.org/) likelihood as its `L` argument. `delay_min` is a duration in the same units as the delay, not a date. `delay_min` truncates on the same scale as the response, that is the observed delay between the primary window lower bound and the secondary event. Data should therefore be excluded on the observed delay rather than on the underlying continuous delay. # Setup ``` r library(epidist) library(ggplot2) library(dplyr) ``` # Simulate left truncated data We simulate an outbreak and a lognormal delay from each primary event. The parameters are chosen so that a large share of the distribution falls below the truncation point. ``` r set.seed(101) true_meanlog <- 1.0 true_sdlog <- 0.8 delay_min <- 2 obs <- simulate_gillespie(r = 0.2, seed = 101) |> simulate_secondary( dist = rlnorm, meanlog = true_meanlog, sdlog = true_sdlog ) ``` Roughly this share of the delay distribution lies below the truncation point. ``` r plnorm(delay_min, true_meanlog, true_sdlog) #> [1] 0.3506501 ``` `simulate_dates()` adds daily reporting windows and returns date. We then drop the pairs whose observed delay is below `delay_min`. ``` r obs_trunc <- obs |> simulate_dates() |> filter(as.numeric(sdate_lwr - pdate_lwr) >= delay_min) |> slice_sample(n = 500) linelist <- as_epidist_linelist_data(obs_trunc) ``` The observed delays are truncated below `delay_min`. ``` r plot_delays(linelist, delay_min = delay_min) ```
(\#fig:hist)Observed delays after left truncation. The dashed line marks the minimum delay.
(\#fig:params-plot)Posterior draws of the lognormal parameters, and of the natural scale mean and standard deviation. Dashed lines are the simulation values. The unadjusted model is biased away from them.
(\#fig:pdf-plot)Estimated delay distributions against the true lognormal density (black line).