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