Learn R Programming

Matrix (version 0.999375-6)

nearPD: Nearest Matrix to a Positive Definite Matrix

Description

Computes the nearest positive definite matrix to an approximate one, typically a correlation or variance-covariance matrix.

Usage

nearPD(x, corr = FALSE, keepDiag = FALSE, do2eigen = TRUE,
       only.values = FALSE,
       eig.tol = 1e-06, conv.tol = 1e-07, posd.tol = 1e-08,
       maxit = 100, trace = FALSE)

Arguments

x
numeric $n \times n$ approximately positive definite matrix, typically an approximation to a correlation or covariance matrix.
corr
logical indicating if the matrix should be a correlation matrix.
keepDiag
logical, generalizing corr: if TRUE, the resulting matrix should have the same diagonal (diag(x)) as the input matrix.
do2eigen
logical indicating if a posdefify() eigen step should be applied to the result of the Hingham algorithm.
only.values
logical; if TRUE, the result is just the vector of eigen values of the approximating matrix.
eig.tol
defines relative positiveness of eigenvalues compared to largest one, $\lambda_1$. Eigen values $\lambda_k$ are treated as if zero when $\lambda_k / \lambda_1
conv.tol
convergence tolerance for Hingham algorithm.
posd.tol
tolerance for enforcing positive definiteness (in the final posdefify step when do2eigen is TRUE).
maxit
maximum number of iterations allowed.
trace
logical or integer specifying if convergence monitoring should be traced.

Value

  • If only.values = TRUE, a numeric vector of eigen values of the approximating matrix; Otherwise, as by default, an S3 object of class "nearPD", basically a list with components
  • mata matrix of class dpoMatrix, the computed positive-definite matrix.
  • eigenvaluesnumeric vector of eigen values of mat.
  • corrlogical, just the argument corr.
  • normFthe Frobenius norm (norm(x-X, "F")) of the difference between the original and the resulting matrix.
  • iterationsnumber of iterations needed.
  • convergedlogical indicating if iterations converged.

Details

This implements the algorithm of Higham (2002), and then forces positive definiteness using code from posdefify. The algorithm of Knol DL and ten Berge (1989) (not implemented here) is more general in (1) that it allows contraints to fix some rows (and columns) of the matrix and (2) to force the smallest eigenvalue to have a certain value.

Note that setting corr = TRUE just sets diag(.) <- 1 within the algorithm.

References

Cheng, Sheung Hun and Higham, Nick (1998) A Modified Cholesky Algorithm Based on a Symmetric Indefinite Factorization; SIAM J. Matrix Anal. Appl., 19, 1097--1110.

Knol DL, ten Berge JMF (1989) Least-squares approximation of an improper correlation matrix by a proper one. Psychometrika 54, 53--61.

Highham (2002) Computing the nearest correlation matrix - a problem from finance; IMA Journal of Numerical Analysis 22, 329--343.

See Also

A first version of this (with non-optional corr=TRUE) has been available as nearcor(); and more simple versions with a similar purpose posdefify(), both from package sfsmisc.

Examples

Run this code
set.seed(27)
 m <- matrix(round(rnorm(25),2), 5, 5)
 m <- m + t(m)
 diag(m) <- pmax(0, diag(m)) + 1
 (m <- round(cov2cor(m), 2))

 str(near.m <- nearPD(m, trace = TRUE))
 round(near.m$mat, 2)
 norm(m - near.m$mat) # 1.102

 if(require("sfsmisc")) {
    m2 <- posdefify(m) # a simpler approach
    norm(m - m2)  # 1.185, i.e., slightly "less near"
 }

 round(nearPD(m, only.values=TRUE), 9)

## A longer example, extended from Jens' original,
## showing the effects of some of the options:

pr <- Matrix(c(1,     0.477, 0.644, 0.478, 0.651, 0.826,
               0.477, 1,     0.516, 0.233, 0.682, 0.75,
               0.644, 0.516, 1,     0.599, 0.581, 0.742,
               0.478, 0.233, 0.599, 1,     0.741, 0.8,
               0.651, 0.682, 0.581, 0.741, 1,     0.798,
               0.826, 0.75,  0.742, 0.8,   0.798, 1),
             nrow = 6, ncol = 6)

nc.  <- nearPD(pr, conv.tol = 1e-7) # default
nc.$iterations  # 2
nc.1 <- nearPD(pr, conv.tol = 1e-7, corr = TRUE)
nc.1$iterations # 11 (!)
ncr   <- nearPD(pr, conv.tol = 1e-15)
str(ncr)# 3 iterations
ncr.1 <- nearPD(pr, conv.tol = 1e-15, corr = TRUE)
ncr.1 $ iterations #  27 !

## But indeed, the 'corr = TRUE' constraint did ensure a better solution;
## cov2cor() does not just fix it up equivalently :
norm(pr - cov2cor(ncr$mat)) # = 0.09994
norm(pr -       ncr.1$mat)  # = 0.08746

Run the code above in your browser using DataLab