Elements of Copula Modeling with R

Code from Chapter 4

Below is the R code from Chapter 4 of the book “Elements of Copula Modeling with R”. The code is also available as an R script. Please cite the book or package when using the code; in particular, in publications.

## By Marius Hofert, Ivan Kojadinovic, Martin Maechler, Jun Yan

## R script for Chapter 4 of Elements of Copula Modeling with R


library(copula)


### 4.1 Estimation under a parametric assumption on the copula #################

### 4.1.1 Parametrically estimated margins #####################################

### Estimation of copula parameters via the MLE

## The "unknown" copula (a 2-dim. Clayton copula with parameter 3)
cc <- claytonCopula(3)
## The "unknown" distribution (N(0,1), Exp(1) margins)
mcc <- mvdc(cc, margins = c("norm", "exp"),
            paramMargins = list(list(mean = 0, sd = 1),
                                list(rate = 1)))
## Generate the "observed" sample
set.seed(712)
n <- 1000
X <- rMvdc(n, mvdc = mcc)
## The function fitMvdc() estimates all the parameters of the mvdc object
## mcc (whose parameter values are not used). Starting values need to be
## provided.
start <- c(mu0 = mean(X[,1]), sig0 = sd(X[,1]), lam0 = 1 / mean(X[,2]),
           th0 = 2)
(mle <- fitMvdc(X, mvdc = mcc, start = start))

summary(mle)


### Estimation of copula parameters via the IFME

## Parametric pseudo-observations obtained from X by marginal MLE
U <- cbind(pnorm(X[,1], mean = mean(X[,1]),
                 sd = sqrt((n - 1) / n) * sd(X[,1])),
           pexp(X[,2], rate = 1 / mean(X[,2])))
ifme <- fitCopula(claytonCopula(), data = U, method = "ml")
summary(ifme)

optimMeth(claytonCopula(), method = "ml", dim = 2)


### 4.1.2 Nonparametrically estimated margins #################################

### Pseudo-observations of daily log-returns

data(rdj) # 'head(rdj)' for looking at the first six observations
splom2(rdj[,2:4], cex = 0.4, col.mat = adjustcolor("black", 0.5))

U <- pobs(rdj[,2:4])
splom2(U, cex = 0.4, col.mat = "black")


### Estimation of copula parameters via the method of moments based on Kendall's tau

## The "unknown" copula (a 2-dim. Gumbel-Hougaard copula with parameter 3)
gc <- gumbelCopula(3)
## The "unknown" distribution (N(0,1) margins)
mgc <- mvdc(gc, margins = c("norm", "norm"),
            paramMargins = list(list(mean = 0, sd = 1),
                                list(mean = 0, sd = 1)))
## Generate the "observed" sample
set.seed(49)
X <- rMvdc(1000, mvdc = mgc)
## The sample version of Kendall's tau
tau.n <- cor(X[,1], X[,2], method = "kendall")
## The corresponding copula parameter estimate
(itau <- iTau(gc, tau = tau.n))

stopifnot(all.equal(itau, 1 / (1 - tau.n))) # the same
## The same but with a standard error
summary(fitCopula(gumbelCopula(), data = pobs(X), method = "itau"))


### Estimation of copula parameters via the method of moments based on Spearman's rho

## The "unknown" copula (a 2-dim. normal copula with parameter 0.5)
nc <- normalCopula(0.5)
## Generate the "observed" sample
set.seed(314)
X <- rCopula(1000, nc)
## The sample estimate of Spearman's rho
rho.n <- cor(X[,1], X[,2], method = "spearman")
## The corresponding copula parameter estimate
(irho <- iRho(nc, rho = rho.n))

stopifnot(all.equal(irho, 2 * sin(pi * rho.n / 6))) # the same
## The same but with a standard error
summary(fitCopula(normalCopula(), data = pobs(X), method = "irho"))


### Application of the estimation via the method of moments based on Kendall's tau

data(danube, package = "lcopula") # already pseudo-observations
U <- as.matrix(danube)
plot(U, xlab = "Donau", ylab = "Inn")

summary(fitCopula(gumbelCopula(), data = U, method = "itau"))

summary(fitCopula(plackettCopula(), data = U, method = "itau"))

summary(fitCopula(normalCopula(), data = U, method = "itau"))


### Estimation of copula parameters via the MPLE

## The "unknown" copula (a 2-dim. Frank copula with parameter 3)
fc <- frankCopula(3)
## Generate the "observed" sample
set.seed(271)
U <- rCopula(1000, fc)
## Compute the MPLE and its standard error
summary(fitCopula(frankCopula(), data = pobs(U), method = "mpl"))

optimMeth(frankCopula(), method = "mpl", dim = 2)


### Application of the estimation via the MPLE

U <- pobs(rdj[,2:4]) # compute the pseudo-observations
## MPLE for the normal copula
summary(fitCopula(normalCopula(dim = 3, dispstr = "un"), data = U))

## MPLE for the t copula
summary(fitCopula(tCopula(dim = 3, dispstr = "un"), data = U))


### 4.1.3 Estimators of elliptical copula parameters ###########################

### Estimation of normal copula parameters via method-of-moments

data(SMI.12) # load the SMI constituent data
library(qrmtools)
X <- returns(SMI.12) # compute log-returns
U <- pobs(X) # compute pseudo-observations
d <- ncol(U) # 20 dimensions

f.irho <- fitCopula(normalCopula(dim = d, dispstr = "un"), data = U,
                    method = "irho")
f.itau <- fitCopula(normalCopula(dim = d, dispstr = "un"), data = U,
                    method = "itau")

P.irho <- p2P(coef(f.irho), d = d)
P.itau <- p2P(coef(f.itau), d = d)


### Estimation of t copula parameters using the method of Mashal and Zeevi

fit <- fitCopula(tCopula(dim = d, dispstr = "un"), data = U,
                 method = "itau.mpl")


### Linear correlation vs Kendall's tau for estimating $t$ distributions

##' @title Compute (repeated) parameter estimates for the bivariate t
##' @param nu degrees of freedom parameter
##' @param rho correlation parameter
##' @param B number of replications
##' @param n sample size
##' @return (B, 2)-matrix containing the B estimates computed
##'         via Pearson's rho and Kendall's tau
estimates <- function(nu, rho, B, n)
{
    ## Generate data (B-list of (n, 2)-matrices)
    tc <- tCopula(rho, df = nu) # define the corresponding t copula
    X <- lapply(1:B, function(i) qt(rCopula(n, copula = tc), df = nu))
    ## For each of the B data sets, estimate the correlation parameter of the
    ## t distribution via Pearson's rho and Kendall's tau.
    pea <- vapply(X, function(x) cor(x[,1], x[,2]), numeric(1))
    ken <- iTau(tCopula(df = nu),
                tau = sapply(X, function(x) cor(x, method = "kendall")[2,1]))
    ## Return
    cbind(Pearson = pea, Kendall = ken) # (B, 2)-matrix
}

set.seed(271) # for reproducibility
nu <- 3 # degrees of freedom
rho <- 0.5 # correlation parameter
r <- estimates(nu, rho = rho, B = 3000, n = 90) # (B, 2)-matrix
varP <- var(r[,"Pearson"]) # variance of sample linear correlation
varK <- var(r[,"Kendall"]) # variance of inverting Kendall's tau
VRF <- varP / varK # variance reduction factor
PIM <- (varP - varK) / varP * 100 # % improvement
boxplot(r, names = c("Sample linear correlation", "Inverting Kendall's tau"),
        ylab = substitute("Estimates of"~rho~"of a"
                          ~t[nu.]~"distribution with"~rho==rho.,
                          list(nu. = nu, rho. = rho)))
mtext(substitute("VRF (% improvement):"~~v~"("*i*"%)",
                 list(v = round(VRF, 2), i = round(PIM))),
      side = 4, line = 1, adj = 0, las = 0)

## Estimate variance reduction factor for various nu and rho
nu. <- 2 + 2^seq(-2, 7, by = 0.5) # degrees of freedom to consider
rho. <- c(-0.99, -0.8, 0, 0.8, 0.99) # correlations to consider
res <- lapply(nu., function(nu..) lapply(rho., function(rho..)
              estimates(nu.., rho = rho.., B = 3000, n = 90)))
vars <- lapply(res, function(r) lapply(r, function(r.)
               var(r.[,"Pearson"])/var(r.[,"Kendall"])))
VRF. <- matrix(unlist(vars), nrow = length(rho.), ncol = length(nu.),
               dimnames = list(rho = rho., nu = nu.))
ylim <- range(VRF.)
lrho <- length(rho.)
plot(nu., VRF.[1,], type = "l", log = "xy", ylim = ylim, col = 1,
     xlab = "Degrees of freedom",
     ylab = "VRF (correlation over inverting Kendall's tau)")
for(k in 2:lrho) lines(nu., VRF.[k,], col = k)
abline(h = 1, lty = 2)
legend("topright", bty = "n", lty = rep(1, lrho), col = 1:lrho,
       legend = as.expression(lapply(1:lrho, function(k)
           substitute(rho == rho., list(rho. = rho.[k])))))


### 4.1.5 Estimation of copula models with partly fixed parameters #############

### Estimation of elliptical copulas with partly fixed parameters

## The "unknown" copula (a 3-dim. normal copula)
nc  <- normalCopula(param = c(0.6, 0.3, 0.2), dim = 3, dispstr = "un")
## Generate the "observed" sample and compute corresponding pobs
set.seed(819)
U <- pobs(rCopula(1000, nc))
## A trivariate normal copula whose first parameter is fixed to 0.6
(ncf <- normalCopula(param = fixParam(c(0.6, NA_real_, NA_real_),
                                      c(TRUE, FALSE, FALSE)),
                     dim = 3, dispstr = "un"))

fitCopula(ncf, data = U) # MPLE

fitCopula(ncf, data = U, method = "itau")

fitCopula(ncf, data = U, method = "irho")

fixedParam(nc) <- c(TRUE, FALSE, FALSE)
nc

## The "unknown" copula is a 3-dim. t copula (with 4 d.o.f. by default)
tc  <- tCopula(param = c(0.6, 0.3, 0.2), dim = 3, dispstr = "un")
## Generate the "observed" sample and compute corresponding pobs
set.seed(314)
U <- pobs(rCopula(1000, tc))
## A trivariate t copula whose first two parameters are fixed to 0.6 and 0.3
(tcf <- tCopula(param = fixParam(c(0.6, 0.3, NA_real_),
                                 c(TRUE, TRUE, FALSE)),
                dim = 3, dispstr = "un"))

fitCopula(tcf, data = U) # MPLE

fitCopula(tcf, data = U, method = "itau.mpl")

## A trivariate t copula whose first correlation parameter is fixed to 0.6
## and whose number of degrees of freedom is fixed to 4 (default value)
(tcf2 <- tCopula(param = fixParam(c(0.6, NA_real_, NA_real_),
                                  c(TRUE, FALSE, FALSE)),
                 dim = 3, dispstr = "un", df.fixed = TRUE))

fitCopula(tcf2, data = U) # MPLE

fitCopula(tcf2, data = U, method = "itau")


### Estimation of Khoudraji-Clayton copulas with partly fixed parameters

## The "unknown" copula (a 2-dim. Khoudraji-Clayton copula)
kc <- khoudrajiCopula(copula2 = claytonCopula(6), shapes = c(0.4, 1))
set.seed(1307)
U <- pobs(rCopula(1000, kc))
## The default optimization method for fitting bivariate copulas
## constructed with Khoudraji's device by MPLE
optimMeth(khoudrajiCopula(), method = "mpl", dim = 2)

try(fitCopula(khoudrajiCopula(copula2 = claytonCopula()),
              start = c(1.1, 0.5, 0.5), data = U))

fitCopula(khoudrajiCopula(copula2 = claytonCopula()),
          start = c(1.1, 0.5, 0.5), data = U,
          optim.method = "Nelder-Mead")

kcf <- khoudrajiCopula(copula2 = claytonCopula(),
                       shapes = fixParam(c(NA_real_, 1),
                                         c(FALSE, TRUE)))
fitCopula(kcf, start = c(1.1, 0.5), data = U)


### 4.2 Nonparametric estimation of the copula ################################

### 4.2.1 The empirical copula #################################################

### Nonparametric estimation by the empirical copula

## The "unknown" copula (a 3-dim. Clayton copula with parameter 3)
d <- 3
cc <- claytonCopula(3, dim = d)
## Generate a sample from the copula, which will be transformed
## to pseudo-observations in 'C.n()'
n <- 1000
set.seed(65)
U <- rCopula(n, copula = cc)
## Generate random points where to evaluate the empirical copula
v <- matrix(runif(n * d), nrow = n, ncol = d)
ec <- C.n(v, X = U)
## Compare with the true copula; increase n to decrease the error
true <- pCopula(v, copula = cc)
round(mean(abs(true - ec) / true) * 100, 2) # mean relative error (in %)


### The empirical beta and checkerboard copulas

gc <- gumbelCopula(4, dim = 3) # the 'unknown' copula
##' @title Mean relative error in % (for empirical copula, empirical beta
##'        copula and empirical checkerboard copula)
##' @param n sample size
##' @param cop copula object
##' @return mean relative errors in %
compareEmpCops <- function(n, cop)
{
    d <- dim(cop)
    U <- rCopula(n, copula = cop) # a sample from the true copula
    v <- matrix(runif(n * d), nrow = n, ncol = d) # random evaluation points
    ec    <- C.n(v, X = U) # the empirical copula values
    beta  <- C.n(v, X = U, smoothing = "beta") # the emp. beta cop. values
    check <- C.n(v, X = U, smoothing = "checkerboard") # emp. check. cop. val
    true <- pCopula(v, copula = cop) # the true copula values
    c(ec    = mean(abs(true - ec) / true),
      beta  = mean(abs(true - beta) / true),
      check = mean(abs(true - check) / true)) * 100 # mean rel. error in %
}

set.seed(2013)
round(rowMeans(replicate(100, compareEmpCops(30, cop = gc))), 2)

set.seed(2013)
round(rowMeans(replicate(100, compareEmpCops(300, cop = gc))), 2)

set.seed(2008)
U <- rCopula(30, copula = gc) # a sample from the true copula
m <- 100 # number of evaluation points
v <- runif(m) # random points where to evaluate the margins of the estimators
w1 <- cbind(v, 1, 1) # evaluations points margin 1
w2 <- cbind(1, v, 1) # evaluations points margin 2
w3 <- cbind(1, 1, v) # evaluations points margin 3
## Checks
stopifnot(all.equal(C.n(w1, X = U, smoothing = "beta"), v))
stopifnot(all.equal(C.n(w2, X = U, smoothing = "beta"), v))
stopifnot(all.equal(C.n(w3, X = U, smoothing = "beta"), v))
stopifnot(all.equal(C.n(w1, X = U, smoothing = "checkerboard"), v))
stopifnot(all.equal(C.n(w2, X = U, smoothing = "checkerboard"), v))
stopifnot(all.equal(C.n(w3, X = U, smoothing = "checkerboard"), v))


### 4.2.2 Under extreme-value dependence #######################################

### Nonparametric estimation of the Pickands dependence function

## The "unknown" copula (a 2-dim. extreme-value copula)
kg <- khoudrajiCopula(copula1 = indepCopula(),
                      copula2 = gumbelCopula(3),
                      shapes = c(0.6, 0.95))
## Generate a sample from this copula transformed to pseudo-observations
set.seed(172)
U <- pobs(rCopula(100, copula = kg))

## Graphs of the Pickands dependence function A and of its two estimates
curve(An.biv(U, x, estimator = "Pickands"), from = 0, to = 1, col = 1,
      lwd = 2, ylim = c(0.5, 1), xlab = "t", ylab = "A(t)")
curve(An.biv(U, x, estimator = "CFG"), 0, 1, add = TRUE, lwd = 2, col = 2)
curve(A(kg, w = x), 0, 1, add = TRUE, lwd = 2, col = 3)
lines(c(0, 0.5, 1), c(1, 0.5, 1), lty = 2)
lines(c(0, 1),      c(1, 1),      lty = 2)
legend("bottomright", bty = "n", lwd = 2, col = 1:3,
       legend = expression({A[list(n,c)]^{P}}(t), {A[list(n,c)]^{CFG}}(t),
                           {A[bold(theta)]^{KGH}}(t)),
       inset = 0.02, y.intersp = 1.2)