## Authors ## Martin Schlather, schlather@math.uni-mannheim.de ## ## ## Copyright (C) 2015 -- 2017 Martin Schlather ## ## This program is free software; you can redistribute it and/or ## modify it under the terms of the GNU General Public License ## as published by the Free Software Foundation; either version 3 ## of the License, or (at your option) any later version. ## ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software ## Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. print.RFratiotest <- function(x, ...) { if (!is.null(x$simu.ratios)) { ## MC ratio test cat("\nMonte Carlo likelihood ratio test", "\n=================================", "\nnull model:", rfConvertRMmodel2string(x$model.list$nullmodel), "\nalt. model:", rfConvertRMmodel2string(x$model.list$alternative), "\n",x$msg) # } else if (is.null(x$model1.df) && is.null(x$loglik)) { # ## wann wird diese if-Anweiung verwendet? # print.default(t(as.matrix(x))) } else { cat("\nApprox. likelihood ratio test\n=============================\n") if (is.null(x$model1.df)) { cat("null model: df=", x$df[1], " loglik=", x$loglik[1], "\n", sep="") cat("alt. model: df=", x$df[2], " loglik=", x$loglik[2], "\n", sep="") cat("p=", x$p,"\n\n") } else { len <- length(x$model1.df) for (i in 1:len) { if (len > 1) cat("Test", i, "\n") cat("Null model:", as.character(x$model1.name)[i], "\n") cat("Alt. model:", as.character(x$model2.name)[i], "\n") cat(as.character(x$txt[i])) } cat("\n") } } } mess <- function(alpha, p, df, nullmodelname=0, altmodelname=1:length(df)) { if (is.na(p)) return("NA") else p <- formatC(p, digits=4) nullmodelname <- if (is.numeric(nullmodelname)) paste("model", nullmodelname) else paste("'", nullmodelname, "'", sep="") altmodelname <- if (is.numeric(altmodelname)) paste("model", altmodelname) else paste("'", altmodelname, "'", sep="") if (missing(df)) ## simulated paste("The p-value equals", p, "and the hypothesis that the two models significantly differ at", "the level alpha=", alpha, "is", ifelse(p <= alpha, "accepted.", "rejected."), "\n") else if (missing(alpha)) paste("loglikelihood test: ", altmodelname, " against ", nullmodelname, ": p=", p, " (df=", df, ")\n", sep="") else { if (length(alpha) != 1) stop("alpha should be a scalar") paste("loglikelihood test: ", altmodelname, " against ", nullmodelname, ": p=", p, " (df=", df, ")", ifelse(p <= alpha, "Null hypothesis accepted.", "Null hypothesis rejected."), "\n", sep="") } ## paste(p, df) ## to do } approx_test <- function(modellist, alpha) { n <- length(modellist) loglik <- df <- numeric() for (m in 1:n) { if (is(modellist[[m]], "RF_fit")) { df[m] <- modellist[[m]]$number.of.parameters loglik[m] <- modellist[[m]]$ml$likelihood } else if (is(modellist[[m]], "RFfit")) { df[m] <- modellist[[m]]@number.of.parameters loglik[m] <- modellist[[m]]["ml"]@likelihood } else stop("wrong class ('", class(modellist), "') in approx_test.") } if (length(df) > 2 && (!missing(alpha) || length(alpha) > 1)) stop("'alpha' must be a scalar") p <- pchisq(diff(loglik), diff(df), lower.tail = FALSE) txt <- mess(alpha=alpha, p=p, df=diff(df)) return(invisible(list(df=df, loglik=loglik, p=p, txt=txt))) } approx_test_single <- function(model, method, alpha, modelinfo) { if (is(model) == "RF_fit") { submodels <- model$submodels df <- model$number.of.parameters loglik <- model[[method]]$likelihood report <- model$report p.proj <- model$p.proj v.proj <- model$v.proj x.proj <- model$x.proj true.tsdim <- model$true.tsdim true.vdim <- model$true.vdim AIC <- model[[method]]$AIC BIC <- model[[method]]$BIC number.of.data <- model$number.of.data if (missing(modelinfo)) modelinfo <- model$modelinfo fixed <- model$fixed fitted.model <- model[[method]]$model } else { # "RFfit" submodels <- model@submodels df <- model@number.of.parameters loglik <- model[method]@likelihood report <- model@report p.proj <- model@p.proj v.proj <- model@v.proj x.proj <- model@x.proj true.tsdim <- model@true.tsdim true.vdim <- model@true.vdim AIC <- model[method]@AIC BIC <- model[method]@BIC number.of.data <- model@number.of.data if (missing(modelinfo)) modelinfo <- model@modelinfo fixed <- NULL fitted.model <- PrepareModel2(model[method])## ok no params } if (!is.logical(x.proj) && length(x.proj) != true.tsdim) ## todo x.proj!=NULL streichen stop("space-time projection can't be evaluated yet. Please contact author.") nm <- rownames(modelinfo) proj.txt <- (if (length(p.proj) == 0) "user's model" else paste("(", paste(nm[p.proj], collapse=", "), sep="")) if (length(fixed$zero) > 0) proj.txt <- paste(proj.txt, ", ", sep="", paste(nm[fixed$zero], "=0", collapse=", ", sep="")) if (length(fixed$one) > 0) proj.txt <- paste(proj.txt, ", ", sep="", paste(nm[fixed$one], "=0", collapse=", ")) if (length(p.proj) > 0) proj.txt <- paste(proj.txt, ")", sep="") if (length(submodels) > 0) { sub.df <- sub.loglik <- sub.report <- sub.p.proj <- sub.proj.txt <- sub.fixed <- NULL for (i in 1:length(submodels)) { ret <- approx_test_single(submodels[[i]], method, alpha, modelinfo) sub.df <- c(sub.df, ret$df) sub.loglik <- c(sub.loglik, ret$loglik) sub.report <- c(sub.report, ret$report) sub.fixed <- c(if (i > 1) sub.fixed, list(ret$fixed)) sub.p.proj <- c(if (i > 1) sub.p.proj, list(ret$p.proj)) sub.v.proj <- c(if (i > 1) sub.v.proj, list(ret$v.proj)) sub.proj.txt <- c(sub.proj.txt, ret$proj.txt) sub.fitted.models <- c(if (i > 1) sub.fitted.models, ret$fitted.model) } len <- length(sub.df) i <- 1 result <- NULL result.model <- list() result.n <- 0 while(i <= len) { result.n <- result.n + 1 j <- i if (j > len) stop("Error. Please contact author") while(j <= len && sub.report[i] == sub.report[j]) { j <- j + 1; } j <- j - 1 tot.loglik <- sum(sub.loglik[i:j]) tot.df <- sum(sub.df[i:j]) tot.proj.txt <- paste(sub.proj.txt[i:j], collapse=" * ") tot.p.proj <- sub.p.proj[i:j] tot.v.proj <- sub.v.proj[i:j] tot.models <- sub.fitted.models[i:j] if (length(sub.fixed) == 0) { tot.fixed.zero <- tot.fixed.one <- NULL } else { tot.fixed.zero <- unlist(lapply(sub.fixed[i:j], function(x) x$zero)) tot.fixed.one <- unlist(lapply(sub.fixed[i:j], function(x) x$one)) } if (true.vdim != length(tot.v.proj[[1]])) { aux.model <- list("+") for (k in 1:length(tot.models)) { aux.model[[k+1]] <- list("M", M=diag(true.vdim)[, tot.v.proj[[k]], drop=FALSE], tot.models[[k]]) } result.model[[result.n]] <- aux.model } else { result.model[[result.n]] <- tot.models[[1]] if (i!=j) stop("model mismatch. Please contact author") } ## oder einfach nur AIC der submodels addieren tot.AIC <- 2 * tot.df - 2 * tot.loglik tot.BIC <- log(number.of.data) * tot.df - 2 * tot.loglik subpproj <- unlist(tot.p.proj) if (length(subpproj) == length(unique(subpproj))) { delta.df <- df - tot.df if (delta.df <= 0) stop("negative df -- please contact author") p <- pchisq(2 * (loglik - tot.loglik), df=delta.df, lower.tail = FALSE) } else { delta.df <- p <- NA } result <- rbind(result, data.frame(model1.name = tot.proj.txt, model1.loglik = tot.loglik, model1.df = tot.df, model1.AIC = tot.AIC, model1.BIC = tot.BIC, model1.zero = paste(nm[tot.fixed.zero], collapse=","), model1.one = paste(nm[tot.fixed.one], collapse=","), model2.name = proj.txt, model2.loglik=loglik, model2.df = df, model2.AIC = AIC, model2.BIC = BIC, model2.zero = paste(nm[fixed$zero], collapse=","), model2.one = paste(nm[fixed$one], collapse=","), delta.df = delta.df, p = p, txt = mess(alpha=alpha, p=p, df=delta.df, nullmodelname="Null model", altmodelname="Alt. model") ) ) i <- j + 1 } return(list(df=df, loglik=loglik, report=report, p.proj = p.proj, v.proj=v.proj, AIC=AIC, BIC=BIC, fixed = fixed, fitted.model=c(result.model, list(fitted.model)), proj.txt=proj.txt, result = result)) } else { return(list(df=df, loglik=loglik, report=report, p.proj = p.proj, v.proj=v.proj, AIC=AIC, BIC=BIC, fixed = fixed, fitted.model=list(fitted.model), proj.txt = proj.txt, result = data.frame( model1.name = "", model1.loglik = NA, model1.df = -1, model1.AIC = NA, model1.BIC = NA, model1.zero = "", model1.one = "", model2.name = proj.txt, model2.loglik=loglik, model2.df = df, model2.AIC = AIC, model2.BIC = BIC, model2.zero = if (is.null(fixed)) "" else paste(nm[fixed$zero], collapse=","), model2.one = if (is.null(fixed)) "" else paste(nm[fixed$one], collapse=","), delta.df = NA, p = NA, txt = "NA" ))) } } RFratiotest <- function(nullmodel, alternative, ## no params as output of RFfit x, y=NULL, z=NULL, T=NULL, grid=NULL, data, alpha, n = 5 / alpha, ## number of simulations to do seed = 0, lower=NULL, upper=NULL, methods, # "reml", "rml1", sub.methods, ## "internal" : name should not be changed; should always be last ## method! optim.control=NULL, users.guess=NULL, distances=NULL, dim, transform=NULL, ##type = c("Gauss", "BrownResnick", "Smith", "Schlather", ## "Poisson"), ... ) { classes <- c("RF_fit", "RFfit") RFoptOld <- internal.rfoptions(#general.modus_operandi="normal", ..., general.seed=NA) on.exit(RFoptions(LIST=RFoptOld[[1]])) RFopt <- RFoptOld[[2]] printlevel <- RFopt$basic$printlevel if (RFopt$general$modus_operandi == "neurotic") stop("crossvalidation is not a precise method") if ((!RFopt$fit$ratiotest_approx && (missing(alpha) || n < 1 / alpha)) || (!missing(alpha) && (alpha < 0 && alpha > 1)) ) stop("alpha is not given or outside [0,1] or to small") if (class(nullmodel) %in% classes) { if (!missing(alternative) && !(class(alternative) %in% classes)) stop("alternative model not of the class 'RFfit'") if (!RFopt$fit$ratiotest_approx) stop("for models of class 'RFfit' the parameter 'ratiotest_approx' must be'TRUE'") if (missing(alternative)) { ats <- approx_test_single(nullmodel, "ml", alpha)$result ats <- ats[!is.na(ats$delta.df) , c("model1.name", "model1.loglik", "model1.df", "model1.zero", "model1.one", "model2.name", "model2.loglik", "model2.df", "model2.zero", "model2.one", "delta.df", "p", "txt" ), drop=FALSE] class(ats) <- "RFratiotest" if (RFopt$general$returncall) attr(ats, "call") <- as.character(deparse(match.call())) attr(ats, "coord_system") <- c(orig=RFopt$coords$coord_system, model=RFopt$coords$new_coord_system) return(ats) } else { ats <- approx_test(list(nullmodel, alternative), alpha) class(ats) <- "RFratiotest" if (RFopt$general$returncall) attr(ats, "call") <- as.character(deparse(match.call())) attr(ats, "coord_system") <- c(orig=RFopt$coords$coord_system, model=RFopt$coords$new_coord_system) return(ats) } } else if (missing(alternative) || (class(alternative) %in% classes)) stop("alternative model is not given or not of model type") if (exists(".Random.seed")) { old.seed <- .Random.seed on.exit(.Random.seed <<- old.seed, add = TRUE) } if (!is.null(seed) && !is.na(seed)) set.seed(seed) else if (!is.na(RFopt$basic$seed)) { if (printlevel >= PL_IMPORTANT) message("NOTE: 'RFratiotest' is performed with fixed random seed ", RFopt$basic$seed, ".\nSet RFoptions(seed=NA) to make the seed arbitrary.") set.seed(RFopt$basic$seed) } nullmodel <- PrepareModel2(nullmodel, ...) ## ok no params alternative <- PrepareModel2(alternative, ...)## ok no params Z <- UnifyData(x=x, y=y, z=z, T=T, grid=grid, data=data, distances=distances, dim=dim, RFopt=RFopt) values <- try(GetValuesAtNA(NAmodel=nullmodel, valuemodel=alternative, # spatialdim=Z$spatialdim, Time=Z$has.time.comp, # shortnamelength=3, skipchecks=FALSE), silent=TRUE) remove("Z") isSubmodel <- is.numeric(values) && all(is.na(values)) if (!isSubmodel && printlevel >= PL_IMPORTANT) message("'nullmodel' cannot be automatically detected as being a nullmodel of 'alternative'") model.list <- list(nullmodel=nullmodel, alternative=alternative) data.fit <- list() guess <- users.guess for (m in 1:length(model.list)) { data.fit[[m]] <- RFfit(model.list[[m]], x=x, y=y, z=z, T=T, grid=grid, data=data, lower=lower, upper=upper, methods=methods, sub.methods=sub.methods, optim.control=optim.control, users.guess=guess, distances=distances, dim=dim, transform=transform, ..., spConform = FALSE) guess <- if (isSubmodel) data.fit[[m]]$ml$model else NULL } if (RFopt$fit$ratiotest_approx) { ats <- approx_test(data.fit) class(ats) <- "RFratiotest" if (RFopt$general$returncall) attr(ats, "call") <- as.character(deparse(match.call())) attr(ats, "coord_system") <- c(orig=RFopt$coords$coord_system, model=RFopt$coords$new_coord_system) return(ats) } model <- data.fit[[1]]$ml$model data.ratio <- -diff(sapply(data.fit, function(x) x$ml$likelihood)) stopifnot(!isSubmodel || data.ratio <= 0) # should never appear simu.n <- n - 1 ratio <- numeric(simu.n) fit <- numeric(2) Z <- UnifyData(x=x, y=y, z=z, T=T, grid=grid, data=data, distances=distances, dim=dim, RFopt=RFopt) if (length(Z$coord) > 1) stop("multisets of data cannot be considered yet") Coord <- Z$coord[[1]] dist.given <- Coord$dist.given newx <- if (!dist.given) Coord$x # lapply(Z$coord, function(x) x$x) newT <- if (!dist.given) Coord$T # lapply(Z$coord, function(x) x$T) pch <- if (RFopt$general$pch=="") "" else '@' for (i in 1:simu.n) { if (printlevel>=PL_SUBIMPORTANT) cat("\n ", i, "th simulation out of", simu.n) else cat(pch) simu <- RFsimulate(model, x=newx, T=newT, grid=grid, distances=if (dist.given) Coord, dim=dim, spC=FALSE) guess <- users.guess for (m in 1:length(model.list)) { simufit <- RFfit(model.list[[m]], x=newx, T=newT, grid=grid, data=simu, lower=lower, upper=upper, methods=methods, sub.methods=sub.methods, optim.control=optim.control, users.guess=guess, distances=if (dist.given) Coord, dim=dim, transform=transform, ..., spConform=FALSE) fit[m] <- simufit$ml$ml guess <- if (isSubmodel) simufit$ml$model else NULL } ratio[i] <- -diff(fit) stopifnot(!isSubmodel || ratio[i] <= 0)# should never appear if (printlevel > PL_SUBIMPORTANT) Print(c(data.ratio, ratio), fit, rank(c(data.ratio, ratio))[1])# } r <- rank(c(data.ratio, ratio))[1] p <- r / n msg <- paste("\nThe likehood ratio test ranks the likelihood of the data on rank", r, "among", simu.n, "simulations:", mess(alpha=alpha, p=p)) res <- list(p=p, n=n, data.ratio=data.ratio, simu.ratios=ratio, data.fit=data.fit, msg=msg, model.list=model.list) class(res) <- "RFratiotest" if (RFopt$general$returncall) attr(res, "call") <- as.character(deparse(match.call())) attr(res, "coord_system") <- c(orig=RFopt$coords$coord_system, model=RFopt$coords$new_coord_system) return(res) }