https://github.com/cran/fields
Tip revision: 6c8b30169bba182a68765ee3cb9b4e2ef7d38332 authored by Doug Nychka on 16 November 2011, 00:00:00 UTC
version 6.6.3
version 6.6.3
Tip revision: 6c8b301
mKrig.family.R
# fields, Tools for spatial data
# Copyright 2004-2007, Institute for Mathematics Applied Geosciences
# University Corporation for Atmospheric Research
# Licensed under the GPL -- www.gpl.org/licenses/gpl.html
mKrig <- function(x, y, weights = rep(1, nrow(x)), Z=NULL,
lambda = 0, cov.function = "stationary.cov", m = 2, chol.args = NULL,
cov.args = NULL, find.trA = TRUE, NtrA = 20, iseed = 123,
...) {
# grab other arguments for covariance function
cov.args <- c(cov.args, list(...))
# see comments in Krig.engine.fixed for algorithmic commentary
#
#
# default values for Cholesky decomposition, these are important
# for sparse matrix decompositions
# check for duplicate x's.
# stop if there are any
if (any(duplicated(cat.matrix(x))))
stop("locations are not unique see help(mKrig) ")
if (any(is.na(y)))
stop("Missing values in y should be removed")
if(!is.null(Z)){
Z<- as.matrix(Z)}
# create fixed part of model as m-1 order polynomial
Tmatrix <- cbind(fields.mkpoly(x, m), Z)
# set some dimensions
np <- nrow(x)
nt<- ncol(Tmatrix)
nZ<- ifelse (is.null(Z), 0, ncol(Z))
ind.drift <- c(rep(TRUE, (nt-nZ)), rep( FALSE, nZ))
# as a place holder for reduced rank Kriging, distinguish between
# observations locations and locations to evaluate covariance.
# (this is will also predict.mKrig to handle a Krig object)
knots <- x
# covariance matrix at observation locations
# NOTE: if cov.function is a sparse constuct then tempM will be sparse.
# see e.g. wendland.cov
tempM <- do.call(cov.function, c(cov.args, list(x1 = x, x2 = x)))
#
# decide how to handle the pivoting.
# one wants to do pivoting if the matrix is sparse.
# if tempM is not a matrix assume that it is in sparse format.
#
sparse.flag <- !is.matrix(tempM)
#
# set arguments that are passed to cholesky
#
if (is.null(chol.args)) {
chol.args <- list(pivot = sparse.flag)
}
else {
chol.args <- chol.args
}
# record sparsity of tempM
nzero <- ifelse(sparse.flag, length(tempM@entries), np^2)
# add diagonal matrix that is the observation error Variance
# NOTE: diag must be a overloaded function to handle sparse format.
if (lambda != 0) {
diag(tempM) <- (lambda/weights) + diag(tempM)
}
# At this point tempM is proportional to the covariance matrix of the
# observation vector, y.
#
# cholesky decoposition of tempM
# do.call used to supply other arguments to the function
# especially for sparse applications.
# If chol.args is NULL then this is the same as
# Mc<-chol(tempM)
Mc <- do.call("chol", c(list(x = tempM), chol.args))
lnDetCov <- 2 * sum(log(diag(Mc)))
# Efficent way to multply inverse of Mc times the Tmatrix
VT <- forwardsolve(Mc, x = Tmatrix, transpose = TRUE, upper.tri = TRUE)
qr.VT <- qr(VT)
# start linear algebra to find solution
# Note that all these expressions make sense if y is a matrix
# of several data sets and one is solving for the coefficients
# of all of these at once. In this case d.coef and c.coef are matrices
#
# now do generalized least squares for d
d.coef <- as.matrix(qr.coef(qr.VT, forwardsolve(Mc, transpose = TRUE,
y, upper.tri = TRUE)))
# and then find c.
# find the coefficents for the spatial part.
c.coef <- as.matrix(forwardsolve(Mc, transpose = TRUE, y - Tmatrix %*%
d.coef, upper.tri = TRUE))
# save intermediate result this is t(y- T d.coef)( M^{-1}) ( y- T d.coef)
quad.form <- c(colSums(as.matrix(c.coef^2)))
# find c coefficients
c.coef <- backsolve(Mc, c.coef)
# GLS covariance matrix for fixed part.
Rinv <- solve(qr.R(qr.VT))
Omega <- Rinv %*% t(Rinv)
# MLE estimate of rho and sigma
# rhohat <- c(colSums(as.matrix(c.coef * y)))/(np - nt)
rho.MLE <- quad.form/np
rhohat<- c(colSums(as.matrix(c.coef * y)))/np
shat.MLE <- sigma.MLE <- sqrt(lambda * rho.MLE)
# the log profile likehood with rhohat and dhat substituted
# leaving a profile for just lambda.
# note that this is _not_ -2*loglike just the log and
# includes the constants
lnProfileLike <- (-np/2 - log(2*pi)*(np/2)
- (np/2)*log(rho.MLE) - (1/2) * lnDetCov)
#
# return coefficients and include lambda as a check because
# results are meaningless for other values of lambda
# returned list is an 'object' of class mKrig (micro Krig)
# also save the matrix decopositions so coefficients can be
# recalculated for new y values.
out <- list(d = (d.coef), c = (c.coef), nt = nt, np = np,
lambda.fixed = lambda, x = x, knots = knots, cov.function.name = cov.function,
args = cov.args, m = m, chol.args = chol.args, call = match.call(),
nonzero.entries = nzero, shat.MLE = shat.MLE, rho.MLE = rho.MLE,
sigma.MLE, rhohat = rhohat, lnProfileLike = lnProfileLike,
lnDetCov = lnDetCov,quad.form = quad.form,
Omega = Omega, qr.VT = qr.VT, Mc = Mc,
Tmatrix = Tmatrix, ind.drift=ind.drift,nZ=nZ)
#
# find the residuals directly from solution
# to avoid a call to predict
out$residuals<- lambda*c.coef/weights
out$fitted.values <- y - out$residuals
# estimate effective degrees of freedom using Monte Carlo trace method.
if (find.trA) {
out2 <- mKrig.trace(out, iseed, NtrA)
out$eff.df <- out2$eff.df
out$trA.info <- out2$trA.info
out$GCV <- (sum(out$residuals^2)/np)/(1 - out2$eff.df/np)^2
if (NtrA < np) {
out$GCV.info <- (sum(out$residuals^2)/np)/(1 - out2$trA.info/np)^2
}
else {
out$GCV.info <- NA
}
}
else {
out$eff.df <- NA
out$trA.info <- NA
out$GCV <- NA
}
class(out) <- "mKrig"
return(out)
}
mKrig.trace <- function(object, iseed, NtrA) {
set.seed(iseed)
# if more tests that number of data points just
# find A exactly by np predicts.
if (NtrA >= object$np) {
Ey <- diag(1, object$np)
NtrA <- object$np
trA.info <- diag(predict.mKrig(object, ynew = Ey))
trA.est <- sum(trA.info)
}
else {
# if fewer tests then use random trace method
# find fitted.values for iid N(0,1) 'data' to calculate the
# the Monte Carlo estimate of tr A(lambda)
# basically repeat the steps above but take some
# short cuts because we only need fitted.values
# create random normal 'data'
Ey <- matrix(rnorm(object$np * NtrA), nrow = object$np,
ncol = NtrA)
trA.info <- colSums(Ey * (predict.mKrig(object, ynew = Ey)))
trA.est <- mean(trA.info)
}
return(list(trA.info = trA.info, eff.df = trA.est))
}
mKrig.coef <- function(object, y) {
# given new data y and the matrix decompositions in the
# mKrig object find coefficients d and c.
# d are the coefficients for the fixed part
# in this case hard coded for a low order polynomial
# c are coefficients for the basis functions derived from the
# covariance function.
#
# see mKrig itself for more comments on the linear algebra
#
# Note that all these expressions make sense if y is a matrix
# of several data sets and one is solving for the coefficients
# of all of these at once. In this case d.coef and c.coef are matrices
#
# generalized least squares for d
d.coef <- qr.coef(object$qr.VT, forwardsolve(object$Mc, transpose = TRUE,
y, upper.tri = TRUE))
# residuals from subtracting off fixed part
# of model as m-1 order polynomial
resid <- y - object$Tmatrix %*% d.coef
# and now find c.
c.coef <- forwardsolve(object$Mc, transpose = TRUE, resid,
upper.tri = TRUE)
c.coef <- backsolve(object$Mc, c.coef)
out <- list(d = (d.coef), c = (c.coef))
return(out)
}
print.mKrig <- function(x, digits = 4, ...) {
if( is.matrix(x$residuals)){
n<- nrow(x$residuals)
NData<- ncol(x$residuals)}
else{
n<- length( x$residuals)
NData<-1 }
c1 <- "Number of Observations:"
c2 <- n
if( NData>1){
c1<- c( c1,"Number of data sets fit:")
c2<- c( c2, NData)}
c1 <- c(c1, "Degree of polynomial null space ( base model):")
c2 <- c(c2, x$m - 1)
c1 <- c(c1, "Total number of parameters in base model")
c2 <- c(c2, x$nt)
if( x$nZ>0){
c1 <- c(c1, "Number of additional covariates (Z)")
c2 <- c(c2, x$nZ)}
if (!is.na(x$eff.df)) {
c1 <- c(c1, " Eff. degrees of freedom")
c2 <- c(c2, signif(x$eff.df, digits))
if (length(x$trA.info) < x$np) {
c1 <- c(c1, " Standard Error of estimate: ")
c2 <- c(c2, signif(sd(x$trA.info)/sqrt(length(x$trA.info)),
digits))
}
}
c1 <- c(c1, "Smoothing parameter")
c2 <- c(c2, signif(x$lambda.fixed, digits))
if( NData==1){
c1 <- c(c1, "MLE sigma ")
c2 <- c(c2, signif(x$shat.MLE, digits))
c1 <- c(c1, "MLE rho")
c2 <- c(c2, signif(x$rho.MLE, digits))}
c1 <- c(c1, "Nonzero entries in covariance")
c2 <- c(c2, x$nonzero.entries)
sum <- cbind(c1, c2)
dimnames(sum) <- list(rep("", dim(sum)[1]), rep("", dim(sum)[2]))
cat("Call:\n")
dput(x$call)
print(sum, quote = FALSE)
cat(" ", fill=TRUE)
cat(" Covariance Model:", x$cov.function, fill = TRUE)
if (x$cov.function == "stationary.cov") {
cat(" Covariance function: ", ifelse(is.null(x$args$Covariance),
"Exponential", x$args$Covariance), fill = TRUE)
}
if (!is.null(x$args)) {
cat(" Non-default covariance arguments and their values ",
fill = TRUE)
nlist <- as.character(names(x$args))
NL <- length(nlist)
for (k in 1:NL) {
cat(" Argument:", nlist[k]," " )
if(object.size(x$args[[k]]) <= 1024) {
cat("has the value(s): ",fill=TRUE)
print(x$args[[k]])}
else{
cat("too large to print value, size > 1K ...", fill =TRUE)}
}
}
invisible(x)
}
summary.mKrig<- function(object,...){
print.mKrig( object,...)}
predict.mKrig <- function(object, xnew = NULL, ynew = NULL,
derivative = 0, Z=NULL, drop.Z=FALSE,just.fixed=FALSE,...) {
# the main reason to pass new args to the covariance is to increase
# the temp space size for sparse multiplications
# other optional arguments from mKrig are passed along in the
# list object$args
cov.args <- list(...)
# predict at observation locations by default
if (is.null(xnew)) {
xnew <- object$x
}
if (is.null(Z)) {
Z <- object$Tmatrix[,!object$ind.drift]
}
if (!is.null(ynew)) {
coef.hold <- mKrig.coef(object, ynew)
c.coef <- coef.hold$c
d.coef <- coef.hold$d
}
else {
c.coef <- object$c
d.coef <- object$d
}
# fixed part of the model this a polynomial of degree m-1
# Tmatrix <- fields.mkpoly(xnew, m=object$m)
#
if (derivative == 0) {
if( drop.Z|object$nZ==0){
# just evaluate polynomial and not the Z covariate
temp1 <- fields.mkpoly(xnew, m = object$m) %*% d.coef[object$ind.drift,]}
else{
temp1<- cbind( fields.mkpoly(xnew, m = object$m),Z) %*% d.coef}
}
else {
if(!drop.Z & object$nZ>0){
stop("derivative not supported with Z covariate included")}
temp1 <- fields.derivative.poly(xnew, m = object$m, d.coef[object$ind.drift,])
}
if( just.fixed){
return(temp1)}
# add nonparametric part. Covariance basis functions
# times coefficients.
# syntax is the name of the function and then a list with
# all the arguments. This allows for different covariance functions
# that have been passed as their name.
if (derivative == 0) {
# argument list are the parameters and other options from mKrig
# locations and coefficients,
temp2 <- do.call(object$cov.function.name, c(object$args,
list(x1 = xnew, x2 = object$knots, C = c.coef), cov.args))
}
else {
temp2 <- do.call(object$cov.function.name, c(object$args,
list(x1 = xnew, x2 = object$knots, C = c.coef, derivative = derivative),
cov.args))
}
# add two parts together and coerce to vector
return((temp1 + temp2))
}