EpiNow2 Stan Functions
observation_model.stan
Go to the documentation of this file.
1/**
2 * Apply day of the week effect to reports
3 *
4 * This function applies a day of the week effect to a vector of reports.
5 *
6 * @param reports Vector of reports to be adjusted.
7 * @param day_of_week Array of integers representing the day of the week for
8 * each report.
9 * @param effect Vector of day of week effects.
10 *
11 * @return A vector of reports adjusted for day of the week effects.
12 *
13 * @ingroup observation_model
14 */
15vector day_of_week_effect(vector reports, array[] int day_of_week,
16 vector effect) {
17 int wl = num_elements(effect);
18 vector[wl] scaled_effect = wl * effect;
19 return reports .* scaled_effect[day_of_week];
20}
21
22/**
23 * Scale observations by fraction reported
24 *
25 * This function scales a vector of reports by a fraction observed.
26 *
27 * @param reports Vector of reports to be scaled.
28 * @param fraction_observed Real value representing the fraction observed.
29 *
30 * @return A vector of scaled reports.
31 *
32 * @ingroup observation_model
33 */
34vector scale_obs(vector reports, real fraction_observed) {
35 int t = num_elements(reports);
36 vector[t] scaled_reports;
37 scaled_reports = reports * fraction_observed;
38 return(scaled_reports);
39}
40
41/**
42 * Truncate observed data by a truncation distribution
43 *
44 * This function truncates a vector of reports based on a truncation
45 * distribution.
46 *
47 * @param reports Vector of reports to be truncated.
48 * @param trunc_rev_cmf Vector representing the reverse cumulative mass function
49 * of the truncation distribution.
50 * @param reconstruct Integer flag indicating whether to reconstruct (1) or
51 * truncate (0) the data.
52 *
53 * @return A vector of truncated reports.
54 *
55 * @ingroup observation_model
56 */
57vector truncate_obs(vector reports, vector trunc_rev_cmf, int reconstruct) {
58 int t = num_elements(reports);
59 int trunc_max = num_elements(trunc_rev_cmf);
60 vector[t] trunc_reports = reports;
61 // Calculate cmf of truncation delay
62 int joint_max = min(t, trunc_max);
63 int first_t = t - joint_max + 1;
64 int first_trunc = trunc_max - joint_max + 1;
65
66 // Apply cdf of truncation delay to truncation max last entries in reports
67 if (reconstruct) {
68 trunc_reports[first_t:t] ./= trunc_rev_cmf[first_trunc:trunc_max];
69 } else {
70 trunc_reports[first_t:t] .*= trunc_rev_cmf[first_trunc:trunc_max];
71 }
72 return(trunc_reports);
73}
74
75/**
76 * Update log density for truncation distribution priors
77 *
78 * This function updates the log density for truncation distribution priors.
79 *
80 * @param truncation_mean Array of real values for truncation mean.
81 * @param truncation_sd Array of real values for truncation standard deviation.
82 * @param trunc_mean_mean Array of real values for mean of truncation mean
83 * prior.
84 * @param trunc_mean_sd Array of real values for standard deviation of
85 * truncation mean prior.
86 * @param trunc_sd_mean Array of real values for mean of truncation standard
87 * deviation prior.
88 * @param trunc_sd_sd Array of real values for standard deviation of truncation
89 * standard deviation prior.
90 *
91 * @ingroup observation_model
92 */
93void truncation_lp(array[] real truncation_mean, array[] real truncation_sd,
94 array[] real trunc_mean_mean, array[] real trunc_mean_sd,
95 array[] real trunc_sd_mean, array[] real trunc_sd_sd) {
96 int truncation = num_elements(truncation_mean);
97 if (truncation) {
98 if (trunc_mean_sd[1] > 0) {
99 // uncertain mean
100 truncation_mean ~ normal(trunc_mean_mean, trunc_mean_sd);
101 }
102 if (trunc_sd_sd[1] > 0) {
103 // uncertain sd
104 truncation_sd ~ normal(trunc_sd_mean, trunc_sd_sd);
105 }
106 }
107}
108
109/**
110 * Negative binomial overdispersion for the reporting model
111 *
112 * Converts the reporting overdispersion parameter into the `phi` of the
113 * negative binomial. When no overdispersion is modelled a large value is
114 * returned so the negative binomial behaves like a Poisson.
115 *
116 * @param reporting_overdispersion Real value for reporting overdispersion.
117 * @param model_type Integer indicating the model type (0 for Poisson, >0 for
118 * Negative Binomial).
119 *
120 * @return The negative binomial overdispersion `phi`.
121 *
122 * @ingroup observation_model
123 */
124real reporting_phi(real reporting_overdispersion, int model_type) {
125 return model_type ? inv_square(reporting_overdispersion) : 1e5;
126}
127
128/**
129 * Update log density for reported cases
130 *
131 * This function updates the log density for reported cases based on the
132 * specified model type.
133 *
134 * @param cases Array of integer observed cases.
135 * @param case_times Array of integer time indices for observed cases.
136 * @param reports Vector of expected reports.
137 * @param reporting_overdispersion Real values for reporting overdispersion.
138 * @param model_type Integer indicating the model type (0 for Poisson, >0 for
139 * Negative Binomial).
140 * @param weight Real value for weighting the log density contribution.
141 *
142 * @ingroup observation_model
143 */
144void report_lp(array[] int cases, array[] int case_times, vector reports,
145 real reporting_overdispersion, int model_type, real weight) {
146 int n = num_elements(case_times); // number of observations
147 vector[n] obs_reports = reports[case_times]; // reports at observation time
148 if (model_type) {
149 real phi = reporting_phi(reporting_overdispersion, model_type);
150 if (weight == 1) {
151 cases ~ neg_binomial_2(obs_reports, phi);
152 } else {
153 target += neg_binomial_2_lpmf(
154 cases | obs_reports, phi
155 ) * weight;
156 }
157 } else {
158 if (weight == 1) {
159 cases ~ poisson(obs_reports);
160 } else {
161 target += poisson_lpmf(cases | obs_reports) * weight;
162 }
163 }
164}
165
166/**
167 * Accumulate reports according to a binary flag at each time point
168 *
169 * This function accumulates reports according to a binary flag at each time
170 * point.
171 *
172 * @param reports Vector of expected reports.
173 * @param accumulate Array of integers indicating, for each time point, whether
174 * to accumulate or not.
175 *
176 * @return A vector of accumulated reports.
177 *
178 * @ingroup observation_model
179 */
180vector accumulate_reports(vector reports, array[] int accumulate) {
181 int ot_h = num_elements(reports); // number of reporting time points modelled
182 vector[ot_h] accumulated_reports = reports;
183 for (i in 1:(ot_h - 1)) {
184 if (accumulate[i]) { // first observation gets ignored when accumulating
185 accumulated_reports[i + 1] += accumulated_reports[i];
186 }
187 }
188 return accumulated_reports;
189}
190
191/**
192 * Calculate log likelihood for reported cases
193 *
194 * This function calculates the log likelihood for reported cases based on the
195 * specified model type.
196 *
197 * @param cases Array of integer observed cases.
198 * @param reports Vector of expected reports.
199 * @param reporting_overdispersion Array of real values for reporting overdispersion.
200 * @param model_type Integer indicating the model type (0 for Poisson, >0 for
201 * Negative Binomial).
202 * @param weight Real value for weighting the log likelihood contribution.
203 *
204 * @return A vector of log likelihoods for each time point.
205 *
206 * @ingroup observation_model
207 */
208vector report_log_lik(array[] int cases, vector reports,
209 real reporting_overdispersion, int model_type, real weight) {
210 int t = num_elements(reports);
211 vector[t] log_lik;
212
213 // defer to poisson if phi is large, to avoid overflow
214 if (model_type == 0) {
215 for (i in 1:t) {
216 log_lik[i] = poisson_lpmf(cases[i] | reports[i]) * weight;
217 }
218 } else {
219 real phi = reporting_phi(reporting_overdispersion, model_type);
220 for (i in 1:t) {
221 log_lik[i] = neg_binomial_2_lpmf(
222 cases[i] | reports[i], phi
223 ) * weight;
224 }
225 }
226 return(log_lik);
227}
228
229
230/**
231 * Custom safe version of the negative binomial sampler
232 *
233 * This function generates random samples of the negative binomial distribution
234 * whilst avoiding numerical overflows. In particular:
235 * - if the mu parameter is very small it always returns 0
236 * - if the phi parameter is large it returns a sample from a Poisosn
237 * distribution
238 * - if the gamma rate of the gamma-Poisson mixture used for simulating from the
239 * distribution is very large, it returns 1e8
240 * - in all other cases it returns a sample from the negative binomial
241 * distribution
242 *
243 * @param mu Real value for mean mu.
244 * @param phi Real value for phi.
245 *
246 * @return A random sample
247 *
248 * @ingroup handlers_and_helpers
249 */
250int neg_binomial_2_safe_rng(real mu, real phi) {
251 if (mu < 1e-8) {
252 return(0);
253 } else if (phi > 1e4) {
254 return(poisson_rng(mu > 1e8 ? 1e8 : mu));
255 } else {
256 real gamma_rate = gamma_rng(phi, phi / mu);
257 return(poisson_rng(gamma_rate > 1e8 ? 1e8 : gamma_rate));
258 }
259}
260
261/**
262 * Generate random samples of reported cases
263 *
264 * This function generates random samples of reported cases based on the
265 * specified model type.
266 *
267 * @param reports Vector of expected reports.
268 * @param reporting_overdispersion Real value for reporting overdispersion.
269 * @param model_type Integer indicating the model type (0 for Poisson, >0 for
270 * Negative Binomial).
271 *
272 * @return An array of integer sampled reports.
273 *
274 * @ingroup observation_model
275 */
276array[] int report_rng(vector reports, real reporting_overdispersion, int model_type) {
277 int t = num_elements(reports);
278 array[t] int sampled_reports;
279 real phi = reporting_phi(reporting_overdispersion, model_type);
280
281 for (s in 1:t) {
282 sampled_reports[s] = neg_binomial_2_safe_rng(reports[s], phi);
283 }
284 return(sampled_reports);
285}
int neg_binomial_2_safe_rng(real mu, real phi)
vector truncate_obs(vector reports, vector trunc_rev_cmf, int reconstruct)
vector report_log_lik(array[] int cases, vector reports, real reporting_overdispersion, int model_type, real weight)
void truncation_lp(array[] real truncation_mean, array[] real truncation_sd, array[] real trunc_mean_mean, array[] real trunc_mean_sd, array[] real trunc_sd_mean, array[] real trunc_sd_sd)
real reporting_phi(real reporting_overdispersion, int model_type)
void report_lp(array[] int cases, array[] int case_times, vector reports, real reporting_overdispersion, int model_type, real weight)
vector accumulate_reports(vector reports, array[] int accumulate)
array[] int report_rng(vector reports, real reporting_overdispersion, int model_type)
vector day_of_week_effect(vector reports, array[] int day_of_week, vector effect)
vector scale_obs(vector reports, real fraction_observed)