#' Summary method for class "mkinfit" #' #' Lists model equations, initial parameter values, optimised parameters with #' some uncertainty statistics, the chi2 error levels calculated according to #' FOCUS guidance (2006) as defined therein, formation fractions, DT50 values #' and optionally the data, consisting of observed, predicted and residual #' values. #' #' @param object an object of class [mkinfit]. #' @param x an object of class \code{summary.mkinfit}. #' @param data logical, indicating whether the data should be included in the #' summary. #' @param distimes logical, indicating whether DT50 and DT90 values should be #' included. #' @param alpha error level for confidence interval estimation from t #' distribution #' @param digits Number of digits to use for printing #' @param \dots optional arguments passed to methods like \code{print}. #' @importFrom stats qt pt cov2cor #' @return The summary function returns a list with components, among others #' \item{version, Rversion}{The mkin and R versions used} #' \item{date.fit, date.summary}{The dates where the fit and the summary were #' produced} #' \item{diffs}{The differential equations used in the model} #' \item{use_of_ff}{Was maximum or minimum use made of formation fractions} #' \item{bpar}{Optimised and backtransformed #' parameters} #' \item{data}{The data (see Description above).} #' \item{start}{The starting values and bounds, if applicable, for optimised #' parameters.} #' \item{fixed}{The values of fixed parameters.} #' \item{errmin }{The chi2 error levels for #' each observed variable.} #' \item{bparms.ode}{All backtransformed ODE #' parameters, for use as starting parameters for related models.} #' \item{errparms}{Error model parameters.} #' \item{ff}{The estimated formation fractions derived from the fitted #' model.} #' \item{distimes}{The DT50 and DT90 values for each observed variable.} #' \item{SFORB}{If applicable, eigenvalues and fractional eigenvector component #' g of SFORB systems in the model.} #' The print method is called for its side effect, i.e. printing the summary. #' @author Johannes Ranke #' @references FOCUS (2006) \dQuote{Guidance Document on Estimating Persistence #' and Degradation Kinetics from Environmental Fate Studies on Pesticides in #' EU Registration} Report of the FOCUS Work Group on Degradation Kinetics, #' EC Document Reference Sanco/10058/2005 version 2.0, 434 pp, #' \url{http://esdac.jrc.ec.europa.eu/projects/degradation-kinetics} #' @examples #' #' summary(mkinfit("SFO", FOCUS_2006_A, quiet = TRUE)) #' #' @export summary.mkinfit <- function(object, data = TRUE, distimes = TRUE, alpha = 0.05, ...) { param <- object$par pnames <- names(param) bpnames <- names(object$bparms.optim) epnames <- names(object$errparms) p <- length(param) mod_vars <- names(object$mkinmod$diffs) covar <- try(solve(object$hessian), silent = TRUE) covar_notrans <- try(solve(object$hessian_notrans), silent = TRUE) rdf <- object$df.residual if (!is.numeric(covar) | is.na(covar[1])) { covar <- NULL se <- lci <- uci <- rep(NA, p) } else { rownames(covar) <- colnames(covar) <- pnames se <- sqrt(diag(covar)) lci <- param + qt(alpha/2, rdf) * se uci <- param + qt(1-alpha/2, rdf) * se } beparms.optim <- c(object$bparms.optim, object$par[epnames]) if (!is.numeric(covar_notrans) | is.na(covar_notrans[1])) { covar_notrans <- NULL se_notrans <- tval <- pval <- rep(NA, p) } else { rownames(covar_notrans) <- colnames(covar_notrans) <- c(bpnames, epnames) se_notrans <- sqrt(diag(covar_notrans)) tval <- beparms.optim / se_notrans pval <- pt(abs(tval), rdf, lower.tail = FALSE) } names(se) <- pnames param <- cbind(param, se, lci, uci) dimnames(param) <- list(pnames, c("Estimate", "Std. Error", "Lower", "Upper")) bparam <- cbind(Estimate = beparms.optim, se_notrans, "t value" = tval, "Pr(>t)" = pval, Lower = NA, Upper = NA) # Transform boundaries of CI for one parameter at a time, # with the exception of sets of formation fractions (single fractions are OK). f_names_skip <- character(0) for (box in mod_vars) { # Figure out sets of fractions to skip f_names <- grep(paste("^f", box, sep = "_"), pnames, value = TRUE) n_paths <- length(f_names) if (n_paths > 1) f_names_skip <- c(f_names_skip, f_names) } for (pname in pnames) { if (!pname %in% f_names_skip) { par.lower <- param[pname, "Lower"] par.upper <- param[pname, "Upper"] names(par.lower) <- names(par.upper) <- pname bpl <- backtransform_odeparms(par.lower, object$mkinmod, object$transform_rates, object$transform_fractions) bpu <- backtransform_odeparms(par.upper, object$mkinmod, object$transform_rates, object$transform_fractions) bparam[names(bpl), "Lower"] <- bpl bparam[names(bpu), "Upper"] <- bpu } } bparam[epnames, c("Lower", "Upper")] <- param[epnames, c("Lower", "Upper")] ans <- list( version = as.character(utils::packageVersion("mkin")), Rversion = paste(R.version$major, R.version$minor, sep="."), date.fit = object$date, date.summary = date(), solution_type = object$solution_type, warnings = object$summary_warnings, use_of_ff = object$mkinmod$use_of_ff, error_model_algorithm = object$error_model_algorithm, df = c(p, rdf), covar = covar, covar_notrans = covar_notrans, err_mod = object$err_mod, niter = object$iterations, calls = object$calls, time = object$time, par = param, bpar = bparam) if (!is.null(object$version)) { ans$fit_version <- object$version ans$fit_Rversion <- object$Rversion if (ans$fit_version >= "0.9.49.6") { ans$AIC = AIC(object) ans$BIC = BIC(object) ans$logLik = logLik(object) } } ans$diffs <- object$mkinmod$diffs if(data) ans$data <- object$data ans$start <- object$start ans$start_transformed <- object$start_transformed ans$fixed <- object$fixed ans$errmin <- mkinerrmin(object, alpha = 0.05) if (object$calls > 0) { if (!is.null(ans$covar)){ Corr <- cov2cor(ans$covar) rownames(Corr) <- colnames(Corr) <- rownames(ans$par) ans$Corr <- Corr } else { warning("Could not calculate correlation; no covariance matrix") } } ans$bparms.ode <- object$bparms.ode ans$shapiro.p <- object$shapiro.p ep <- endpoints(object) if (length(ep$ff) != 0) ans$ff <- ep$ff if (distimes) ans$distimes <- ep$distimes if (length(ep$SFORB) != 0) ans$SFORB <- ep$SFORB if (!is.null(object$d_3_message)) ans$d_3_message <- object$d_3_message class(ans) <- "summary.mkinfit" return(ans) } #' @rdname summary.mkinfit #' @export print.summary.mkinfit <- function(x, digits = max(3, getOption("digits") - 3), ...) { if (is.null(x$fit_version)) { cat("mkin version: ", x$version, "\n") cat("R version: ", x$Rversion, "\n") } else { cat("mkin version used for fitting: ", x$fit_version, "\n") cat("R version used for fitting: ", x$fit_Rversion, "\n") } cat("Date of fit: ", x$date.fit, "\n") cat("Date of summary:", x$date.summary, "\n") cat("\nEquations:\n") nice_diffs <- gsub("^(d.*) =", "\\1/dt =", x[["diffs"]]) writeLines(strwrap(nice_diffs, exdent = 11)) df <- x$df rdf <- df[2] cat("\nModel predictions using solution type", x$solution_type, "\n") cat("\nFitted using", x$calls, "model solutions performed in", x$time[["elapsed"]], "s\n") if (!is.null(x$err_mod)) { cat("\nError model: ") cat(switch(x$err_mod, const = "Constant variance", obs = "Variance unique to each observed variable", tc = "Two-component variance function"), "\n") cat("\nError model algorithm:", x$error_model_algorithm, "\n") if (!is.null(x$d_3_message)) cat(x$d_3_message, "\n") } cat("\nStarting values for parameters to be optimised:\n") print(x$start) cat("\nStarting values for the transformed parameters actually optimised:\n") print(x$start_transformed) cat("\nFixed parameter values:\n") if(length(x$fixed$value) == 0) cat("None\n") else print(x$fixed) # We used to only have one warning - show this for summarising old objects if (!is.null(x[["warning"]])) cat("\n\nWarning:", x$warning, "\n\n") if (length(x$warnings) > 0) { cat("\n\nWarning(s):", "\n") cat(x$warnings, sep = "\n") } if (!is.null(x$AIC)) { cat("\nResults:\n\n") print(data.frame(AIC = x$AIC, BIC = x$BIC, logLik = x$logLik, row.names = " ")) } cat("\nOptimised, transformed parameters with symmetric confidence intervals:\n") print(signif(x$par, digits = digits)) if (x$calls > 0) { cat("\nParameter correlation:\n") if (!is.null(x$covar)){ print(x$Corr, digits = digits, ...) } else { cat("No covariance matrix") } } cat("\nBacktransformed parameters:\n") cat("Confidence intervals for internally transformed parameters are asymmetric.\n") if ((x$version) < "0.9-36") { cat("To get the usual (questionable) t-test, upgrade mkin and repeat the fit.\n") print(signif(x$bpar, digits = digits)) } else { cat("t-test (unrealistically) based on the assumption of normal distribution\n") cat("for estimators of untransformed parameters.\n") print(signif(x$bpar[, c(1, 3, 4, 5, 6)], digits = digits)) } cat("\nFOCUS Chi2 error levels in percent:\n") x$errmin$err.min <- 100 * x$errmin$err.min print(x$errmin, digits=digits,...) printSFORB <- !is.null(x$SFORB) if(printSFORB){ cat("\nEstimated Eigenvalues and DFOP g parameter of SFORB model(s):\n") print(x$SFORB, digits=digits,...) } printff <- !is.null(x$ff) if(printff){ cat("\nResulting formation fractions:\n") print(data.frame(ff = x$ff), digits=digits,...) } printdistimes <- !is.null(x$distimes) if(printdistimes){ cat("\nEstimated disappearance times:\n") print(x$distimes, digits=digits,...) } printdata <- !is.null(x$data) if (printdata){ cat("\nData:\n") print(format(x$data, digits = digits, ...), row.names = FALSE) } invisible(x) }