R/ExWALD.R

Defines functions fitexw negllexw den_exw rew pwald exwstpt ExWALD

Documented in den_exw ExWALD exwstpt fitexw negllexw pwald rew

#' The Ex-Wald family
#'
#' @author Freddy Hernandez, \email{fhernanb@unal.edu.co}
#'
#' @description
#' The function \code{ExWALD()} defines the Ex-wALD distribution, three-parameter
#' continuous distribution for a \code{gamlss.family} object to be used in 
#' GAMLSS fitting using the function \code{gamlss()}.
#'
#' @param mu.link defines the mu.link, with "log" link as the default for the mu parameter.
#' @param sigma.link defines the sigma.link, with "log" link as the default for the sigma parameter.
#' @param nu.link defines the nu.link, with "log" link as the default for the nu parameter.
#'
#' @references
#' Schwarz, W. (2001). The ex-Wald distribution as a descriptive model 
#' of response times. Behavior Research Methods, 
#' Instruments, & Computers, 33, 457-469.
#' 
#' Heathcote, A. (2004). Fitting Wald and ex-Wald distributions to 
#' response time data: An example using functions for the S-PLUS package. 
#' Behavior Research Methods, Instruments, & Computers, 36, 678-694.
#'
#' @seealso \link{dExWALD}.
#'
#' @details
#' The Ex-Wald distribution with parameters \eqn{\mu}, \eqn{\sigma} 
#' and \eqn{\nu} has density given by
#'
#' \eqn{f(x |\mu, \sigma, \nu) = \frac{1}{\nu} \exp(\frac{-x}{\nu} + \sigma(\mu-k)) F_W(x|k, \sigma) \, \text{for} \, k \geq 0}
#'
#' \eqn{f(x |\mu, \sigma, \nu) = \frac{1}{\nu} \exp\left( \frac{-(\sigma-\mu)^2}{2x} \right) Re \left( w(k^\prime \sqrt{x/2} + \frac{\sigma i}{\sqrt{2x}}) \right)  \, \text{for} \, k < 0}
#'
#' where \eqn{k=\sqrt{\mu^2-\frac{2}{\nu}}}, 
#' \eqn{k^\prime=\sqrt{\frac{2}{\nu}-\mu^2}} and
#' \eqn{F_W} corresponds to the cumulative function of 
#' the Wald distribution. 
#' 
#' More details about those expressions
#' can be found on page 680 from Heathcote (2004).
#'
#' @returns Returns a gamlss.family object which can be used to fit a 
#' Ex-WALD distribution in the \code{gamlss()} function.
#'
#' @example examples/examples_ExWALD.R
#'
#' @importFrom gamlss.dist checklink
#' @importFrom gamlss rqres.plot
#' @export
ExWALD <- function(mu.link="log", 
                   sigma.link="log", 
                   nu.link="log") {
  
  mstats <- checklink("mu.link", "Ex-Wald",
                      substitute(mu.link), c("log"))
  dstats <- checklink("sigma.link", "Ex-Wald",
                      substitute(sigma.link), c("log"))
  vstats <- checklink("nu.link", "Ex-Wald",
                      substitute(nu.link), c("log"))
  
  structure(list(family     = c("ExWALD", "Ex-Wald"),
                 parameters = list(mu=TRUE, sigma=TRUE, nu=TRUE),
                 nopar      = 3,
                 type       = "Continuous",
                 
                 mu.link    = as.character(substitute(mu.link)),
                 sigma.link = as.character(substitute(sigma.link)),
                 nu.link    = as.character(substitute(nu.link)),
                 
                 mu.linkfun    = mstats$linkfun,
                 sigma.linkfun = dstats$linkfun,
                 nu.linkfun    = vstats$linkfun,
                 
                 mu.linkinv    = mstats$linkinv,
                 sigma.linkinv = dstats$linkinv,
                 nu.linkinv    = vstats$linkinv,
                 
                 mu.dr    = mstats$mu.eta,
                 sigma.dr = dstats$mu.eta,
                 nu.dr    = vstats$mu.eta,
                 
                 # First derivates
                 
                 dldm = function(y, mu, sigma, nu) {
                   dm <- gamlss::numeric.deriv(dExWALD(y, mu, sigma, nu, log=TRUE),
                                               theta="mu",
                                               delta=0.001)
                   dldm <- as.vector(attr(dm, "gradient"))
                   dldm
                 },
                 
                 dldd = function(y, mu, sigma, nu) {
                   dd <- gamlss::numeric.deriv(dExWALD(y, mu, sigma, nu, log=TRUE),
                                               theta="sigma",
                                               delta=0.001)
                   dldd <- as.vector(attr(dd, "gradient"))
                   dldd
                 },
                 
                 dldv = function(y, mu, sigma, nu) {
                   dv <- gamlss::numeric.deriv(dExWALD(y, mu, sigma, nu, log=TRUE),
                                               theta="nu",
                                               delta=0.001)
                   dldv <- as.vector(attr(dv, "gradient"))
                   dldv
                 },

                 # Second derivates
                 
                 d2ldm2 = function(y, mu, sigma, nu) {
                   dm <- gamlss::numeric.deriv(dExWALD(y, mu, sigma, nu, log=TRUE),
                                                 theta="mu",
                                                 delta=0.001)
                   dldm   <- as.vector(attr(dm, "gradient"))
                   d2ldm2 <- - dldm*dldm
                   d2ldm2 <- ifelse(d2ldm2 < -1e-15, d2ldm2, -1e-15)
                   d2ldm2
                 },
                 
                 d2ldmdd = function(y, mu, sigma, nu) {
                   dm <- gamlss::numeric.deriv(dExWALD(y, mu, sigma, nu, log=TRUE),
                                                 theta="mu",
                                                 delta=0.001)
                   dldm <- as.vector(attr(dm, "gradient"))
                   dd   <- gamlss::numeric.deriv(dExWALD(y, mu, sigma, nu, log=TRUE),
                                                 theta="sigma",
                                                 delta=0.001)
                   dldd    <- as.vector(attr(dd, "gradient"))
                   d2ldmdd <- - dldm*dldd
                   d2ldmdd <- ifelse(d2ldmdd < -1e-15, d2ldmdd, -1e-15)
                   d2ldmdd
                 },
                 
                 d2ldmdv = function(y, mu, sigma, nu) {
                   dm <- gamlss::numeric.deriv(dExWALD(y, mu, sigma, nu, log=TRUE),
                                                 theta="mu",
                                                 delta=0.001)
                   dldm <- as.vector(attr(dm, "gradient"))
                   dv   <- gamlss::numeric.deriv(dExWALD(y, mu, sigma, nu, log=TRUE),
                                                 theta="nu",
                                                 delta=0.001)
                   dldv    <- as.vector(attr(dv, "gradient"))
                   d2ldmdv <- - dldm*dldv
                   d2ldmdv <- ifelse(d2ldmdv < -1e-15, d2ldmdv, -1e-15)
                   d2ldmdv
                 },
                 
                 d2ldd2  = function(y, mu, sigma, nu) {
                   dd  <- gamlss::numeric.deriv(dExWALD(y, mu, sigma, nu, log=TRUE),
                                                 theta="sigma",
                                                 delta=0.001)
                   dldd   <- as.vector(attr(dd, "gradient"))
                   d2ldd2 <- - dldd*dldd
                   d2ldd2 <- ifelse(d2ldd2 < -1e-15, d2ldd2, -1e-15)
                   d2ldd2
                 },
                 
                 d2ldddv = function(y, mu, sigma, nu) {
                   dd <- gamlss::numeric.deriv(dExWALD(y, mu, sigma, nu, log=TRUE),
                                                 theta="sigma",
                                                 delta=0.001)
                   dldd <- as.vector(attr(dd, "gradient"))
                   dv   <- gamlss::numeric.deriv(dExWALD(y, mu, sigma, nu, log=TRUE),
                                                 theta="nu",
                                                 delta=0.001)
                   dldv    <- as.vector(attr(dv, "gradient"))
                   d2ldddv <- - dldd*dldv
                   d2ldddv <- ifelse(d2ldddv < -1e-15, d2ldddv, -1e-15)
                   d2ldddv
                 },
                 
                 d2ldv2  = function(y, mu, sigma, nu) {
                   dv   <- gamlss::numeric.deriv(dExWALD(y, mu, sigma, nu, log=TRUE),
                                                 theta="nu",
                                                 delta=0.001)
                   dldv   <- as.vector(attr(dv, "gradient"))
                   d2ldv2 <- - dldv*dldv
                   d2ldv2 <- ifelse(d2ldv2 < -1e-15, d2ldv2, -1e-15)
                   d2ldv2
                 },
                 
                 G.dev.incr = function(y, mu, sigma, nu, ...) -2*dExWALD(y, mu, sigma, nu, log=TRUE),
                 rqres      = expression(rqres(pfun="pExWALD", type="Continuous", y=y, mu=mu, sigma=sigma, nu=nu)),
                 
                 mu.initial    = expression(mu    <- rep(fitexw(y)[1], length(y))),
                 sigma.initial = expression(sigma <- rep(fitexw(y)[2], length(y))),
                 nu.initial    = expression(nu    <- rep(fitexw(y)[3], length(y))),
                 
                 mu.valid    = function(mu)    all(mu > 0),
                 sigma.valid = function(sigma) all(sigma > 0),
                 nu.valid    = function(nu)    all(nu > 0),
                 
                 y.valid = function(y) all(y > 0),
                 
                 mean     = function(mu, sigma, nu) {nu + sigma/mu}, 
                 variance = function(mu, sigma, nu) {nu^2 + sigma/mu^3}
  ),
  class = c("gamlss.family", "family"))
}
#' Auxiliar function for the Ex-Wald distribution
#' @description This function generates start values.
#' @param x a value for x.
#' @param p a value for p.
#' @return returns a vector with starting values.
#' @keywords internal
#' @importFrom stats sd var
#' @export
exwstpt <- function(x, p=0.5) {
  t <- p*sd(x)
  m <- sqrt((mean(x)-t)/(var(x)-t^2))
  a <- m*(mean(x)-t)
  res <- c(m, a, t)
  return(res)
}
#' Auxiliar pwald function for the Ex-Wald distribution
#' @description This function emulates the pWALD function.
#' @param w a value for w.
#' @param m a value for m.
#' @param a a value for a.
#' @param s a value for s.
#' @return returns a vector with cumulative probabilities.
#' @keywords internal
#' @export
pwald <- function(w, m, a, s=0) {
  w <- w - s; 
  sqrtw <- sqrt(w); 
  k1 <- (m*w-a)/sqrtw; 
  k2 <- (m*w+a)/sqrtw
  p1 <- exp(2*a*m); 
  p2 <- pnorm(-k2); 
  bad <- (p1==Inf) | (p2==0); 
  p <- p1*p2
  p[bad] <- (exp(-(k1[bad]^2)/2 - 0.94/(k2[bad]^2))/(k2[bad]*((2*pi)^.5)))
  p + pnorm(k1)
}
#' Auxiliar function to generate random values for Ex-Wald distribution
#' @description This function generates another form to generate values.
#' @param x a value for x.
#' @param y a value for y.
#' @return returns a vector with values.
#' @keywords internal
#' @export
rew <- function(x, y) {
  uv <- uandv(y, x)
  exp(y^2-x^2)*(cos(2*x*y)*(1-uv[,1])+sin(2*x*y)*uv[,2])
}
#' Auxiliar den_exw function for the Ex-Wald distribution
#' @description This function emulates the dExWALD function.
#' @param r value for r.
#' @param m value for m.
#' @param a value for a.
#' @param t value for t.
#' @return returns a vector with probabilities.
#' @keywords internal
#' @export
den_exw <- function(r, m, a, t) {
  k <- (m^2 - (2/t))
  if (k < 0) {
    res <- exp(m*a - (a^2)/(2*r) - r*(m^2)/2)*
      rew(sqrt(-r*k/2), a/sqrt(2*r))/t
  } else {
    k <- sqrt(k)
    res <- pwald(r,k,a)*exp(a*(m-k) - (r/t))/t
  }
  return(res)
}
#' Auxiliar function to obtain minus loglik for Ex-Wald 
#' @description This function calculates minus loglik.
#' @param p a vector with the parameters.
#' @param x a vector with the random sample.
#' @return returns the minus loglik.
#' @keywords internal
#' @export
negllexw <- function(p, x) {
  -sum(log(den_exw(x, p[1], p[2], p[3])))
}
#' Auxiliar function for the Ex-Wald distribution
#' @description This function generates starting values.
#' @param rt a vector with the random sample.
#' @param p a value for p.
#' @param start an optional start vector.
#' @param scaleit logical value to scale.
#' @return returns a vector with cumulative probabilities.
#' @keywords internal
#' @importFrom stats nlminb
#' @export
fitexw <- function(rt, p=0.5,
                   start=exwstpt(rt, p),
                   scaleit=TRUE){
  if (scaleit) 
    fit <- nlminb(start=start,
                  lower=c(1e-8, 1e-8, 1),
                  objective=negllexw,
                  x=rt,
                  scale=(1/start),
                  control=list(eval.max=400, iter.max=300))
  else 
    fit <- nlminb(start=start,
                  lower=c(1e-8, 1e-8, 1),
                  objective=negllexw,
                  x=rt,
                  control=list(eval.max=400, iter.max=300, 
                               scale.tol=1e-8))

  return(fit$par)
}
ousuga/RelDists documentation built on July 4, 2025, 10:55 a.m.