"FRcop" <- # Frank copula, Joe (2015, p. 165, sec. 4.5.1)
function(u, v, para=NULL, rhotau=NULL, userhotau_chk=TRUE,
cortype=c("kendall", "spearman", "tau", "rho"), ...) {
cortype <- tolower(cortype)
cortype <- match.arg(cortype)
D1 <- function(x) { # Joe (2015, p. 166). This integral needed for Kendall's tau.
integrate(function(t) 1*x^(-1) * (t^1)*(expm1(t))^(-1), lower=0, upper=x,
subdivisions=200, stop.on.error=FALSE) # expm1(t) = exp(t) - 1
# set stop.on.error=TRUE for deeper testing when exploring limits of FRcop() implementation
}
D2 <- function(x) { # Joe (2015, p. 166). This integral needed for Kendall's tau.
integrate(function(t) 2*x^(-2) * (t^2)*(expm1(t))^(-1), lower=0, upper=x,
subdivisions=200, stop.on.error=FALSE) # expm1(t) = exp(t) - 1
# set stop.on.error=TRUE for deeper testing when exploring limits of FRcop() implementation
}
big.lwr <- -3500
big.upr <- +3500
para.small <- 1.082793e-06 # from lines of code below and abs() will be used.
# uniroot(ofunc_tau, interval=c(big.lwr, big.upr), target_tau=0)$root # [1] +1.082793e-06
# uniroot(ofunc_rho, interval=c(big.lwr, big.upr), target_rho=0)$root # [1] -2.097237e-10
# it is in the copula computations themselves, so we need another heuristic limit.
large.para <- 354 # based on copBasic::simCOP behavior and handling of reflection with positive parameters
large.rho <- 0.9999479
large.tau <- 0.9887531
if(is.null(para)) {
if(cortype == "spearman" | cortype == "rho") {
if(is.null(rhotau)) {
rho <- cor(u,v, method="spearman")
} else {
rho <- rhotau
}
ofunc_rho <- function(trial_para, target_rho=NA) {
rho <- 1 + (12/trial_para) * (D2(trial_para)$value - D1(trial_para)$value) # Joe (2015, p. 166)
return(target_rho - rho)
}
para <- uniroot(ofunc_rho, interval=c(big.lwr, big.upr), target_rho=rho)$root
names(para) <- "theta"
names(rho ) <- "Spearman Rho"
return(list(para=para, rho=rho))
} else if(cortype == "kendall" | cortype == "tau") {
if(is.null(rhotau)) {
tau <- cor(u,v, method="kendall")
} else {
tau <- rhotau
}
ofunc_tau <- function(trial_para, target_tau=NA) {
tau <- 1 + (4/trial_para) * (D1(trial_para)$value - 1) # Joe (2015, p. 166)
return(target_tau - tau)
}
# uniroot(ofunc, interval=c(-3000, +3000), target_tau=0.998)$root
# Error in integrate(function(t, k = 1) x^(-k) * (t^k) * (exp(t) - 1)^(-1), :
# the integral is probably divergent
para <- NULL
try(para <- uniroot(ofunc_tau, interval=c(big.lwr, big.upr), target_tau=tau)$root)
if(is.null(para)) {
try(para <- uniroot(ofunc_tau, interval=c(big.lwr, big.upr), target_tau=tau)$root)
}
names(para) <- "theta"
names(tau ) <- "Kendall Tau"
return(list(para=para, tau=tau))
} else {
stop("should not be here in logic")
}
}
if(length(para) == 1) {
if(abs(para) < para.small) {
tau <- 0
} else {
if(userhotau_chk) {
# design choice for lesson is to compute tau again for purposes of outer limits of operation
# but would be better to accept a precomputed tau just once for given parameter and pass
# as another argument to this function to avoid the D1() call on the next line.
d1 <- D1(para)$value
tau <- 1 + ( 4/para) * (d1 - 1) # Joe (2015, p. 166)
rho <- 1 + (12/para) * (D2(para)$value - d1) # Joe (2015, p. 166)
# print(c(rho, tau))
}
}
} else {
warning("Parameter Theta can not be a vector")
return(NULL)
}
if(length(u) > 1 & length(v) > 1 & length(u) != length(v)) { # standard syntax in copBasic style
warning("length u = ", length(u), " and length v = ", length(v))
warning("longer object length is not a multiple of shorter object length, ",
"no recycling")
return(NA)
}
if(length(u) == 1) { # standard syntax in copBasic style
u <- rep(u, length(v))
} else if(length(v) == 1) {
v <- rep(v, length(u))
}
if(abs(para[1]) < para.small) return(u*v) # independence
if(userhotau_chk) {
if( tau > +large.tau ) return( M(u,v) ) # upper bound copula, perfect 1:+1, limiting condition
if( tau < -large.tau ) return( W(u,v) ) # lower bound copula, perfect 1:-1, limiting condition
if( rho > +large.rho ) return( M(u,v) ) # upper bound copula, perfect 1:+1, limiting condition
if( rho < -large.rho ) return( W(u,v) ) # lower bound copula, perfect 1:-1, limiting condition
} else {
if(para > +large.para) return( M(u,v) ) # upper bound copula, perfect 1:+1, limiting condition
if(para < -large.para) return( W(u,v) ) # lower bound copula, perfect 1:-1, limiting condition
}
# Parameter reflection behavior of partial derivative or its inversion by copBasic::derCOP()
# and copBasic::derCOPinv() for the positive parameters breaks down as log(0) encounter, but the
# mirror image of the parameter in the negative domains does not see break down, so reverse
# the parameter for the copula computations but reflect the v.
p <- para
if(para > 0) { v <- 1 - v; p <- -p } # reflection PART A, like "grave" operation in copBasic::COP()
#a <- exp(-p); b <- exp(-p*u); c <- exp(-p*v) # THE FRANK COPULA
#cop <- (1 - a - (1 - b)*(1 - c) ) / (1 - a) # THE FRANK COPULA
#cop <- -para[1]^(-1) * log(cop) # THE FRANK COPULA
# see ?expm1
a <- -expm1(-p); b <- -expm1(-p*u); c <- -expm1(-p*v) # THE FRANK COPULA
cop <- (a - b*c ) / a # THE FRANK COPULA
lcop <- log(cop) # THE FRANK COPULA
lcop[lcop == -Inf] <- log(.Machine$double.eps) # THE FRANK COPULA
cop <- -p^(-1) * lcop # THE FRANK COPULA
# The parameter reversing might protect us from the lcop == -Inf meaning that
# (a - b*c) ---> 0 but let us use one last insurance policy of sorts and make zero
# become .Machine$double.eps. Likely, real protection is the large.para setting at function top.
if(para > 0) cop <- u - cop # reflection PART B, like "grave" operation in copBasic::COP()
return(cop)
}
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.