Raw File
kdevine.R
#' Kernel density estimatior based on simplified vine copulas
#'
#' Implements the vine-copula based estimator of Nagler and Czado (2016). The
#' marginal densities are estimated by \code{\link{kde1d}}, the vine copula
#' density by \code{\link{kdevinecop}}.
#'
#' @param data (\eqn{n x d}) data matrix.
#' @param mult.1d numeric; all bandwidhts for marginal kernel density estimation
#' are multiplied with \code{mult.1d}.
#' @param xmin numeric vector of length d; see \code{\link{kde1d}}.
#' @param xmax numeric vector of length d; see \code{\link{kde1d}}.
#' @param copula.type either \code{"kde"} (default) or \code{"parametric"} for
#' kernel or parametric estimation of the vine copula.
#' @param ... further arguments passed to \code{\link{kde1d}} or
#'  \code{\link{kdevinecop}}.
#'
#' @return An object of class \code{kdevine}.
#'
#' @seealso
#' \code{\link{dkdevine}}
#' \code{\link{kde1d}}
#' \code{\link{kdevinecop}}
#'
#' @references
#' Nagler, T., Czado, C. (2016) \cr
#' Evading the curse of dimensionality in nonparametric density estimation with
#' simplified vine copulas. \cr
#' \emph{Journal of Multivariate Analysis 151, 69-89 (doi:10.1016/j.jmva.2016.07.003)}
#'
#' @examples
#' # load data
#' data(wdbc, package = "kdecopula")
#' \dontshow{wdbc <- wdbc[1:30, ]}
#' # estimate density (use xmin to indicate positive support)
#' fit <- kdevine(wdbc[, 5:7], xmin = rep(0, 3))
#'
#' # evaluate density estimate
#' dkdevine(c(1000, 0.1, 0.1), fit)
#'
#' # plot simulated data
#' pairs(rkdevine(nrow(wdbc), fit))
#'
#' @importFrom VineCopula RVineStructureSelect RVineCopSelect
#' @export
kdevine <- function(data, mult.1d = 1, xmin = NULL, xmax = NULL, copula.type = "kde", ...) {
    data <- as.matrix(data)
    d <- ncol(data)

    ## sanity checks
    if (!is.null(xmin)) {
        if (length(xmin) != d)
            stop("'xmin' has to be of length d")
    }
    if (!is.null(xmax)) {
        if (length(xmax) != d)
            stop("'xmin' has to be of length d")
    }
    if (length(list(...)$bw) != d && !is.null(list(...)$bw))
        stop("'bw' hast to be of length d")
    if (is.null((list(...)$copula.type))) {
        copula.type <- "kde"
    } else {
        copula.type <- list(...)$copula.type
    }
    if (ncol(data) != d)
        data <- t(data)
    stopifnot(ncol(data) == d)

    ## estimation of the marginals
    marg.dens <- as.list(numeric(d))
    for (k in 1:d) {
        marg.dens[[k]] <- kde1d(data[, k],
                                xmin = list(...)$xmin[k],
                                xmax = list(...)$xmax[k],
                                bw   = list(...)$bw[k],
                                mult = mult.1d)
    }
    res <- list(marg.dens = marg.dens)

    ## estimation of the R-vine copula (only if d > 1)
    if (d > 1) {
        # transform to copula data
        u <- sapply(1:d, function(k) pkde1d(data[, k], marg.dens[[k]]))

        if (copula.type == "kde") {
            res$vine  <- suppressWarnings(
                kdevinecop(u,
                           matrix      = list(...)$matrix,
                           method      = list(...)$method,
                           mult        = list(...)$mult,
                           info        = list(...)$info,
                           test.level  = list(...)$test.level,
                           trunc.level = list(...)$trunc.level,
                           treecrit    = list(...)$treecrit,
                           cores       = list(...)$cores)
            )
        } else if (copula.type == "parametric") {
            # get family and matrix if available
            fam <- list(...)$familyset
            if (is.null(fam))
                fam <- NA
            mat <- list(...)$Matrix

            # fit parametric vine
            res$vine <- if (is.null(mat) & d > 2) {
                # structure selection if no matrix is provided and d > 2
                RVineStructureSelect(u, familyset = fam)
            } else if (d == 2) {
                # select copula for default structure if d = 2
                RVineCopSelect(u,
                               familyset = fam,
                               Matrix = matrix(c(2, 1, 0, 1), 2, 2))
            } else {
                # or select copulas to provided structure
                RVineCopSelect(u,
                               familyset = fam,
                               Matrix = mat)
            }
        } else {
            stop("copula.type not implemented.")
        }
    }

    ## return results
    res$copula.type <- copula.type
    class(res) <- "kdevine"
    res
}

#' Evaluate the density of a kdevine object
#'
#' @param x (\eqn{m x d}) matrix of evaluation points (or vector of length \eqn{d}).
#' @param obj a \code{kdevine} object.
#'
#' @return The density estimate evaluated at \code{x}.
#'
#' @seealso
#' \code{\link{kdevine}}
#'
#' @examples
#' # load data
#' data(wdbc)
#' \dontshow{wdbc <- wdbc[1:30, ]}
#' # estimate density (use xmin to indicate positive support)
#' fit <- kdevine(wdbc[, 5:7], xmin = rep(0, 3))
#'
#' # evaluate density estimate
#' dkdevine(c(1000, 0.1, 0.1), fit)
#'
#' @importFrom VineCopula RVinePDF
#'
#' @export
dkdevine <- function(x, obj) {
    x <- as.matrix(x)
    n <- length(obj$marg.dens[[1]]$data)
    if (ncol(x) == 1)
        x <- t(x)
    d <- ncol(x)

    stopifnot(class(obj) == "kdevine")
    if (length(obj$marg.dens) != d)
        stop("'x' has incorrect dimension")

    ## evaluate marginals
    margvals <- u <- x
    for(i in 1:d){
        margvals[, i] <- dkde1d(x[, i], obj$marg.dens[[i]])
    }

    ## evaluate copula density (if necessary)
    if (!is.null(obj$vine)) {
        # PIT to copula level
        for (i in 1:d)
            u[, i] <- pkde1d(x[, i], obj$marg.dens[[i]])
        if (inherits(obj$vine, "kdevinecop")) {
            vinevals <- dkdevinecop(u, obj = obj$vine, stable = TRUE)
        } else if (inherits(obj$vine, "RVineMatrix")) {
            vinevals <- RVinePDF(u, obj$vine)
        } else {
            stop("vine has incompatible type")
        }
    } else {
        vinevals <- rep(1, nrow(x))
    }

    ## final density estimate is product of marginals and copula density
    apply(cbind(margvals, vinevals), 1, prod)
}


#' Simulate from a kdevine object
#'
#' @param n number of observations.
#' @param obj a \code{kdevine} object.
#'
#' @return An \eqn{n x d} matrix of simulated data from the \code{kdevine}
#' object.
#'
#' @seealso
#' \code{\link{kdevine}},
#' \code{\link{rkdevinecop}},
#' \code{\link{rkde1d}}
#'
#' @examples
#' # load and plot data
#' data(wdbc)
#' \dontshow{wdbc <- wdbc[1:30, ]}
#' # estimate density
#' fit <- kdevine(wdbc[, 5:7], xmin = rep(0, 3))
#'
#' # plot simulated data
#' pairs(rkdevine(nrow(wdbc), fit))
#'
#' @importFrom VineCopula pobs RVineSim
#' @export
rkdevine <- function(n, obj) {
    # simulate from copula
    usim <- switch(obj$copula.type,
                   "kde" = rkdevinecop(n, obj$vine),
                   "parametric" = RVineSim(n, obj$vine))
    # use quantile transformation for marginals
    sapply(seq_len(ncol(usim)),
           function(i) qkde1d(usim[, i], obj$marg.dens[[i]]))
}




back to top