Skip to content

Commit 80cc863

Browse files
committed
Adds vcov method for drcHetVar objects, and delta confidence interval for bmdHetVar.
1 parent 9d6b139 commit 80cc863

7 files changed

Lines changed: 191 additions & 50 deletions

File tree

NAMESPACE

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ S3method(print,bmdOrdinal)
1616
S3method(print,drcHetVar)
1717
S3method(print,drcMMRE)
1818
S3method(print,drcOrdinal)
19+
S3method(vcov,drcHetVar)
1920
S3method(vcov,drcMMRE)
2021
export(BCa)
2122
export(MACurve)

R/bmdHetVar.R

Lines changed: 53 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,12 @@
4949
#' is the normal distribution function and sigma(BMD) is the SD at the
5050
#' benchmark dose.
5151
#' @param interval character string specifying the type of confidence interval
52-
#' to use: "boot" (default) or "none"
52+
#' to use: "boot" (default), "delta" or "none"
5353
#'
5454
#' "boot" - BMDL is based on percentile bootstrapping.
5555
#'
56+
#' "delta" - BMDL is based on the delta method.
57+
#'
5658
#' "none" - no confidence interval is computed.
5759
#' @param R number of bootstrap samples. Ignored if \code{interval = "none"}
5860
#' @param level numeric value specifying the levle of the confidence interval
@@ -113,7 +115,7 @@
113115
#' def = "hybridExc", R = 50, level = 0.95, progressInfo = TRUE, display = TRUE)
114116
#'
115117
#' @export
116-
bmdHetVar <- function(object, bmr, backgType = c("absolute", "hybridSD", "hybridPercentile"), backg = NA, def = c("hybridExc", "hybridAdd"), interval = c("boot", "none"), R = 1000, level = 0.95, bootType = "nonparametric", progressInfo = TRUE, display = TRUE){
118+
bmdHetVar <- function(object, bmr, backgType = c("absolute", "hybridSD", "hybridPercentile"), backg = NA, def = c("hybridExc", "hybridAdd"), interval = c("boot", "delta", "none"), R = 1000, level = 0.95, bootType = "nonparametric", progressInfo = TRUE, display = TRUE){
117119
### Assertions ###
118120
# object
119121
if(!inherits(object,"drcHetVar")){
@@ -158,14 +160,19 @@ bmdHetVar <- function(object, bmr, backgType = c("absolute", "hybridSD", "hybrid
158160

159161
level <- 1-2*(1-level)
160162

163+
# Model parameters
164+
curveParInd <- 1:length(object$curvePar)
165+
sigmaParInd <- (length(object$curvePar)+1):(length(object$curvePar)+length(object$sigmaPar))
166+
161167
# SLOPE
162168
slope <- drop(ifelse(object$curve(0)-object$curve(Inf)>0,"decreasing","increasing"))
163169
if(is.na(object$curve(0)-object$curve(Inf))){
164170
slope <- drop(ifelse(object$curve(0.00000001)-object$curve(100000000)>0,"decreasing","increasing"))
165171
}
166172

167-
# sigmaFun
168-
sigmaFun0 <- object$sigmaFun # sigmaFun(object, var.formula)
173+
# functions
174+
curveFun0 <- object$funList$curveFun
175+
sigmaFun0 <- object$funList$sigmaFun # sigmaFun(object, var.formula)
169176

170177
# bmrScaled
171178
if(slope == "increasing"){
@@ -174,50 +181,51 @@ bmdHetVar <- function(object, bmr, backgType = c("absolute", "hybridSD", "hybrid
174181
if(is.na(backg)){
175182
stop('backgType = absolute, but backg not supplied')
176183
}
177-
p0 <- 1 - pnorm((backg - object$curve(0)) / sigmaFun0(0))
184+
p0 <- function(par) 1 - pnorm((backg - curveFun0(0, par[curveParInd])) / sigmaFun0(0, sigmaPar = par[sigmaParInd], curvePar = par[curveParInd]))
178185
}
179186
if(identical(backgType, "hybridPercentile")) {
180-
p0 <- ifelse(is.na(backg),1-0.9,1-backg)
187+
p0 <- function(par) ifelse(is.na(backg),1-0.9,1-backg)
181188
}
182189
if (identical(backgType,"hybridSD")) {
183-
p0 <- ifelse(is.na(backg), 1-pnorm(2), 1-pnorm(backg))
190+
p0 <- function(par) ifelse(is.na(backg), 1-pnorm(2), 1-pnorm(backg))
184191
}
185192

186193
# BMRSCALED
187194
bmrScaled <- switch(
188195
def,
189-
hybridExc = function(x){ sigmaFun0(x) *
190-
(qnorm(1 - p0) - qnorm(1 - p0 - (1 - p0)*bmr)) + object$curve(0)},
191-
hybridAdd = function(x){ sigmaFun0(x) *
192-
(qnorm(1 - p0) - qnorm(1 - (p0 + bmr))) + object$curve(0)}
196+
hybridExc = function(x, par){ sigmaFun0(x) *
197+
(qnorm(1 - p0(par)) - qnorm(1 - p0(par) - (1 - p0)*bmr)) + curveFun0(0, par[curveParInd])},
198+
hybridAdd = function(x, par){ sigmaFun0(x) *
199+
(qnorm(1 - p0(par)) - qnorm(1 - (p0(par) + bmr))) + curveFun0(0, par[curveParInd])}
193200
)
194201
} else {
195202
# BACKGROUND
196203
if (identical(backgType,"absolute")) {
197204
if(is.na(backg)){
198205
stop('backgType = absolute, but backg not supplied')
199206
}
200-
p0 <- pnorm((backg - object$curve(0)) / sigmaFun0(0))
207+
p0 <- function(par) pnorm((backg - curveFun0(0, par[curveParInd])) / sigmaFun0(0, sigmaPar = par[sigmaParInd], curvePar = par[curveParInd]))
201208
}
202209
if(identical(backgType, "hybridPercentile")) {
203-
p0 <- ifelse(is.na(backg),0.1,backg)
210+
p0 <- function(par) ifelse(is.na(backg),0.1,backg)
204211
}
205212
if (identical(backgType,"hybridSD")) {
206-
p0 <- ifelse(is.na(backg), pnorm(-2), pnorm(-backg))
213+
p0 <- function(par) ifelse(is.na(backg), pnorm(-2), pnorm(-backg))
207214
}
208215

209216
# BMRSCALED
210217
bmrScaled <- switch(
211218
def,
212-
hybridExc = function(x){ sigmaFun0(x) *
213-
(qnorm(p0) - qnorm(bmr + (1-bmr) * p0)) + object$curve(0)},
214-
hybridAdd = function(x){ sigmaFun0(x) *
215-
(qnorm(p0) - qnorm(bmr + p0)) + object$curve(0)}
219+
hybridExc = function(x, par){ sigmaFun0(x, sigmaPar = par[sigmaParInd], curvePar = par[curveParInd]) *
220+
(qnorm(p0(par)) - qnorm(bmr + (1-bmr) * p0(par))) + curveFun0(0, par[curveParInd])},
221+
hybridAdd = function(x, par){ sigmaFun0(x, sigmaPar = par[sigmaParInd], curvePar = par[curveParInd]) *
222+
(qnorm(p0(par)) - qnorm(bmr + p0(par))) + curveFun0(0, par[curveParInd])}
216223
)
217224
}
218225

219226
# BMD ESTIMATION
220-
f0 <- function(x) object$curve(x) - bmrScaled(x)
227+
par0 <- c(object$curvePar, object$sigmaPar)
228+
f0 <- function(x) curveFun0(x, par0[curveParInd]) - bmrScaled(x, par0)
221229
interval0 <- range(object$dataList$dose, na.rm = TRUE)
222230
uniroot0 <- try(uniroot(f = f0, interval = interval0), silent = TRUE)
223231

@@ -228,17 +236,34 @@ bmdHetVar <- function(object, bmr, backgType = c("absolute", "hybridSD", "hybrid
228236
bmdEst <- uniroot0$root
229237
}
230238

239+
231240
# INTERVAL
232241
interval <- match.arg(interval)
233242
if(identical(interval, "none")){
234243
BMDL <- NA
235244
BMDU <- NA
236-
} else {
237-
# drc_obj <- eval(substitute(drm(formula0, data = object$data, fct = fct0, type = "continuous", control = drmc(maxIt = 1, noMessage = TRUE)),
238-
# list(formula0 = object$formula,
239-
# fct0 = object$fct
240-
# )))
241-
# bootDataList <- bootDataGen(drc_obj, R=R, bootType="nonparametric",aggregated=FALSE)
245+
SDbmd <- NA
246+
} else if(identical(interval, "delta")){
247+
if(!requireNamespace("numDeriv")){
248+
stop('package "numDeriv" must be installed to compute delta confidence intervals with bmdHetVar')
249+
}
250+
251+
getBmdEst <- function(par){
252+
f0 <- function(x) curveFun0(x, par[curveParInd]) - bmrScaled(x, par)
253+
interval0 <- range(object$dataList$dose, na.rm = TRUE)
254+
uniroot0 <- try(uniroot(f = f0, interval = interval0), silent = TRUE)
255+
bmdEst <- as.numeric(uniroot0$root)
256+
}
257+
258+
dBmd <- try(numDeriv::grad(getBmdEst, par0))
259+
260+
Vbmd <- dBmd %*% vcov(object) %*% dBmd
261+
SDbmd <- sqrt(Vbmd)
262+
263+
BMDL <- qnorm(c(1-level)/2, mean = bmdEst, sd = SDbmd)
264+
BMDU <- qnorm(1-(1-level)/2, mean = bmdEst, sd = SDbmd)
265+
266+
} else if(identical(interval, "boot")) {
242267
bootDataList <- bootDataGenHetVar(object, R = R, bootType = bootType)
243268

244269
bmdHetVarBoot <- function(bootData){
@@ -265,9 +290,11 @@ bmdHetVar <- function(object, bmr, backgType = c("absolute", "hybridSD", "hybrid
265290
if(length(boot0) == 0){
266291
BMDL <- NA
267292
BMDU <- NA
293+
SDbmd <- NA
268294
} else {
269295
BMDL <- quantile(boot0,p=c(1-level), na.rm = TRUE) # ABC percentile lims.
270296
BMDU <- quantile(boot0,p=c(level), na.rm = TRUE)
297+
SDbmd <- sd(boot0)
271298
}
272299
}
273300

@@ -282,6 +309,7 @@ bmdHetVar <- function(object, bmr, backgType = c("absolute", "hybridSD", "hybrid
282309

283310
resBMD<-list(Results = resMat,
284311
bmrScaled = bmrScaled,
312+
SE = SDbmd,
285313
interval = bmdInterval,
286314
model = object)
287315
class(resBMD) <- c("bmdHetVar", "bmd")

R/drmHetVar.R

Lines changed: 33 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -108,19 +108,19 @@ drmHetVar <- function(formula, var.formula, data, fct, curveStart = NULL) {
108108
fct$fct(x, t(par))
109109
}
110110

111-
# Define tau function constructor
112-
makeTauFun <- function(var.formula) {
113-
function(x, tauPar, curvePar) {
111+
# Define sigma function constructor
112+
makeSigmaFun <- function(var.formula) {
113+
function(x, sigmaPar, curvePar) {
114114
fitted <- curveFun(x, curvePar)
115115
env <- data.frame(x = x, "fitted" = fitted)
116116
colnames(env)[1] <- dName
117117
design_matrix <- model.matrix(var.formula, data = env)
118-
as.vector(design_matrix %*% tauPar)
118+
as.vector(design_matrix %*% sigmaPar)
119119
}
120120
}
121121

122-
# Build tau function
123-
tauFun <- makeTauFun(var.formula)
122+
# Build sigma function
123+
sigmaFun0 <- makeSigmaFun(var.formula)
124124

125125
# Total number of parameters:
126126
# - Mean model (from fct, usually fixed length)
@@ -132,10 +132,10 @@ drmHetVar <- function(formula, var.formula, data, fct, curveStart = NULL) {
132132
# Negative log-likelihood
133133
negLogLik <- function(par) {
134134
curvePar <- par[1:n_mean_par]
135-
tauPar <- par[-(1:n_mean_par)]
135+
sigmaPar <- par[-(1:n_mean_par)]
136136

137137
mu <- curveFun(dose, curvePar)
138-
sigma <- tauFun(dose, tauPar, curvePar)
138+
sigma <- sigmaFun0(dose, sigmaPar, curvePar)
139139
sigmaSq <- sigma^2
140140

141141
if (any(sigma <= 0)) return(1e10) # enforce positivity
@@ -145,8 +145,8 @@ drmHetVar <- function(formula, var.formula, data, fct, curveStart = NULL) {
145145

146146
# Initial values (somewhat naive)
147147
# start_curve <- coef(drm(formula, data = data, fct = fct)) # rep(mean(y), n_mean_par)
148-
# start_tau <- rep(sd(y), n_var_par)
149-
# start <- c(start_curve, 0.05)# start_tau)
148+
# start_sigma <- rep(sd(y), n_var_par)
149+
# start <- c(start_curve, 0.05)# start_sigma)
150150
start <- unlist(drmHetVarSelfStarter(formula, var.formula, mf, fct, curveStart))
151151

152152
fit <- optim(start, negLogLik, method = "BFGS", hessian = TRUE)
@@ -160,8 +160,9 @@ drmHetVar <- function(formula, var.formula, data, fct, curveStart = NULL) {
160160
names(sigmaPar) <- colnames(model.matrix(var.formula, data = tmp_env))
161161

162162
# Define sigmaFun
163+
funList <- list(curveFun = curveFun, sigmaFun = sigmaFun0)
163164
curve <- function(x) curveFun(x, par = curvePar)
164-
sigmaFun <- function(x) tauFun(x, tauPar = sigmaPar, curvePar = curvePar)
165+
sigmaFun <- function(x) sigmaFun0(x, sigmaPar = sigmaPar, curvePar = curvePar)
165166

166167
# sumList
167168
sumList <- list(numObs = length(resp),
@@ -177,6 +178,7 @@ drmHetVar <- function(formula, var.formula, data, fct, curveStart = NULL) {
177178
hessian = fit$hessian,
178179
curve = curve,
179180
sigmaFun = sigmaFun,
181+
funList = funList,
180182
formula = formula,
181183
var.formula = var.formula,
182184
fct = fct,
@@ -192,3 +194,23 @@ drmHetVar <- function(formula, var.formula, data, fct, curveStart = NULL) {
192194

193195
return(object)
194196
}
197+
198+
#' Extract Variance-Covariance Matrix from drcHetVar Objects
199+
#'
200+
#' @description
201+
#' S3 method to extract the variance-covariance matrix from fitted drcHetVar objects
202+
#' by inverting the Hessian of the negative log-likelihood function.
203+
#'
204+
#' @param object An object of class "drcHetVar"
205+
#' @param ... Additional arguments (currently unused)
206+
#'
207+
#' @return The variance-covariance matrix of the model parameters.
208+
#'
209+
#' @seealso \code{\link{vcov}} for the generic function
210+
#'
211+
#' @method vcov drcHetVar
212+
#' @export
213+
vcov.drcHetVar <- function(object, ...){
214+
solve(object$hessian)
215+
}
216+

man/bmdHetVar.Rd

Lines changed: 4 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

man/vcov.drcHetVar.Rd

Lines changed: 23 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)