1
+ library(MASS )
1
2
library(drc )
2
3
library(DEoptimR )
3
4
@@ -24,14 +25,14 @@ fpl <- list(
24
25
a / (1 + a )
25
26
)
26
27
},
27
- init = function (x , y , scale = 3 ) {
28
+ init = function (x , y , extend = 3 ) {
28
29
coef <- summary(drm(y ~ x , fct = LL.4()))$ coefficients
29
30
idx <- if (coef [1 ] > = 0 ) c(3 , 1 , 4 , 2 ) else c(2 , 1 , 4 , 3 )
30
31
t <- unname(coef [idx , 1 ])
31
32
se <- unname(coef [idx , 2 ])
32
33
t [2 ] <- abs(t [2 ])
33
- extend <- function (x , dir ) x + dir * scale * abs(x )
34
- init <- list (t = t , se = se , lower = extend (t , - 1 ), upper = extend (t , + 1 ))
34
+ bound <- function (x , dir ) x + dir * extend * abs(x )
35
+ init <- list (t = t , se = se , lower = bound (t , - 1 ), upper = bound (t , + 1 ))
35
36
init $ lower [c(2 , 3 )] <- 1e-100
36
37
init
37
38
}
@@ -74,29 +75,201 @@ bisquare <- function(k) {
74
75
# '
75
76
# ' @return The `rho`-based M-estimate of scale for `r`.
76
77
# '
78
+ # ' @examples
79
+ # ' m_scale(r, bisquare(1.55))
80
+ # '
77
81
# ' @export
78
82
m_scale <- function (r , rho , extend = 5 ) {
79
83
f <- function (s ) mean(rho(r / s )) - 0.5
80
84
m <- median(abs(r ))
81
85
uniroot(f , lower = 0 , upper = extend * m , check.conv = TRUE )$ root
82
86
}
83
87
88
+ # ' Compute robust estimates of dose-response model parameters
89
+ # '
90
+ # ' `drob` computes an M-estimate of location, given data `x` and `y` and model
91
+ # ' `model`, potentially nonlinear and heteroscedastic. It implements a 3-step
92
+ # ' procedure in the spirit of MM-estimation:
93
+ # ' - Step 1: heuristically computes an initial location estimate `t0`.
94
+ # ' - Step 2: computes scale estimates `s` for each dose, based on the residuals
95
+ # ' with respect to `t0`.
96
+ # ' - Step 3: starting from `t0` and scaling by `s` a final location estimate `t`
97
+ # ' is found iteratively.
98
+ # ' Each step can be fine-tuned using the parameters described below.
99
+ # '
100
+ # ' @param x A vector of doses. It is expected that each dose is repeated enough
101
+ # ' times so as to be able to compute a good estimate of scale conditional to
102
+ # ' the dose in step 2.
103
+ # '
104
+ # ' @param y A vector of responses with the same length than `x`.
105
+ # '
106
+ # ' @param model The string `"fpl"` for the predefined 4PL model or, more
107
+ # ' generally, a list describing a model with at least two mandatory elements:
108
+ # ' - `fun`: the model function, which takes doses `x` and parameters `t` as
109
+ # ' its two only arguments.
110
+ # ' - `init`: an initialization function that is able to produce lower and
111
+ # ' upper search bounds for step 1. It takes `x`, `y` as arguments, as well
112
+ # ' as an optional `extend` argument that controls the extension of the
113
+ # ' search region.
114
+ # '
115
+ # ' A third optional element can also be included in the list:
116
+ # ' - `grad`: the gradient of `fun`, which has the same signature. When `grad`
117
+ # ' is present it will be used both for computing a gradient in step 3
118
+ # ' (if `qn_gr` is `TRUE`) and for computing the standard errors of the
119
+ # ' returned estimates.
120
+ # '
121
+ # ' @param step_1 The loss function used for computing `t0`. It may be a function
122
+ # ' that takes a vector of residuals as its only argument or one of the following
123
+ # ' predefined strings:
124
+ # ' - `"lts"`: upper-trimmed mean (up to `lts_q`) of squared residuals.
125
+ # ' - `"lms"`: median of squared residuals.
126
+ # ' - `"ml1"`: mean of absolute residuals. This implements an M-estimate with
127
+ # ' `rho(r)` equal to `|r|`, i.e. L1-regression.
128
+ # ' - `"sl1"`: median of absolute residuals. This implements an S-estimate with
129
+ # ' `rho(r)` equal to `I(|r| > 1)`.
130
+ # ' - `"mbi"`: loss for bisquare M-estimate with cutoff point `mbi_k`, scaled by
131
+ # ' `mad(y)` so as to make it scale-equivariant.
132
+ # ' - `"sbi"`: (the default) loss for bisquare S-estimate with cutoff point
133
+ # ' `sbi_k`. The root finding routine will be extended by `ms_extend` (see the
134
+ # ' documentation for `m_scale`).
135
+ # '
136
+ # ' `loss(y - model$fun(x, t))` will be minimized with respect to `t` using a
137
+ # ' differential evolution routine provided by the `DEoptimR` package. Lower
138
+ # ' and upper bounds for the search come from `model$init` as documented for
139
+ # ' the `model` parameter. Other arguments passed to `JDEoptim` may be overriden
140
+ # ' by passing a `de_args` function. If the optimizer fails to converge to a
141
+ # ' solution, the entire `drob` function execution is aborted with an error.
142
+ # '
143
+ # ' @param step_2 The function used for computing a scale estimate for each dose.
144
+ # ' It may be a function taking a vector of residuals (with respect to `t0`,
145
+ # ' computed in step 1) and returning a scale estimate, or one of the following
146
+ # ' predefined strings:
147
+ # ' - `"sl1"`: median of absolute residuals. This is an M-estimate of scale with
148
+ # ' `rho(r)` equal to `I(|r| > 1)`. It is scaled by `sl1_k`.
149
+ # ' - `"sbi"`: (the default) bisquare M-estimate of scale with cutoff point
150
+ # ' `sbi_k`.
151
+ # '
152
+ # ' The default values for `sl1_k` and `sbi_k` make the estimates equal to 1
153
+ # ' under the standard normal distribution. The function is called once for
154
+ # ' each different dose, passing it a vector with the responses for such dose.
155
+ # '
156
+ # ' @param step_3 The loss function used for computing `t`. It may be the
157
+ # ' string `"mbi"` (the default) for a bisquare loss defining an M-estimate
158
+ # ' with cutoff point `mbi_k` or, more generally, a list containing at the least
159
+ # ' the following mandatory element:
160
+ # ' - `rho`: a rho-function taking a vector of scaled residuals.
161
+ # '
162
+ # ' The list may also contain two optional arguments:
163
+ # ' - `psi`: the first derivative of `rho`.
164
+ # ' - `dpsi`: the second derivative of `rho`.
165
+ # '
166
+ # ' When `psi` and `dpsi` are present (as well as `model$grad`) standard errors
167
+ # ' of the returned estimates will be computed. Moreover, when `psi` is present
168
+ # ' (as well as `model$grad` and also `qn_gr` is `TRUE`) a gradient function
169
+ # ' is composed and passed as the argument for the parameter `gr` of `optim`.
170
+ # '
171
+ # ' This step minimizes `loss((y - model$fun(x, t)) / s)` with respect to `t`.
172
+ # ' It uses the standard `optim` routine with initial parameter `t0` (also
173
+ # ' used for parameter scaling, see the `parscale` control parameter of `optim`)
174
+ # ' and sequentially attempting the following three methods in order:
175
+ # ' 1. Quasi-Newton L-BFGS-B with the same bounds than in step 1.
176
+ # ' 2. Quasi-Newton BFGS.
177
+ # ' 3. Conjugate gradient CG.
178
+ # '
179
+ # ' The result of the first successful optimizer is kept. If all optimizers
180
+ # ' fail to converge to a solution, the entire `drob` function execution is
181
+ # ' aborted with an error.
182
+ # '
183
+ # ' Other arguments passed to `optim` may be overriden by passing a `qn_args`
184
+ # ' function.
185
+ # '
186
+ # ' @param lts_q The proportion of data to delete for the upper-trimmed mean
187
+ # ' loss (used when `step_1` is `"lts"`). By default 0.5 to achieve a high
188
+ # ' breakdown point.
189
+ # '
190
+ # ' @param mbi_k The cutoff point used for bisquare M-estimates (argument
191
+ # ' `"mbi"` to `step_1` or `step_3`). By default 3.44 to achieve about 85%
192
+ # ' asymptotic efficiency under the normal distribution. Other typical values
193
+ # ' and their asymptotic efficiencies are: 3.14 (80%), 3.88 (90%), 4.68 (95%).
194
+ # '
195
+ # ' @param sbi_k The cutoff point used for bisquare M-estimates of scale and,
196
+ # ' consequently, also for bisquare S-estimates (argument `"sbi"` to `step_1`
197
+ # ' or `step_2`). By default 1.548 to achieve a breakdown point of about 0.5.
198
+ # '
199
+ # ' @param sl1_k Scaling factor for the median of absolute residuals (
200
+ # ' argument `"sl1"` to `step_2`). By default 1.48 so that scale equals 1 under
201
+ # ' the standard normal distribution.
202
+ # '
203
+ # ' @param de_args A function that takes a list with the arguments to be pased
204
+ # ' to `JDEoptim` in step 1 and returns and arbitrarily modified list of
205
+ # ' arguments. By default it returns its argument unmodified.
206
+ # '
207
+ # ' @param qn_args A function that takes a list with the arguments to be pased
208
+ # ' to `optim` in step 3 and returns and arbitrarily modified list of
209
+ # ' arguments. By default it returns its argument unmodified.
210
+ # '
211
+ # ' @param qn_gr A flag that indicates if a composed function is passed as the
212
+ # ' argument to the parameter `gr` of the `optim` routine in step 3. Besides
213
+ # ' pass `TRUE` to `qn_gr`, for this to be the case both `model$grad` and
214
+ # ' `loss$psi` must be provided. Otherwise, a finite-difference approximation
215
+ # ' will be used as per usual. By default `FALSE`.
216
+ # '
217
+ # ' @param ms_extend When computing bisquare M-estimates of scale and,
218
+ # ' consequently, also when computing bisquare S-estimates, `ms_extend` will
219
+ # ' be based to `m_scale` in order to extend the root finding interval (for
220
+ # ' further details, refer to the documentation for `m_scale`). By default 5.
221
+ # '
222
+ # ' @param init_extend Passed to `model$init` in order to extend the search
223
+ # ' region defined by the lower and bounds passed to the differential
224
+ # ' evolution routine in step 1 and to the quasi-Newton L-BFGS-B routine in
225
+ # ' step 3. By default 3.
226
+ # '
227
+ # ' @return A list containing the following elements:
228
+ # ' - `t`: a vector with the final location estimate, produced by step 3.
229
+ # ' - `t0`: a vector with the initial location estimate, produced by step 1.
230
+ # ' - `s`: a vector with a scale estimate for each dose, produced by step 2.
231
+ # ' `names(s)` gives the corresponding doses.
232
+ # ' - `init`: a list with the results of the initialization step for the model.
233
+ # ' It usually includes a non-robust location estimate with its standard error
234
+ # ' estimates, besides lower and upper search bounds.
235
+ # ' - `loss`: the value of the loss function for step 3 evaluated at `t`.
236
+ # '
237
+ # ' If `model$grad`, `loss$psi` and `loss$dpsi` are all provided, the resulting
238
+ # ' list will also include a further element:
239
+ # ' - `se`: asymptotic standard error estimates for `t`.
240
+ # '
241
+ # ' @examples
242
+ # '
243
+ # ' set.seed(0)
244
+ # ' t <- c(10, 10, 40, 20)
245
+ # ' x <- rep(seq(1, 100, 10), each = 30)
246
+ # ' n <- length(x)
247
+ # ' cont <- runif(n) > 0.9
248
+ # ' e <- rnorm(n, ifelse(cont, 5, 0), (x / 100) * ifelse(cont, 3, 1))
249
+ # ' y <- fpl$fun(x, t) + e
250
+ # ' est <- drob(x, y)
251
+ # ' plot(x, y)
252
+ # ' lines(x, fpl$fun(x, t), lwd = 2)
253
+ # ' lines(x, fpl$fun(x, est$init$t), col = 2)
254
+ # ' lines(x, fpl$fun(x, est$t), col = 3)
255
+ # ' legend("topleft", legend = c("t", "t_ls", "t_m"), lty = rep(1, 3), col = 1:3)
256
+ # '
84
257
# ' @export
85
258
drob <- function ( # nolint
86
259
x , y ,
87
260
model = " fpl" ,
88
261
step_1 = " sbi" ,
89
262
step_2 = " sbi" ,
90
263
step_3 = " mbi" ,
91
- lts_q = 0.7 ,
264
+ lts_q = 0.5 ,
92
265
mbi_k = 3.44 ,
93
- sbi_k = 1.55 ,
94
- sli_k = 1.48 ,
266
+ sbi_k = 1.548 ,
267
+ sl1_k = 1.48 ,
95
268
de_args = identity ,
96
269
qn_args = identity ,
97
270
qn_gr = FALSE ,
98
271
ms_extend = 5 ,
99
- init_extend = 5
272
+ init_extend = 3
100
273
) {
101
274
select <- function (arg , ... ) if (is.character(arg )) switch (arg , ... ) else arg
102
275
mbi <- bisquare(mbi_k )
@@ -136,7 +309,7 @@ drob <- function( # nolint
136
309
137
310
scale <- select(
138
311
step_2 ,
139
- " sl1" = function (r ) median(abs(r )) * sli_k ,
312
+ " sl1" = function (r ) median(abs(r )) * sl1_k ,
140
313
" sbi" = function (r ) m_scale(r , sbi $ rho , ms_extend ),
141
314
stop(" Step 2: invalid value '" , step_2 , " '" )
142
315
)
@@ -189,6 +362,8 @@ drob <- function( # nolint
189
362
}
190
363
191
364
365
+
366
+
192
367
# https://cran.r-project.org/web/packages/robustbase/vignettes/psi_functions.pdf
193
368
# The constant k for 95% efficiency of the regression estimator is 4.685 and
194
369
# the constant for a breakdown point of 0.5 of the S-estimator is 1.548
@@ -197,3 +372,4 @@ drob <- function( # nolint
197
372
# p.130
198
373
# When computing an initial estimate β0 for the MM-estimate,
199
374
# the bisquare ρ works quite well and we recommend its use for this purpose.
375
+
0 commit comments