# R/BMT.R In BMT: The BMT Distribution

#' @title The BMT Distribution.
#'
#' @description Density, distribution, quantile function, random number
#'   generation for the BMT distribution, with \code{p3} and \code{p4} tails
#'   weights (\eqn{\kappa_l} and \eqn{\kappa_r}) or asymmetry-steepness
#'   parameters (\eqn{\zeta} and \eqn{\xi}) and \code{p1} and \code{p2} domain
#'   (minimum and maximum) or location-scale (mean and standard deviation)
#'   parameters.
#'
#' @rdname BMT
#' @name BMT
#' @aliases dBMT
#' @aliases pBMT
#' @aliases qBMT
#' @aliases rBMT
#'
#' @details The BMT distribution with tails weights and domain parametrization
#'   (\code{type.p.3.4 = "t w"} and \code{type.p.1.2 = "c-d"}) has quantile
#'   function \deqn{(d - c) [3 t_p ( 1 - t_p )^2 \kappa_l - 3 t_p^2 ( 1 - t_p )
#'   \kappa_r + t_p^2 ( 3 - 2 t_p ) ] + c} where \eqn{0 \le p \le 1}, \eqn{t_p =
#'   1/2 - \cos ( [\arccos ( 2 p - 1 ) - 2 \pi] / 3 )}, and \eqn{0 < \kappa_l <
#'   1} and \eqn{0 < \kappa_r < 1} are, respectively, related to left and right
#'   tail weights or curvatures.
#'
#'   The BMT coefficient of asymmetry \eqn{-1 < \zeta < 1} is \deqn{\kappa_r -
#'   \kappa_l}
#'
#'   The BMT coefficient of steepness \eqn{0 < \xi < 1} is \deqn{(\kappa_r +
#'   \kappa_l - |\kappa_r - \kappa_l|) / (2 (1 -  |\kappa_r - \kappa_l|))} for
#'   \eqn{|\kappa_r - \kappa_l| < 1}.
#'
#' @param x,q vector of quantiles.
#' @param p vector of probabilities.
#' @param n number of observations. If \code{length(n) > 1}, the lenght is taken
#'   to be the number required
#' @param p3,p4 tails weights (\eqn{\kappa_l} and \eqn{\kappa_r}) or
#'   asymmetry-steepness (\eqn{\zeta} and \eqn{\xi}) parameters of the BMT
#'   distribution.
#' @param type.p.3.4 type of parametrization asociated to p3 and p4. "t w" means
#'   tails weights parametrization (default) and "a-s" means asymmetry-steepness
#'   parametrization.
#' @param p1,p2 domain (minimum and maximum) or location-scale (mean and
#'   standard deviation) parameters of the BMT ditribution.
#' @param type.p.1.2 type of parametrization asociated to p1 and p2. "c-d" means
#'   domain parametrization (default) and "l-s" means location-scale
#'   parametrization.
#' @param log,log.p logical; if TRUE, probabilities p are given as log(p).
#' @param lower.tail logical; if TRUE (default), probabilities are \eqn{P[X \le
#'   x]}, otherwise, \eqn{P[X > x]}.
#'
#' @return \code{dBMT} gives the density, \code{pBMT} the distribution function,
#'   \code{qBMT} the quantile function, and \code{rBMT} generates random
#'   deviates.
#'
#'   The length of the result is determined by \code{n} for \code{rBMT}, and is
#'   the maximum of the lengths of the numerical arguments for the other
#'   functions.
#'
#'   The numerical arguments other than \code{n} are recycled to the length of
#'   the result. Only the first elements of the logical arguments are used.
#'
#'   If \code{type.p.3.4 == "t w"}, \code{p3 < 0} and \code{p3 > 1} are errors
#'   and return \code{NaN}.
#'
#'   If \code{type.p.3.4 == "a-s"}, \code{p3 < -1} and \code{p3 > 1} are errors
#'   and return \code{NaN}.
#'
#'   \code{p4 < 0} and \code{p4 > 1} are errors and return \code{NaN}.
#'
#'   If \code{type.p.1.2 == "c-d"}, \code{p1 >= p2} is an error and returns
#'   \code{NaN}.
#'
#'   If \code{type.p.1.2 == "l-s"}, \code{p2 <= 0} is an error and returns
#'   \code{NaN}.
#'
#' @references Torres-Jimenez, C. J. and Montenegro-Diaz, A. M. (2017, September),
#'   \emph{An alternative to continuous univariate distributions supported on a
#'   bounded interval: The BMT distribution}. ArXiv e-prints.
#'
#'   Torres-Jimenez, C. J. (2017, September), \emph{Comparison of estimation methods
#'   for the BMT distribution}. ArXiv e-prints.
#'
#'   Torres-Jimenez, C. J. (2018), \emph{The BMT Item Response Theory model: A
#'   new skewed distribution family with bounded domain and an IRT model based
#'   Nacional de Colombia, Sede Bogota.
#'
#'   \code{\link{BMTmoments}} for descriptive measures or moments.
#'   \code{\link{BMTchangepars}} for parameter conversion between different
#'   parametrizations.
#'
#' @author Camilo Jose Torres-Jimenez [aut,cre] \email{cjtorresj@unal.edu.co}
#'   and Alvaro Mauricio Montenegro Diaz [ths]
#'
#' @examples
#' # BMT on [0,1] with left tail weight equal to 0.25 and
#' # right tail weight equal to 0.75
#' z <- seq(0, 1, length.out = 100)
#' F1 <- pBMT(z, 0.25, 0.75, "t w")
#' Q1 <- qBMT(F1, 0.25, 0.75, "t w")
#' max(abs(z - Q1))
#' f1 <- dBMT(z, 0.25, 0.75, "t w")
#' r1 <- rBMT(100, 0.25, 0.75, "t w")
#' layout(matrix(c(1,2,1,3), 2, 2))
#' hist(r1, freq = FALSE, xlim = c(0,1))
#' lines(z, f1)
#' plot(z, F1, type="l")
#' plot(F1, Q1, type="l")
#'
#' # BMT on [0,1] with asymmetry coefficient equal to 0.5 and
#' # steepness coefficient equal to 0.5
#' F2 <- pBMT(z, 0.5, 0.5, "a-s")
#' Q2 <- qBMT(F2, 0.5, 0.5, "a-s")
#' f2 <- dBMT(z, 0.5, 0.5, "a-s")
#' r2 <- rBMT(100, 0.5, 0.5, "a-s")
#' max(abs(f1 - f2))
#' max(abs(F1 - F2))
#' max(abs(Q1 - Q2))
#'
#' # BMT on [-1.783489, 3.312195] with
#' # left tail weight equal to 0.25 and
#' # right tail weight equal to 0.75
#' x <- seq(-1.783489, 3.312195, length.out = 100)
#' F3 <- pBMT(x, 0.25, 0.75, "t w", -1.783489, 3.312195, "c-d")
#' Q3 <- qBMT(F3, 0.25, 0.75, "t w", -1.783489, 3.312195, "c-d")
#' max(abs(x - Q3))
#' f3 <- dBMT(x, 0.25, 0.75, "t w", -1.783489, 3.312195, "c-d")
#' r3 <- rBMT(100, 0.25, 0.75, "t w", -1.783489, 3.312195, "c-d")
#' layout(matrix(c(1,2,1,3), 2, 2))
#' hist(r3, freq = FALSE, xlim = c(-1.783489,3.312195))
#' lines(x, f3)
#' plot(x, F3, type="l")
#' plot(F3, Q3, type="l")
#'
#' # BMT with mean equal to 0, standard deviation equal to 1,
#' # asymmetry coefficient equal to 0.5 and
#' # steepness coefficient equal to 0.5
#' f4 <- dBMT(x, 0.5, 0.5, "a-s", 0, 1, "l-s")
#' F4 <- pBMT(x, 0.5, 0.5, "a-s", 0, 1, "l-s")
#' Q4 <- qBMT(F4, 0.5, 0.5, "a-s", 0, 1, "l-s")
#' r4 <- rBMT(100, 0.5, 0.5, "a-s", 0, 1, "l-s")
#' max(abs(f3 - f4))
#' max(abs(F3 - F4))
#' max(abs(Q3 - Q4))
#'
#' @keywords distribution

#' @rdname BMT
#' @export dBMT
dBMT <- function(x, p3, p4, type.p.3.4 = "t w",
p1 = 0, p2 = 1, type.p.1.2 = "c-d",
log = FALSE) {
# Control type.p.3.4
TYPE.P.3.4 <- c("t w", "a-s") # tail weights or asymmetry-steepness
int.type.p.3.4 <- pmatch(type.p.3.4, TYPE.P.3.4)
if (is.na(int.type.p.3.4))
stop("invalid type of parametrization for parameters 3 and 4")
if (int.type.p.3.4 == -1)
stop("ambiguous type of parametrization for parameters 3 and 4")
# Control type.p.1.2
TYPE.P.1.2 <- c("c-d", "l-s") # domain or location-scale
int.type.p.1.2 <- pmatch(type.p.1.2, TYPE.P.1.2)
if (is.na(int.type.p.1.2))
stop("invalid type of parametrization for parameters 1 and 2")
if (int.type.p.1.2 == -1)
stop("ambiguous type of parametrization for parameters 1 and 2")
# The length of the result is determined by the maximum of the lengths of the
# numerical arguments. The numerical arguments are recycled to the length of
# the result.
len <- max(length(x),length(p1),length(p2),length(p3),length(p4))
x <- rep(x, len = len)
p1 <- rep(p1, len = len)
p2 <- rep(p2, len = len)
p3 <- rep(p3, len = len)
p4 <- rep(p4, len = len)
# Transform x to 0,1 given domain or location-scale parameters
if (int.type.p.1.2 == 1) {
# domain parametrization
# Control domain parameters
min <- replace(p1, p1 >= p2, NaN)
max <- replace(p2, p1 >= p2, NaN)
# Transform x
range <- max - min
x <- (x - min) / range
}
else{
# location-scale parametrization
# Control location-scale parameters
mu <- p1
sigma <- replace(p2, p2 <= 0, NaN)
# Transform x
range <- sigma / BMTsd(p3, p4, type.p.3.4)
x <- (x - mu) / range + BMTmean(p3, p4, type.p.3.4)
}
# Obtain coefficients of polynomials x.t and yf.t given tail weights or
# asymmetry-steepness parameters
if (int.type.p.3.4 == 1) {
# tail weights parametrization
# Control tail weights parameters
kappa_l <- replace(p3, p3 < 0 | p3 > 1, NaN)
kappa_r <- replace(p4, p4 < 0 | p4 > 1, NaN)
# Coefficients a_3*t^3+a_2*t^2+a_1*t+a_0
a_3 <- 3 * kappa_l + 3 * kappa_r - 2
a_2 <- (-6 * kappa_l - 3 * kappa_r + 3)
a_1 <- (3 * kappa_l)
}
else{
# asymmetry-steepness parametrization
# Control asymmetry-steepness parameters
zeta <- replace(p3, p3 < -1 | p3 > 1, NaN)
xi <- replace(p4, p4 < 0 | p4 > 1, NaN)
# Coefficients a_3*t^3+a_2*t^2+a_1*t+a_0
abs.zeta <- abs(zeta)
aux1 <- 0.5 - xi
a_3 <- 6 * (xi + abs.zeta * aux1) - 2
a_2 <- -9 * (xi + abs.zeta * aux1) + 1.5 * zeta + 3
a_1 <- 3 * (xi + abs.zeta * aux1) - 1.5 * zeta
}
# NaNs
y <- x + a_3 + a_2 + a_1
if (any(is.nan(y))) {
warning("NaNs founded or produced")
}
ind <- !is.na(y)
# Transformed x outside 0,1
y[ind] <- 0
# Transformed x inside 0,1
ind[ind] <- x[ind] > 0 & x[ind] < 1
if (any(ind)) {
# inv.x.t
t <- .inv.x.t(x[ind], a_3[ind], a_2[ind], a_1[ind])
# yf.t
y[ind] <- .yf.t(t, a_3[ind], a_2[ind], a_1[ind]) / range[ind]
}
# density values y are given as log(y)
if (log)
y <- log(y)
return(y)
}

#' @rdname BMT
#' @export pBMT
pBMT <- function(q, p3, p4, type.p.3.4 = "t w",
p1 = 0, p2 = 1, type.p.1.2 = "c-d",
lower.tail = TRUE, log.p = FALSE){
# Control type.p.3.4
TYPE.P.3.4 <- c("t w", "a-s") # tail weights or asymmetry-steepness
int.type.p.3.4 <- pmatch(type.p.3.4, TYPE.P.3.4)
if (is.na(int.type.p.3.4))
stop("invalid type of parametrization for parameters 3 and 4")
if (int.type.p.3.4 == -1)
stop("ambiguous type of parametrization for parameters 3 and 4")
# Control type.p.1.2
TYPE.P.1.2 <- c("c-d", "l-s") # domain or location-scale
int.type.p.1.2 <- pmatch(type.p.1.2, TYPE.P.1.2)
if (is.na(int.type.p.1.2))
stop("invalid type of parametrization for parameters 1 and 2")
if (int.type.p.1.2 == -1)
stop("ambiguous type of parametrization for parameters 1 and 2")
# The length of the result is determined by the maximum of the lengths of the
# numerical arguments. The numerical arguments are recycled to the length of
# the result.
len <- max(length(q),length(p1),length(p2),length(p3),length(p4))
q <- rep(q, len=len)
p1 <- rep(p1, len=len)
p2 <- rep(p2, len=len)
p3 <- rep(p3, len=len)
p4 <- rep(p4, len=len)
# Transform q to 0,1 given domain or location-scale parameters
if(int.type.p.1.2 == 1){ # domain parametrization
# Control domain parameters
min <- replace(p1, p1 >= p2, NaN)
max <- replace(p2, p1 >= p2, NaN)
# Transform q
range <- max - min
q <- (q - min)/range
}
else{ # location-scale parametrization
# Control location-scale parameters
mu <- p1
sigma <- replace(p2, p2 <= 0, NaN)
# Transform q
range <- sigma/BMTsd(p3, p4, type.p.3.4)
q <- (q - mu)/range + BMTmean(p3, p4, type.p.3.4)
}
# Obtain coefficients of polynomials x.t and yf.t given tail weights or
# asymmetry-steepness parameters
if(int.type.p.3.4 == 1){ # tail weights parametrization
# Control tail weights parameters
kappa_l <- replace(p3, p3 < 0 | p3 > 1, NaN)
kappa_r <- replace(p4, p4 < 0 | p4 > 1, NaN)
# Coefficients a_3*t^3+a_2*t^2+a_1*t+a_0
a_3 <- 3*kappa_l+3*kappa_r-2
a_2 <- (-6*kappa_l-3*kappa_r+3)
a_1 <- (3*kappa_l)
}
else{ # asymmetry-steepness parametrization
# Control asymmetry-steepness parameters
zeta <- replace(p3, p3 < -1 | p3 > 1, NaN)
xi <- replace(p4, p4 < 0 | p4 > 1, NaN)
# Coefficients a_3*t^3+a_2*t^2+a_1*t+a_0
abs.zeta <- abs(zeta)
aux1 <- 0.5-xi
a_3 <- 6*(xi+abs.zeta*aux1)-2
a_2 <- -9*(xi+abs.zeta*aux1)+1.5*zeta+3
a_1 <- 3*(xi+abs.zeta*aux1)-1.5*zeta
}
# NaNs
p <- q+a_3+a_2+a_1
if(any(is.nan(p)))
warning("NaNs founded or produced")
ind <- !is.na(p)
# Transformed q outside 0,1
p[ind] <- 0
ind2 <- ind
ind2[ind] <- q[ind] >= 1
p[ind2] <- 1
# Transformed q inside 0,1
ind[ind] <- q[ind] > 0 & q[ind] < 1
if(any(ind)){
# inv.x.t
t <- .inv.x.t(q[ind], a_3[ind], a_2[ind], a_1[ind])
# yF.t
p[ind] <- .yF.t(t)
}
# probabilities are \eqn{P[X > x]}
if(!lower.tail)
p <- 1 - p
# probabilities p are given as log(p)
if(log.p)
p <- log(p)
return(p)
}

#' @rdname BMT
#' @export qBMT
qBMT <- function(p, p3, p4, type.p.3.4 = "t w",
p1 = 0, p2 = 1, type.p.1.2 = "c-d",
lower.tail = TRUE, log.p = FALSE){
# probabilities p are given as log(p)
if(log.p)
p <- exp(p)
# probabilities are \eqn{P[X > x]}
if(!lower.tail)
p <- 1 - p
# Control type.p.3.4
TYPE.P.3.4 <- c("t w", "a-s") # tail weights or asymmetry-steepness
int.type.p.3.4 <- pmatch(type.p.3.4, TYPE.P.3.4)
if (is.na(int.type.p.3.4))
stop("invalid type of parametrization for parameters 3 and 4")
if (int.type.p.3.4 == -1)
stop("ambiguous type of parametrization for parameters 3 and 4")
# Control type.p.1.2
TYPE.P.1.2 <- c("c-d", "l-s") # domain or location-scale
int.type.p.1.2 <- pmatch(type.p.1.2, TYPE.P.1.2)
if (is.na(int.type.p.1.2))
stop("invalid type of parametrization for parameters 1 and 2")
if (int.type.p.1.2 == -1)
stop("ambiguous type of parametrization for parameters 1 and 2")
# The length of the result is determined by the maximum of the lengths of the
# numerical arguments. The numerical arguments are recycled to the length of
# the result.
len <- max(length(p),length(p1),length(p2),length(p3),length(p4))
p <- rep(p, len=len)
p1 <- rep(p1, len=len)
p2 <- rep(p2, len=len)
p3 <- rep(p3, len=len)
p4 <- rep(p4, len=len)
# Obtain coefficients of polynomials x.t and yf.t given tail weights or
# asymmetry-steepness parameters
if(int.type.p.3.4 == 1){ # tail weights parametrization
# Control tail weights parameters
kappa_l <- replace(p3, p3 < 0 | p3 > 1, NaN)
kappa_r <- replace(p4, p4 < 0 | p4 > 1, NaN)
# Coefficients a_3*t^3+a_2*t^2+a_1*t+a_0
a_3 <- 3*kappa_l+3*kappa_r-2
a_2 <- (-6*kappa_l-3*kappa_r+3)
a_1 <- (3*kappa_l)
}
else{ # asymmetry-steepness parametrization
# Control asymmetry-steepness parameters
zeta <- replace(p3, p3 < -1 | p3 > 1, NaN)
xi <- replace(p4, p4 < 0 | p4 > 1, NaN)
# Coefficients a_3*t^3+a_2*t^2+a_1*t+a_0
abs.zeta <- abs(zeta)
aux1 <- 0.5-xi
a_3 <- 6*(xi+abs.zeta*aux1)-2
a_2 <- -9*(xi+abs.zeta*aux1)+1.5*zeta+3
a_1 <- 3*(xi+abs.zeta*aux1)-1.5*zeta
}
# NaNs
q <- p+a_3+a_2+a_1
if(any(is.nan(q)))
warning("NaNs founded or produced")
ind <- !is.na(q)
# q outside (0,1)
q <- rep(NaN,len)
ind2 <- ind
ind2[ind] <- p[ind] == 0
q[ind2] <- 0
ind2 <- ind
ind2[ind] <- p[ind] == 1
q[ind2] <- 1
# q inside (0,1)
ind[ind] <- p[ind] > 0 & p[ind] < 1
if(any(ind)){
# inv.yF.t
t <- .inv.yF.t(p[ind])
# x.t
q[ind] <- .x.t(t, a_3[ind], a_2[ind], a_1[ind])
}
# Transform q to [c,d] given domain or location-scale parameters
if(int.type.p.1.2 == 1){ # domain parametrization
# Control domain parameters
min <- replace(p1, p1 >= p2, NaN)
max <- replace(p2, p1 >= p2, NaN)
# Transform q
range <- max - min
q <- q * range + min
}
else{ # location-scale parametrization
# Control location-scale parameters
mu <- p1
sigma <- replace(p2, p2 <= 0, NaN)
# Transform q
range <- sigma/BMTsd(p3, p4, type.p.3.4)
q <- (q - BMTmean(p3, p4, type.p.3.4)) * range + mu
}
return(q)
}

#' @rdname BMT
#' @export rBMT
rBMT <- function(n, p3, p4, type.p.3.4 = "t w",
p1 = 0, p2 = 1, type.p.1.2 = "c-d"){
#
len <- length(n)
if(len > 1)
n <- len
else
n <- trunc(n)
if(n < 1)
stop("invalid arguments")
# Method of inversion
p <- runif(n)
x <- qBMT(p, p3, p4, type.p.3.4, p1, p2, type.p.1.2)
return(x)
}

# Global constants
.epsilon <- 1e-10
.zero <- 0-.epsilon
.one <- 1+.epsilon

# x.t
.x.t <- function(t, a_3, a_2, a_1){
x <- ((a_3*t + a_2)*t + a_1)*t
return(x)
}

# Inverse function for x.t
.inv.x.t <- function(x, a_3, a_2, a_1){
# Press W.H., Teukolsky S.A., Vetterling W.T. & Flannery B.P. 2007.
# Numerical recipes: The art of scientific computing
# Section 5.6: Quadratic and Cubic Equations. Page 228.
len <- length(x)
a <- a_2/a_3
b <- a_1/a_3
c <- -x/a_3
Q <- (a*a - 3*b)/9
R <- ((2*a*a - 9*b)*a + 27*c)/54
r <- rep(0,len)
# All real roots
ind.r <- Q*Q*Q-R*R > 0
a.v <- a[ind.r]
Q.v <- Q[ind.r]
R.v <- R[ind.r]
theta <- acos(R.v/sqrt(Q.v*Q.v*Q.v))
aux1 <- -2*sqrt(Q.v)
aux2 <- a.v/3
r.v <- aux1*cos(theta/3)-aux2
ind.no01 <- r.v < .zero | r.v > .one
r.v[ind.no01] <- aux1[ind.no01]*cos((theta[ind.no01]+2*pi)/3)-aux2[ind.no01]
ind.no01 <- r.v < .zero | r.v > .one
r.v[ind.no01] <- aux1[ind.no01]*cos((theta[ind.no01]-2*pi)/3)-aux2[ind.no01]
r[ind.r] <- r.v
# Two complex roots
a.v <- a[!ind.r]
Q.v <- Q[!ind.r]
R.v <- R[!ind.r]
aux2 <- a.v/3
A <- -sign(R.v)*(abs(R.v)+sqrt(R.v*R.v-Q.v*Q.v*Q.v))^(1/3)
B <- rep(0,length(A))
B[A!=0] <- Q.v[A!=0]/A[A!=0]
r.v <- (A+B)-aux2
ind.no01 <- r.v < .zero | r.v > .one
r.v[ind.no01] <- -0.5*(r.v[ind.no01])-1.5*aux2[ind.no01]
r[!ind.r] <- r.v
# Considering an epsilon, all roots in [0,1]
r[r >= .zero & r < 0] <- 0
r[r > 1 & r <= .one] <- 1
return(r)
}

# yF.t
.yF.t <- function(t){
yF <- (-2*t + 3)*t*t
return(yF)
}

# inv.yF.t
.inv.yF.t <- function(yF){
t <- 0.5-cos((acos(2*yF-1)-2*pi)/3)
return(t)
}

# yf.t
.yf.t <- function(t, a_3, a_2, a_1){
yf <- ((-6*t + 6)*t) / ((3*a_3*t + 2*a_2)*t + a_1)
return(yf)
}


## Try the BMT package in your browser

Any scripts or data that you put into this service are public.

BMT documentation built on May 2, 2019, 5:41 a.m.