Diff of /R/custom_priors.R [000000] .. [dfe06d]

Switch to unified view

a b/R/custom_priors.R
1
2
#' Customise priors for outbreaker
3
#'
4
#' Priors can be specified in several ways in outbreaker2 (see details and
5
#' examples). The most flexible way to specify a prior is to provide a prior
6
#' function directly. This function must take an argument 'param', which is a
7
#' list which contains all the states of the parameters and augmented data. See
8
#' the documentation of \link{create_param} for more information.
9
#'
10
#' @details
11
#' There are three ways a user can specify priors:\cr
12
#'
13
#' 1) Default: this is what happens when the 'config' has default values of
14
#' prior parameters.\cr
15
16
#' 2) Customized parameters: in this case, the prior functions are the default
17
#' ones from the package, but will use custom parameters, specified by the user
18
#' through \code{\link{create_config}}.\cr
19
#'
20
#' 3) Customized functions: in this case, prior functions themselves are
21
#' specified by the user, through the '...' argument of 'custom_priors'. The
22
#' requirements is that such functions must have either hard-coded parameters or
23
#' enclosed values. They will take a single argument which is a list containing
24
#' all model parameters with the class 'outbreaker_param'. ALL PRIORS functions
25
#' are expected to return values on a LOG SCALE.\cr
26
#'
27
#' Priors currently used for the model are:
28
#' \itemize{
29
#'
30
#' \item \code{mu} (mutation rate): default function is an exponential
31
#' distribution implemented in \code{outbreaker:::cpp_prior_mu}. New prior
32
#' functions should use \code{x$mu} to refer to the current value of \code{mu},
33
#' assuming their argument is called \code{x}.
34
#'
35
#' \item \code{pi} (reporting probability): default function is a beta
36
#' distribution implemented in \code{outbreaker:::cpp_prior_pi}. New prior
37
#' functions should use \code{x$pi} to refer to the current value of \code{pi},
38
#' assuming their argument is called \code{x}.
39
#'
40
#' \item \code{eps} (contact reporting coverage): default function is a beta
41
#' distribution implemented in \code{outbreaker:::cpp_prior_eps}. New prior
42
#' functions should use \code{x$eps} to refer to the current value of \code{eps},
43
#' assuming their argument is called \code{x}.
44
#'
45
#' \item \code{lambda} (non-infectious contact rate): default function is a
46
#' beta distribution implemented in \code{outbreaker:::cpp_prior_lambda}. New
47
#' prior functions should use \code{x$lambda} to refer to the current value of
48
#' \code{lambda}, assuming their argument is called \code{x}.
49
#'
50
#' }
51
#'
52
#' @author Thibaut Jombart (\email{thibautjombart@@gmail.com}).
53
#'
54
#' @export
55
#'
56
#' @param ... A list or a series of named, comma-separated functions
57
#'     implementing priors. Each function must have a single argument, which
58
#'     corresponds to a 'outbreaker_param' list.
59
#'
60
#' @return A list of custom functions with class \code{custom_priors}. Values
61
#'     set to \code{NULL} will be ignored and default functions will be used
62
#'     instead.
63
#'
64
#' @seealso See \href{http://www.repidemicsconsortium.org/outbreaker2/articles/customisation.html#customising-priors}{customization vignette} for detailed examples on how to customize priors.
65
#' 
66
#' @examples
67
#'
68
#' ## BASIC CONFIGURATION
69
#' custom_priors()
70
#'
71
#'
72
#' ## SPECIFYING PRIOR PARAMETERS
73
#' ## - this will need to be passed to outbreaker
74
#' default_config <- create_config()
75
#' new_config <- create_config(prior_mu = 1e-5,
76
#'                         prior_pi = c(2, 1))
77
#'
78
#' ## - to check the prior manually, default settings:
79
#' param <- list(mu = 0.001, pi = 0.9)
80
#' outbreaker2:::cpp_prior_mu(param, default_config)
81
#' outbreaker2:::cpp_prior_pi(param, default_config)
82
#'
83
#' outbreaker2:::cpp_prior_mu(param, new_config)
84
#' outbreaker2:::cpp_prior_pi(param, new_config)
85
#'
86
#' ## these correspond to:
87
#' dexp(0.001, 0.01, log = TRUE)
88
#' dbeta(0.9, 2, 1, log = TRUE)
89
#'
90
#'
91
#' ## SPECIFYING A PRIOR FUNCTION
92
#'
93
#' ## flat prior for pi between 0.5 and 1
94
#' f <- function(x) {ifelse(x$pi > 0.5, log(2), log(0))}
95
#' priors <- custom_priors(pi = f)
96
#' priors # this should be passed to outbreaker
97
#'
98
#' ## test the prior manually
99
#' priors$pi(list(pi=1))
100
#' priors$pi(list(pi=.6))
101
#' priors$pi(list(pi=.2))
102
#' priors$pi(list(pi=.49))
103
#'
104
105
custom_priors <- function(...) {
106
107
    ## This function returns a list of functions with the class
108
    ## 'outbreaker_priors'. It is used to process custom priors passed by the
109
    ## user. Each item of the list will be a prior function. If not provided,
110
    ## the default value is 'NULL', in which case c++ priors will have the
111
    ## default behaviour. This function tests some basic properties of the prior
112
    ## functions:
113
114
    ## 1) that if not NULL, the prior is a function
115
116
    ## 2) that if a function, it has a single argument called 'param'
117
118
119
120
    ## Get user-specified prior functions
121
122
    priors <- list(...)
123
    if (length(priors) == 1L && is.list(priors[[1]])) {
124
        priors <- priors[[1]]
125
    }
126
127
128
    ## Use user-provided priors where provided, default otherwise. The default
129
    ## for a prior is NULL, in which case the movement functions in C++ will use
130
    ## C++ versions.
131
132
    defaults <- list(mu = NULL, # mutation rate
133
                     pi = NULL, # reporting probability
134
                     eps = NULL, # contact reporting coverage
135
                     lambda = NULL # non-infectious contact rate
136
                     )
137
138
    priors <- modify_defaults(defaults, priors, FALSE)
139
    priors_names <- names(priors)
140
141
142
143
    ## check all priors are functions
144
145
    function_or_null <- function(x) {
146
        is.null(x) || is.function(x)
147
    }
148
149
    is_ok <- vapply(priors, function_or_null, logical(1))
150
151
    if (!all(is_ok)) {
152
        culprits <- priors_names[!is_ok]
153
        msg <- paste0("The following priors are not functions: ",
154
                      paste(culprits, collapse = ", "))
155
        stop(msg)
156
    }
157
158
159
    ## check they all have a single argument
160
161
    with_one_arg <- function(x) {
162
        if(is.function(x)) {
163
            return (length(methods::formalArgs(x)) == 1L)
164
        }
165
166
        return(TRUE)
167
    }
168
169
    one_arg <- vapply(priors, with_one_arg, logical(1))
170
171
    if (!all(one_arg)) {
172
        culprits <- priors_names[!one_arg]
173
        msg <- paste0("The following priors dont' have a single argument: ",
174
                      paste(culprits, collapse = ", "))
175
        stop(msg)
176
    }
177
178
179
    class(priors) <- c("custom_priors", "list")
180
    return(priors)
181
}
182
183
184
185
186
187
188
189
#' @rdname custom_priors
190
#'
191
#' @export
192
#'
193
#' @aliases print.custom_priors
194
#'
195
#' @param x an \code{outbreaker_config} object as returned by \code{create_config}.
196
#'
197
198
print.custom_priors <- function(x, ...) {
199
    cat("\n\n ///// outbreaker custom priors ///\n")
200
    cat("\nclass:", class(x))
201
    cat("\nnumber of items:", length(x), "\n\n")
202
203
    is_custom <- !vapply(x, is.null, FALSE)
204
205
206
    names_default <- names(x)[!is_custom]
207
    if (length(names_default) > 0) {
208
        cat("/// custom priors set to NULL (default used) //\n")
209
        print(x[!is_custom])
210
   }
211
212
213
    names_custom <- names(x)[is_custom]
214
    if (length(names_custom) > 0) {
215
        cat("/// custom priors //\n")
216
        print(x[is_custom])
217
    }
218
219
    return(invisible(NULL))
220
221
}
222