From 0a5b0fc0cb7d50a70f7657c2daa7649aff105f6e Mon Sep 17 00:00:00 2001 From: Reto Buergin Date: Sat, 8 Nov 2014 02:13:19 +0000 Subject: [PATCH] version 0.2-2 --- DESCRIPTION | 11 +- MD5 | 60 +-- NAMESPACE | 11 +- NEWS | 41 +- R/AllGeneric.R | 2 + R/fvcm.R | 37 +- R/olmm-methods.R | 88 ++-- R/olmm.R | 66 +-- R/tvcm-cv.R | 170 ++++---- R/tvcm-methods.R | 551 +++++++++++------------- R/tvcm-plot.R | 207 ++++++--- R/tvcm-utils.R | 1006 ++++++++++++++++++++++++------------------- R/tvcm.R | 274 +++++++----- R/utils.R | 2 +- data/PL.RData | Bin 0 -> 223752 bytes man/PL.Rd | 66 +++ man/fvcm.Rd | 19 +- man/olmm-gefp.Rd | 32 +- man/olmm-methods.Rd | 33 +- man/olmm.Rd | 2 +- man/poverty.Rd | 2 +- man/tvcglm.Rd | 169 ++++++++ man/tvcm-control.Rd | 136 +++--- man/tvcm-cv.Rd | 247 +++++------ man/tvcm-methods.Rd | 17 +- man/tvcm-plot.Rd | 7 +- man/tvcm.Rd | 167 +++---- man/tvcolmm.Rd | 190 ++++++++ src/init.c | 3 +- src/olmm.c | 33 +- src/tvcm.c | 43 ++ src/tvcm.h | 9 + src/utils.c | 44 +- src/utils.h | 2 +- 34 files changed, 2223 insertions(+), 1524 deletions(-) create mode 100644 data/PL.RData create mode 100644 man/PL.Rd create mode 100644 man/tvcglm.Rd create mode 100644 man/tvcolmm.Rd create mode 100644 src/tvcm.c create mode 100644 src/tvcm.h diff --git a/DESCRIPTION b/DESCRIPTION index 7f1daea..4b67f08 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -2,22 +2,23 @@ Package: vcrpart Type: Package Title: Tree-Based Varying Coefficient Regression for Generalized Linear and Ordinal Mixed Models -Version: 0.2-1 -Date: 2014-09-10 +Version: 0.2-2 +Date: 2014-10-24 Authors@R: c( person("Reto", "Buergin", role = c("aut", "cre", "cph"), email = "rbuergin@gmx.ch"), person("Gilbert", "Ritschard", role = c("ctb", "ths"), email = "gilbert.ritschard@unige.ch")) Maintainer: Reto Buergin -Description: Recursive partitioning algorithm for varying coefficient generalized linear models and ordinal 2-stage linear mixed models. Special features are coefficient-wise partitioning, non-varying coefficients and partitioning of time-varying variables in longitudinal ordinal regression. +Description: Recursive partitioning for varying coefficient generalized linear models and ordinal linear mixed models. Special features are coefficient-wise partitioning, non-varying coefficients and partitioning of time-varying variables in longitudinal regression. License: GPL (>= 2) Depends: R (>= 3.1.0), parallel, partykit Imports: stats, grid, graphics, methods, nlme, rpart, numDeriv, ucminf, zoo, sandwich, strucchange +URL: http://vcrpart.wordpress.com LazyLoad: yes NeedsCompilation: yes -Packaged: 2014-09-09 22:27:25 UTC; rbuergin +Packaged: 2014-11-07 17:28:59 UTC; reto Author: Reto Buergin [aut, cre, cph], Gilbert Ritschard [ctb, ths] Repository: CRAN -Date/Publication: 2014-09-10 00:51:30 +Date/Publication: 2014-11-08 02:13:19 diff --git a/MD5 b/MD5 index 12d888b..3e0d0b9 100644 --- a/MD5 +++ b/MD5 @@ -1,20 +1,21 @@ -1cbd34b3510d90cfb1a6848b44abb82b *DESCRIPTION -9fa1ee1a037664dca6310ec341ced692 *NAMESPACE -c9cf28f3456a5cf3c5d84783d4c6f7bb *NEWS +6d24e99c18c914344785bf01d1b715a6 *DESCRIPTION +dfae833697182c8bb7f3b3572b2413e6 *NAMESPACE +4ad832a72530f1d077cd0296dbcdecb7 *NEWS 8b40e51cf24df0eb99503a7070b84a93 *R/AAA.R -76803808590893cc4202d5c78b2b8ada *R/AllGeneric.R -ed5c8359901f28c78724ea61ac731f3b *R/fvcm.R +b70bc37834d2f3b8c94d9d1c0020c614 *R/AllGeneric.R +91d50efd23854dea04fca307a8c9ce97 *R/fvcm.R 80b84163397dcda539b20d9ab51c56e7 *R/import.R -38166df78924be2eecfa73a0df50500d *R/olmm-methods.R +0801b64fedea58af67c400f8b0e15bd2 *R/olmm-methods.R 7e4d28175b9184bf3a0992401f277072 *R/olmm-utils.R -47d508ee6eda573318986badf2bc07b1 *R/olmm.R +ecdcf046dcbb466c4cf29bc7e9efaf8a *R/olmm.R b6c73716e8ea10810b0ef419e666217c *R/otsplot.R -93374105b3b9b55f260ebe19155d9d8b *R/tvcm-cv.R -5ded623facdc6ee8b637444a5328c3d2 *R/tvcm-methods.R -535373d36b06c8ce69666be8656a398f *R/tvcm-plot.R -556205fc2616156b58239d61f6ae926f *R/tvcm-utils.R -db0d8050384fd821d8f3f31acc56b14c *R/tvcm.R -0ecd155512121e5553df3af06a0a1fc2 *R/utils.R +2af1d39305eedf8fc4128b14307e1317 *R/tvcm-cv.R +32a6d478af156ed579d5909795d5cc98 *R/tvcm-methods.R +d7de761ae5514253233d0540ddd85ec5 *R/tvcm-plot.R +3d45fc591fdde7ffc882b8add1448436 *R/tvcm-utils.R +efa265167aaa866b67a1acd78df5a859 *R/tvcm.R +759f474cb091fa4062bec1eaabfdd8de *R/utils.R +8562fccdfc9dc113b72f8eb8df66bf57 *data/PL.RData a2c9a87cf50549fd5802cbb6a88d281a *data/movie.RData 72890e1f368da6d42d9cf9414c5396e0 *data/poverty.RData ea62be4e5d10df5bc683725a7f7935f6 *data/schizo.RData @@ -23,28 +24,33 @@ ea62be4e5d10df5bc683725a7f7935f6 *data/schizo.RData cb803decf95c39567f648e1e5e0ddb45 *data/vcrpart_2.RData 3ddf5ac69dc37253946b83c5bfd5fca7 *data/vcrpart_3.RData c2c0ec5bc767a17c65b372996eb094bc *inst/CITATION +3bc5b4abcf89156967fbf807a9125ee6 *man/PL.Rd 02a3675b72ff9f699d45dab0183780b8 *man/fvcm-methods.Rd -1bd0959c572019011ab4c537f9f86919 *man/fvcm.Rd +2ad931e91f7922156d825b32e68df10a *man/fvcm.Rd edb080c605c0dacb43ae037d6199abd4 *man/movie.Rd f66f99d711ddf32948fbf9b39c63b888 *man/olmm-control.Rd -b57f0746e38cad4b16bfe592ec7c60ac *man/olmm-gefp.Rd -6ee7950a48737e3b9ef60d8ba4990989 *man/olmm-methods.Rd +e7cc5a4ab52c84577dd32c4c04219ef1 *man/olmm-gefp.Rd +c0635745a839f49a8ffaa66ffcacdb65 *man/olmm-methods.Rd e58a118afbf3ff05498f9e5bbc73e893 *man/olmm-predict.Rd c914ee5c769aacd190558f0b967f1072 *man/olmm-summary.Rd -fedfc37145b8e3764bef8589ad05496c *man/olmm.Rd +99fd9612846185c8b253368ad62640bc *man/olmm.Rd 913e87ac8d1158061b59644190ef8984 *man/otsplot.Rd -58735e7359b9794d34d5c78eb0c07aa4 *man/poverty.Rd +b5d023f2e25d796fc013667de0bf0c2b *man/poverty.Rd 198b4f1a33689dc49700c02f14e42510 *man/schizo.Rd -061e82789901fc9517c35f3d44daa35c *man/tvcm-control.Rd -2a47a463b75faddfdbaacdd763760b9e *man/tvcm-cv.Rd -33dc39a7f1d534bbd8b26a5a89b05f1e *man/tvcm-methods.Rd -20100f8ce3bbcb0ca761531b0705dee8 *man/tvcm-plot.Rd -1676e4649643b03e5b50cf0b79988d6d *man/tvcm.Rd +830a40f120259f6c1e30cce2f06a224e *man/tvcglm.Rd +fc4d871d97b9315c31c720307d4b481c *man/tvcm-control.Rd +5b0157aea2925e83c7325814864fad14 *man/tvcm-cv.Rd +c2434053aa241bce6f70d969bf385e25 *man/tvcm-methods.Rd +0fa2459d031af80f1b4dbd7d3d1da797 *man/tvcm-plot.Rd +395961700e672d7ba7f7901564e83892 *man/tvcm.Rd +8c644871c09cae7e14ca6ad204a0dee3 *man/tvcolmm.Rd 158d7c7045232f33bd35bf5c39dcedcf *man/vcrpart-demo.Rd a14ef1287c994bf09c716cc21d50517f *man/vcrpart-formula.Rd 1e0af65b11fb043bd18f7982291d076f *src/Makevars -490c20f360ddf1fa209c81bbf51ba1de *src/init.c -04dd4cbf292548702d5590e2475aec35 *src/olmm.c +e0ee3aca34161fb42f8fffa717fc6c3e *src/init.c +62783432fffd5da456a3bb3e61c2be35 *src/olmm.c 1e5f560c59e4ea73b51fa72c057166ec *src/olmm.h -e5dcd67c462566773294a1e5829ef188 *src/utils.c -5d30135987641736d67d08d3c140c86e *src/utils.h +0af72621bd0a3d74afc3a3a0220f92c4 *src/tvcm.c +e8b96835ed09462a3fef4b1f581279cd *src/tvcm.h +79abdf7fd5ae53f7c77a6db8453d3408 *src/utils.c +643b41eb70ee7ca5689f176f7d82b795 *src/utils.h diff --git a/NAMESPACE b/NAMESPACE index a1cca45..cd10bd5 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -49,7 +49,9 @@ export( predecor_control, estfun.olmm, gefp.olmm, - tvcm_control, + tvcm_control, + tvcolmm_control, + tvcglm_control, tvcm, tvcolmm, tvcglm, @@ -57,8 +59,13 @@ export( fvcolmm, fvcglm, fvcm_control, + fvcolmm_control, + fvcglm_control, folds_control) +## Exported methods for 'glm' class +S3method(fixef, glm) + ## Exported methods for 'fvcm' class S3method(fitted, fvcm) S3method(predict, fvcm) @@ -109,6 +116,7 @@ S3method(print, otsplot) ## Exported methods for 'tvcm' class S3method(coef, tvcm) S3method(coefficients, tvcm) +S3method(depth, tvcm) S3method(cvloss, tvcm) S3method(plot, cvloss.tvcm) S3method(print, cvloss.tvcm) @@ -135,3 +143,4 @@ S3method(splitpath, tvcm) S3method(print, splitpath.tvcm) S3method(summary, tvcm) S3method(weights, tvcm) +S3method(width, tvcm) diff --git a/NEWS b/NEWS index 1bbc832..af2e11d 100644 --- a/NEWS +++ b/NEWS @@ -1,9 +1,44 @@ +Changes in Version 0.2-2 + + o Added seed option to 'tvcm_control'. + + o The new implementation clearly distinguishes between the two + functions 'tvcolmm' and 'tvcolmm' with separate help files. + The general function 'tvcm' is still available. + + o Added convenience function 'tvcolmm_control' and + 'tvglm_control'. + + o Improvement for 'panel_coef': Points and lines surpassing + the boxes are now suppressed. + + o Added variable centering as default for split selection. + + o Redefinition of tuning parameters for 'tvcm'. The main + tuning parameter is now 'cp'. See the help of 'tvcm' and + 'tvcm_control' for details. + + o Added 'nimpute' argument for 'tvcm_control'. + + o Added detail section to the help page of 'tvcm_control' + + o Removed AIC table from 'print.tvcm' (AIC and BIC seem not + relevant measures for models fitted by 'tvcm'). + + o Added 'PL' data set. + + o Removed bug for numeric estimation of covariance of 'olmm' + objects. + + o Added 'depth' and 'width' methods. + + Changes in Version 0.2-1 - o First CRAN release. + o First CRAN release. - o 'tvcm' and 'fvcm' allow for multiple 'vc' terms, i.e. - coefficient-specific partitions + o 'tvcm' and 'fvcm' allow for multiple 'vc' terms, i.e. + coefficient-specific partitions o Complete revision of syntaxes, argument names and default parameters. R commands for the former version 0.1-14 are diff --git a/R/AllGeneric.R b/R/AllGeneric.R index 124bf41..7a8d8f5 100644 --- a/R/AllGeneric.R +++ b/R/AllGeneric.R @@ -35,6 +35,8 @@ cvloss <- function(object, ...) UseMethod("cvloss") extract <- function(object, ...) UseMethod("extract") +fixef.glm <- function(object, ...) coef(object) + neglogLik2 <- function(object, ...) UseMethod("neglogLik2") neglogLik2.default <- function(object, ...) diff --git a/R/fvcm.R b/R/fvcm.R index 5c710d2..f7fd477 100644 --- a/R/fvcm.R +++ b/R/fvcm.R @@ -1,7 +1,7 @@ ##' -------------------------------------------------------- # ##' Author: Reto Buergin ##' E-Mail: reto.buergin@unige.ch, rbuergin@gmx.ch -##' Date: 2014-09-07 +##' Date: 2014-10-14 ##' ##' Description: ##' Random forests and bagging for the 'tvcm' algorithm. @@ -22,6 +22,9 @@ ##' - set 'ptry', 'vtry' and 'ntry' automatically (see Hastie) ##' ##' Last modifications: +##' 2014-10-14: found bug in predict.tvcm: now the 'coefi' +##' matrices are ordered by the column names of +##' 'coef'. ##' 2014-09-07: - improvment of predict.tvcm function ##' - treated bugs for 'type = "coef"' ##' - deal with ordinal responses in cases not @@ -33,7 +36,7 @@ ##' 2014-08-05: - changed specification for folds ##' -------------------------------------------------------- # -fvcolmm <- function(..., family = cumulative(), control = fvcm_control()) { +fvcolmm <- function(..., family = cumulative(), control = fvcolmm_control()) { mc <- match.call() mc[[1L]] <- as.name("fvcm") mc$fit <- "olmm" @@ -43,7 +46,17 @@ fvcolmm <- function(..., family = cumulative(), control = fvcm_control()) { } -fvcglm <- function(..., family, control = fvcm_control()) { +fvcolmm_control <- function(maxstep = 10, folds = folds_control("subsampling", 5), + ptry = 1, ntry = 1, vtry = 5, alpha = 1.0, ...) { + + mc <- match.call() + mc[[1L]] <- as.name("fvcm_control") + mc$sctest <- TRUE + return(eval.parent(mc)) +} + + +fvcglm <- function(..., family, control = fvcglm_control()) { mc <- match.call() mc[[1L]] <- as.name("fvcm") mc$fit <- "glm" @@ -53,6 +66,15 @@ fvcglm <- function(..., family, control = fvcm_control()) { } +fvcglm_control <- function(maxstep = 10, folds = folds_control("subsampling", 5), + ptry = 1, ntry = 1, vtry = 5, mindev = 0, ...) { + + mc <- match.call() + mc[[1L]] <- as.name("fvcm_control") + return(eval.parent(mc)) +} + + fvcm <- function(..., control = fvcm_control()) { mc <- match.call() @@ -110,7 +132,7 @@ fvcm <- function(..., control = fvcm_control()) { fvcm_control <- function(maxstep = 10, folds = folds_control("subsampling", 5), ptry = 1, ntry = 1, vtry = 5, - alpha = 1.0, maxoverstep = Inf, ...) { + alpha = 1.0, mindev = 0.0, ...) { ## modify the 'papply' argument mc <- match.call() @@ -123,7 +145,7 @@ fvcm_control <- function(maxstep = 10, folds = folds_control("subsampling", 5), ## combine the parameter to a list and disble cross validation and pruning call <- list(maxstep = maxstep, folds = folds, ptry = ptry, ntry = ntry, vtry = vtry, - alpha = alpha, maxoverstep = Inf, + alpha = alpha, mindev = mindev, papply = papply, cv = FALSE, prune = FALSE) call <- appendDefArgs(call, list(...)) @@ -346,7 +368,7 @@ predict.fvcm <- function(object, newdata = NULL, coefi <- predict(object, newdata = newdata, type = "coef", ranef = FALSE, na.action = na.pass, ...) if (!is.matrix(coefi)) coefi <- matrix(coefi, nrow = nrow(newdata)) - + ## acount for skipped categories if (object$info$fit == "olmm" && ncol(coefi) < ncol(coef)) { subsiCols <- table(mf[folds[, i] > 0, yName]) > 0L @@ -366,6 +388,9 @@ predict.fvcm <- function(object, newdata = NULL, colnames(coefi) <- colnamesi } + ## order columns of coefi + coefi <- coefi[, intersect(colnames(coef), colnames(coefi)), drop = FALSE] + ## index matrix for valid entries subsi <- subs if (oob) subsi[folds[,i] > 0L, ] <- FALSE diff --git a/R/olmm-methods.R b/R/olmm-methods.R index 84b23e3..0f22c9f 100644 --- a/R/olmm-methods.R +++ b/R/olmm-methods.R @@ -1,6 +1,6 @@ ##' -------------------------------------------------------- # ##' Author: Reto Buergin, rbuergin@gmx.ch -##' Date: 2014-09-08 +##' Date: 2014-10-24 ##' ##' Description: ##' methods for olmm objects. @@ -40,6 +40,11 @@ ##' weights: Weights ##' ##' Modifications: +##' 2014-10-24: - improve simulate.olmm +##' - improved 'estfun.olmm' call in 'gefp.olmm' +##' 2014-10-23: - fix bug in predict.olmm +##' 2014-09-22: - (internal) change 'Ninpute' to 'Nimpute' in estfun.olmm +##' 2014-09-20: - use tile case in titles ##' 2014-09-08: - partial substitution of 'rep' by 'rep.int' ##' - replace 'do.call' by 'call' in 'resid.olmm' ##' 2013-03-17: changed many methods to S3 methods (as in lme4) @@ -52,6 +57,7 @@ ##' - improve update method ##' - plot methods ##' - estfun.olmm: handle equal zero random effects +##' - anova with a single model ##' -------------------------------------------------------- # anova.olmm <- function(object, ...) { @@ -203,9 +209,10 @@ estfun.olmm <- function(x, predecor = FALSE, control = predecor_control(), parm <- seq_along(x$coefficients) # internal variable if (!is.null(nuisance) & is.character(nuisance)) nuisance <- which(names(coef(x)) %in% nuisance) + nuisance <- sort(union(nuisance, which(x$restricted))) parm <- setdiff(parm, nuisance) attr <- list() # default attributes - + scores <- x$score_obs subsImp <- rep.int(FALSE, nrow(scores)) @@ -215,19 +222,19 @@ estfun.olmm <- function(x, predecor = FALSE, control = predecor_control(), if (predecor && any(Ni != Nmax)) { - Ninpute <- Nmax - Ni - subsImp <- c(rep.int(FALSE, x$dims["n"]), rep.int(TRUE, sum(Ninpute))) - sbjImp <- factor(rep.int(names(Ni), Ninpute), names(Ni)) + Nimpute <- Nmax - Ni + subsImp <- c(rep.int(FALSE, x$dims["n"]), rep.int(TRUE, sum(Nimpute))) + sbjImp <- factor(rep.int(names(Ni), Nimpute), names(Ni)) ranef <- ranef(x) ranefImp <- ranef[rownames(ranef) %in% unique(sbjImp),,drop = FALSE] ## get predictors from empirical distribution yName <- all.vars(formula(x))[1L] yLevs <- levels(x$y) - newFrame <- x$frame[rep.int(1L, sum(Ninpute)),,drop=FALSE] - newFrame[, x$subjectName] <- rep.int(names(Ninpute), Ninpute) - newX <- x$X[rep.int(1L, sum(Ninpute)),,drop=FALSE] - newW <- x$W[rep.int(1L, sum(Ninpute)),,drop=FALSE] + newFrame <- x$frame[rep.int(1L, sum(Nimpute)),,drop=FALSE] + newFrame[, x$subjectName] <- rep.int(names(Nimpute), Nimpute) + newX <- x$X[rep.int(1L, sum(Nimpute)),,drop=FALSE] + newW <- x$W[rep.int(1L, sum(Nimpute)),,drop=FALSE] ## add imputations to model x$frame <- rbind(x$frame, newFrame) @@ -239,10 +246,10 @@ estfun.olmm <- function(x, predecor = FALSE, control = predecor_control(), factor(c(as.character(x$subject), newFrame[, x$subjectName]), levels = names(Ni)) x$weights <- x$weights_sbj[as.integer(x$subject)] - x$offset <- rbind(x$offset, matrix(0.0, sum(Ninpute), x$dims["nEta"])) + x$offset <- rbind(x$offset, matrix(0.0, sum(Nimpute), x$dims["nEta"])) x$dims["n"] <- nrow(x$frame) - x$eta <- rbind(x$eta, matrix(0.0, sum(Ninpute), x$dims["nEta"])) - x$score_obs <- rbind(x$score_obs, matrix(0.0, sum(Ninpute), x$dims["nPar"])) + x$eta <- rbind(x$eta, matrix(0.0, sum(Nimpute), x$dims["nEta"])) + x$score_obs <- rbind(x$score_obs, matrix(0.0, sum(Nimpute), x$dims["nPar"])) ## simulate responses if (control$impute) { @@ -250,9 +257,10 @@ estfun.olmm <- function(x, predecor = FALSE, control = predecor_control(), if (control$verbose) cat("\n* impute scores ... ") ## set seed + if (!is.null(control$seed)) set.seed(control$seed) ## impute predictors - times <- Ninpute[x$subject[!subsImp]] + times <- Nimpute[x$subject[!subsImp]] rows <- unlist(tapply(1:sum(Ni), x$subject[!subsImp], function(x) sample(x, times[x[1L]], replace = TRUE))) x$frame[subsImp,] <- x$frame[rows,,drop=FALSE] x$X[subsImp, ] <- x$X[rows,,drop=FALSE] @@ -268,7 +276,6 @@ estfun.olmm <- function(x, predecor = FALSE, control = predecor_control(), ranef[as.integer(x$subject[subsImp])]) %*% tmatW eta <- etaFixef + etaRanef probs <- x$family$linkinv(eta) - if (!is.null(control$seed)) set.seed(control$seed) x$y[subsImp] <- # simulate responses ordered(apply(probs, 1L, function(x) sample(yLevs, 1L, prob = x)), yLevs) @@ -370,9 +377,13 @@ gefp.olmm <- function(object, scores = NULL, order.by = NULL, subset = NULL, ## extract scores (if scores is not a matrix) if (is.null(scores)) { - estfunCall <- call(name = "estfun.olmm", x = quote(object), predecor = predecor) - dotargs <- list(...)[names(formals(estfun.olmm))] - for (arg in names(dotargs)) estfunCall[arg] <- dotargs[[arg]] + estfunCall <- list(name = as.name("estfun.olmm"), + x = quote(object), + predecor = quote(predecor)) + dotargs <- list(...) + dotargs <- dotargs[intersect(names(formals(estfun.olmm)), names(dotargs))] + estfunCall[names(dotargs)] <- dotargs + mode(estfunCall) <- "call" scores <- try(eval(estfunCall)) } else if (is.function(scores)) { scores <- scores(object) @@ -464,7 +475,7 @@ gefp.olmm <- function(object, scores = NULL, order.by = NULL, subset = NULL, lim.process = "Brownian bridge", type.name = "M-fluctuation test", order.name = deparse(substitute(order.by)), - subset <- rownames(model.frame(object))[subset & subsScores], + subset = rownames(model.frame(object))[subset & subsScores], J12 = NULL) class(rval) <- "gefp" return(rval) @@ -525,7 +536,7 @@ predict.olmm <- function(object, newdata = NULL, if (type == "ranef") return(ranef(object, ...)) if (type == "prob") type <- "response" - formList <- vcrpart_formula(formula(object)) # extract formulas + formList <- vcrpart_formula(formula(object), object$family) # extract formulas offset <- list(...)$offset subset <- list(...)$subset dims <- object$dims @@ -563,13 +574,17 @@ predict.olmm <- function(object, newdata = NULL, ## fixed effects only getTerms <- function(x) attr(terms(x, keep.order = TRUE), "term.labels") terms <- lapply(formList$fe$eta, getTerms) - mfForm <- formula(paste("~", paste(unlist(terms), collapse = "+"))) + if (length(unlist(terms)) > 0L) { + mfForm <- formula(paste("~", paste(unlist(terms), collapse = "+"))) + } else { + mfForm <- ~ 1 + } } mf <- model.frame(object) Terms <- delete.response(terms(mfForm)) xlevels <- .getXlevels(attr(mf, "terms"), mf) xlevels <- xlevels[names(xlevels) %in% all.vars(Terms)] - + xlevels <- xlevels[names(xlevels) != object$subjectName] newdata <- as.data.frame(model.frame(Terms, newdata, na.action = na.action, xlev = xlevels)) @@ -607,7 +622,7 @@ predict.olmm <- function(object, newdata = NULL, rownames(W) <- rownames(newdata) ## check entered random effects - if (any(dim(ranef) != c(nlevels(subject), ncol(W)))) + if (any(dim(ranef) != c(nlevels(subject), nrow(object$ranefCholFac)))) stop("'ranef' matrix has wrong dimensions") if (any(!levels(subject) %in% rownames(ranef))) { @@ -806,6 +821,23 @@ simulate.olmm <- function(object, nsim = 1, seed = NULL, if (!exists(".Random.seed", envir = .GlobalEnv)) runif(1) RNGstate <- .Random.seed dotArgs$type <- "response" + if (is.logical(ranef) && ranef) { + if (object$subjectName %in% colnames(newdata)) { + subject <- droplevels(newdata[, object$subjectName]) + ranef <- ranef(object) + if (any(!levels(subject) %in% rownames(ranef))) { + ranef <- matrix(rnorm(nlevels(subject) * ncol(ranef)), + nrow = nlevels(subject), ncol = ncol(ranef), + dimnames = list(levels(subject), colnames(ranef))) + ranef <- ranef %*% t(object$ranefCholFac) + } else { + ranef <- ranef[rownames(ranef) %in% levels(subject),,drop = FALSE] + } + } else { + stop(paste("'newdata' must contain a column '", + object$subjectName, "'", sep = "")) + } + } pred <- predict(object, newdata = newdata, type = "prob", ranef = ranef, ...) FUN <- function(x) sample(levels(object$y), 1, prob = x) rval <- as.data.frame(replicate(nsim, apply(pred, 1L, FUN))) @@ -884,14 +916,14 @@ summary.olmm <- function(object, etalab = c("int", "char", "eta"), VarCorr <- matrix(, 0L, 3L, dimnames = list(c(), c("Variance", "StdDev", ""))) } - + ## title - methTitle <- "Ordinal linear" - if (dims["hasRanef"] > 0L) methTitle <- paste(methTitle, "mixed") - methTitle <- paste(methTitle, "model") + methTitle <- "Ordinal Linear" + if (dims["hasRanef"] > 0L) methTitle <- paste(methTitle, "Mixed") + methTitle <- paste(methTitle, "Model") if (dims["hasRanef"] > 0L) - paste(methTitle, " fit by marginal maximum\n", - "likelihood with Gauss-Hermite quadrature", sep = "") + methTitle <- paste(methTitle, " fit by Marginal Maximum\n", + "Likelihood with Gauss-Hermite Quadrature", sep = "") na.action <- naprint(attr(model.frame(object), "na.action")) na.action <- if (na.action == "") character() else paste("(", na.action, ")", sep = "") diff --git a/R/olmm.R b/R/olmm.R index e4ca180..13b502b 100644 --- a/R/olmm.R +++ b/R/olmm.R @@ -1,6 +1,6 @@ ##' -------------------------------------------------------- # ##' Author: Reto Buergin, rbuergin@gmx.ch -##' Date: 2014-09-08 +##' Date: 2014-09-25 ##' ##' References: ##' ordinal: http://cran.r-project.org/web/packages/ordinal/index.html @@ -13,6 +13,11 @@ ##' ##' ##' Modifications: +##' 2014-09-25: - removed bug for numeric estimation of covariance of +##' 'olmm' objects +##' - define 'score_sbj' and 'score_obs' slot even if +##' 'numGrad = FALSE' (otherwise olmm_update_marg gives error) +##' 2014-09-19: allow 'family' to be of class 'function' ##' 2014-09-08: partial substitution of 'rep' by 'rep.int' ##' 2014-06-17: convert routine to S3 class ##' 2014-05-03: moved several control parameters to 'control' argument @@ -21,9 +26,9 @@ ##' 2014-04-22: implement change in 'form' object ##' 2013-09-15: Free() commands were added in olmm.c ##' 2013-09-07: C implementation for updating the marginal Likelihood -##' and predicting random-effects was stabilized by -##' replacing many alloca's by the R built-in function -##' Calloc, which may slow the estimation +##' and predicting random-effects was stabilized by +##' replacing many alloca's by the R built-in function +##' Calloc, which may slow the estimation ##' 2013-07-27: change 'start' handling and add 'restricted' ##' argument ##' 2013-07-19: correct use of numGrad argument (from now the slots @@ -106,6 +111,18 @@ olmm_control <- function(fit = c("nlminb", "ucminf", "optim"), doFit = TRUE, numGrad = FALSE, numHess = numGrad, nGHQ = 7L, start = NULL, restricted = NULL, verbose = FALSE, ...) { fit <- match.arg(fit) + stopifnot(is.logical(doFit) && length(doFit) == 1) + stopifnot(is.logical(numGrad) && length(numGrad) == 1) + stopifnot(is.logical(numHess) && length(numHess) == 1) + if (!numHess & numGrad) + stop("'numHess' must be TRUE if numGrad is 'TRUE'") + stopifnot(is.numeric(nGHQ) && length(nGHQ) == 1) + if (nGHQ != round(nGHQ)) + warning(paste("'nGHQ' is set to ", nGHQ, ".", sep = "")) + nGHQ <- as.integer(round(nGHQ)) + stopifnot(is.null(start) | is.numeric(start)) + stopifnot(is.null(restricted) | is.character(restricted)) + stopifnot(is.logical(verbose) && length(verbose) == 1) rval <- append(list(fit = fit, doFit = doFit, numGrad = numGrad, @@ -137,13 +154,17 @@ olmm <- function(formula, data, family = cumulative(), stopifnot(inherits(control, "olmm_control")) ## link and family - stopifnot(class(family) == "family.olmm") + if (is.character(family)) { + family <- get(family, mode = "function", envir = parent.frame()) + } else if (is.function(family)) { + family <- family() + } + if (!inherits(family, "family.olmm")) { + print(family) + stop("'family' not recognized") + } linkNum <- switch(family$link, logit = 1L, probit = 2L, cauchy = 3L) famNum <- switch(family$family, cumulative = 1L, baseline = 2L, adjacent = 3L) - - ## numerical methods for score and hessian matrix - if (!control$numHess & control$numGrad) - stop("'numHess' must be 'TRUE' if 'numGrad' is 'TRUE'") ## evaluate contrasts con <- lapply(1:ncol(data), function(i) attr(data[, i], "contrasts")) @@ -152,14 +173,9 @@ olmm <- function(formula, data, family = cumulative(), if (missing(contrasts)) contrasts <- NULL contrasts <- appendDefArgs(contrasts, con) - ## number of Gauss-Hermite quadrature points - if (control$nGHQ != as.integer(round(control$nGHQ))) - warning(paste("'nGHQ' has been set to ", control$nGHQ, ".", sep = "")) - control$nGHQ <- as.integer(round(control$nGHQ)) - ## optimizer control option optim <- olmm_optim_setup(x = control, env = environment()) - control$numGrad <- control$numHess <- is.null(optim$gr) + ## control$numGrad <- control$numHess <- is.null(optim$gr) ## set environment env <- if (!is.null(list(...)$env)) list(...)$env else parent.frame(n = 1L) @@ -325,16 +341,11 @@ olmm <- function(formula, data, family = cumulative(), ll <- c(0.0) ## score function - if (!control$numGrad) { - score_obs <- matrix(0, dims["n"], dims["nPar"]) - rownames(score_obs) <- rownames(X) - colnames(score_obs) <- unlist(parNames) - score_sbj <- matrix(0, dims["N"], dims["nPar"], - dimnames = list(levels(subject), unlist(parNames))) - } else { - score_obs <- matrix(, 0L, 0L) - score_sbj <- matrix(, 0L, 0L) - } + score_obs <- matrix(0, dims["n"], dims["nPar"]) + rownames(score_obs) <- rownames(X) + colnames(score_obs) <- unlist(parNames) + score_sbj <- matrix(0, dims["N"], dims["nPar"], + dimnames = list(levels(subject), unlist(parNames))) score <- rep.int(0, dims["nPar"]) names(score) <- unlist(parNames) @@ -485,8 +496,9 @@ olmm <- function(formula, data, family = cumulative(), object$info[] <- # replace the info slot - hessian(func = object$optim[[2L]], x = object$coefficients, - method.args = list(r = 2, show.details = TRUE), - restricted = object$restricted) + method.args = list(func = + if (dims["numGrad"]) object$optim[[3L]] else NULL), + restricted = rep.int(FALSE, dims["nPar"])) if (dims["verb"] > 0L) cat("OK") } diff --git a/R/tvcm-cv.R b/R/tvcm-cv.R index 76b36b1..437f00f 100644 --- a/R/tvcm-cv.R +++ b/R/tvcm-cv.R @@ -47,7 +47,6 @@ ##' - cvloss: add 'direction' as new parameter ## --------------------------------------------------------- # - oobloss.tvcm <- function(object, newdata = NULL, weights = NULL, fun = NULL, ...) { @@ -190,38 +189,36 @@ tvcm_folds <- function(object, control) { } -cvloss.tvcm <- function(object, folds = folds_control(), - fun = NULL, dfpar = NULL, - direction = c("backward", "forward"), - papply = mclapply, verbose = FALSE, ...) { +cvloss.tvcm <- function(object, folds = folds_control(), ...) { mc <- match.call() + + control <- extract(object, "control") stopifnot(inherits(folds, "folds")) + control$folds <- folds - if (is.null(dfpar)) dfpar <- object$info$control$dfpar - dfsplit <- object$info$control$dfsplit + ## set the verbose (no verbose is shown when evaluating the validation sets) + object$info$control$verbose <- FALSE + + ## desactivate parallelization in single evaluations + object$info$control$papply <- "lapply" + object$info$control$papply.args <- list() - stopifnot(is.numeric(dfpar) && length(dfpar) == 1L) + ## (hidden) type of evaluation ('loss', 'forest' etc.) type <- list(...)$type if (is.null(type)) type <- "loss" + + ## (hidden) whether the original sample should be evaluated original <- list(...)$original if (is.null(original)) original <- FALSE - direction <- match.arg(direction) + + ## get folds foldsMat <- tvcm_folds(object, folds) - stopifnot(is.character(papply) | is.function(papply)) - if (is.function(papply)) { - if ("papply" %in% names(mc)) { - papply <- deparse(mc$papply) - } else { - papply <- deparse(formals(tvcm_control)$papply) - } - } - papplyArgs <- list(...)[names(list(...)) %in% names(formals(papply))] - keeploss <- list(...)$keeploss - if (is.null(keeploss)) keeploss <- object$info$control$keeploss - control <- object$info$control - control$verbose <- FALSE + ## get initial complexity penalty + cp <- control$cp + + ## weights and model frame weights <- weights(object$info$model) mf <- model.frame(object) @@ -229,11 +226,12 @@ cvloss.tvcm <- function(object, folds = folds_control(), cvFun <- function(i) { + ## the return value of 'cvFun' cv <- vector(mode = "list", length = switch(type, loss = 2L, forest = 3L)) - if (verbose) { - if (papply == "lapply" && i > 1L) cat("\n") - if (papply != "lapply") cat("[", i, "]") else cat("* fold", i, "...") + if (control$verbose) { + if (control$papply == "lapply" && i > 1L) cat("\n") + if (control$papply != "lapply") cat("[", i, "]") else cat("* fold", i, "...") } if (i > 0L) { @@ -250,8 +248,8 @@ cvloss.tvcm <- function(object, folds = folds_control(), ibWeights <- weights[ibSubs] oobWeights <- weights[oobSubs] } - object$info$control <- control } else { + object$info$control <- control ibSubs <- NULL ibWeights <- NULL } @@ -271,20 +269,19 @@ cvloss.tvcm <- function(object, folds = folds_control(), run <- 1L while (run > 0L) { - ibTree <- try(prune(ibTree, dfsplit, dfpar, direction, - papply = "lapply", keeploss = keeploss), TRUE) + ibTree <- try(prune(tree = ibTree, cp = cp), TRUE) if (!inherits(ibTree, "try-error")) { ## save the out-of-bag loss and the current tuning parameter oobLoss <- oobloss(ibTree, newdata = mf[oobSubs,,drop = FALSE], - weights = oobWeights, fun = fun) + weights = oobWeights, fun = control$ooblossfun) cv[[1L]] <- - cbind(cv[[1L]], c(dfsplit)) + cbind(cv[[1L]], cp) cv[[2L]] <- - cbind(cv[[2L]], c(control$lossfun(ibTree), oobLoss)) + cbind(cv[[2L]], oobLoss) ## set a new and stronger tuning parameter tab <- ibTree$info$prunepath[[length(ibTree$info$prunepath)]]$tab - if (nrow(tab) > 1L) dfsplit <- min(tab$dfsplit[-1L]) + if (nrow(tab) > 1L) cp <- min(tab[, "dev"][-1L]) if (nrow(tab) == 1L) run <- 0L } else { @@ -295,7 +292,7 @@ cvloss.tvcm <- function(object, folds = folds_control(), } } else if (type == "forest") { - if (verbose && papply == "lapply") cat("...") + if (control$verbose && control$papply == "lapply") cat("...") ibTreePr <- ibTree cv[[1]] <- ibTree$info$node cv[[2]] <- coef(extract(ibTree, "model")) @@ -307,8 +304,8 @@ cvloss.tvcm <- function(object, folds = folds_control(), cv <- NULL } - if (verbose) { - if (papply != "lapply") { + if (control$verbose) { + if (control$papply != "lapply") { if (is.null(cv)) cat("failed") } else { if (is.null(cv)) cat("failed") else cat(" OK") @@ -318,10 +315,10 @@ cvloss.tvcm <- function(object, folds = folds_control(), return(cv) } - call <- list(name = as.name(papply), - X = quote(seq(ifelse(original, 0, 1), ncol(foldsMat))), + call <- list(name = as.name(control$papply), + X = quote(seq(ifelse(original, 0L, 1L), ncol(foldsMat))), FUN = quote(cvFun)) - call[names(papplyArgs)] <- papplyArgs + call[names(control$papply.args)] <- control$papply.args mode(call) <- "call" cv <- eval(call) @@ -330,7 +327,9 @@ cvloss.tvcm <- function(object, folds = folds_control(), ## extract tree on all data if (original) { tree <- cv[[1L]] + tree$info$control <- control cv <- cv[2:length(cv)] + if (is.null(tree)) stop("partitioning failed.") } else { tree <- NULL } @@ -358,38 +357,17 @@ cvloss.tvcm <- function(object, folds = folds_control(), ## compute results grid <- sort(unique(c(unlist(lapply(cv, function(x) x[[1]][1, ]))))) rval <- list(grid = grid) - - ## column names - if (length(grid) > 1L) { - cn <- c(paste("<=", round(grid[2L], 2)), paste(">", round(grid[-1L], 2))) - } else { - cn <- "0" - } ## make a matrix with the 'loss' for each fold - ## at each dfsplit in 'grid' + ## at each cp in 'grid' cv <- lapply(cv, function(x) { x[[2]][is.nan(x[[2]]) | is.infinite(x[[2]])] <- NA; return(x) }) - ## ib-loss - ibLoss <- t(sapply(cv, getVals, grid = grid, rowsG = 1L, rowsL = 1L)) - if (length(grid) == 1L) ibLoss <- t(ibLoss) - - if (attr(foldsMat, "value") == "weights") { - ibWeights <- foldsMat - } else { - ibWeights <- matrix(rep(weights, ncol(foldsMat)), ncol = ncol(foldsMat)) - ibWeights[foldsMat <= 0] <- 0 - } - ibLoss <- ibLoss / colSums(ibWeights)[!fails] - rownames(ibLoss) <- paste("fold", 1L:length(cv)) - colnames(ibLoss) <- cn - - ## oob-loss - oobLoss <- t(sapply(cv, getVals, grid = grid, rowsG = 1L, rowsL = 2L)) + ## oob-loss (computes the average loss) + oobLoss <- t(sapply(cv, getVals, grid = grid, rowsG = 1L, rowsL = 1L)) if (length(grid) == 1L) oobLoss <- t(oobLoss) oobWeights <- matrix(rep(weights, ncol(foldsMat)), ncol = ncol(foldsMat)) @@ -400,23 +378,27 @@ cvloss.tvcm <- function(object, folds = folds_control(), } oobLoss <- oobLoss / colSums(oobWeights)[!fails] rownames(oobLoss) <- paste("fold", 1L:length(cv)) - colnames(oobLoss) <- cn + colnames(oobLoss) <- if (length(grid) > 1L) { + cn <- c(paste("<=", round(grid[2L], 2)), paste(">", round(grid[-1L], 2))) + } else { + cn <- "0" + } - rval <- append(rval, list(ibloss = ibLoss, oobloss = oobLoss)) + rval <- append(rval, list(oobloss = oobLoss)) meanLoss <- colMeans(rval$oobloss, na.rm = TRUE) - ## dfsplit with minimal loss + ## cp with minimal loss minSubs <- which(meanLoss == min(meanLoss)) if (length(minSubs) > 1L) minSubs <- max(minSubs) - rval$dfsplit.hat <- max(0, mean(c(rval$grid, Inf)[minSubs:(minSubs + 1)])) + rval$cp.hat <- max(0, mean(c(rval$grid, Inf)[minSubs:(minSubs + 1)])) rval$foldsMat <- foldsMat class(rval) <- "cvloss.tvcm" } - rval$direction <- direction - rval$call <- deparseCall(getCall(object)) - + rval$call <- getCall(object) + environment(rval$call) <- NULL + if (original) { tree$info$cv <- rval rval <- tree @@ -437,36 +419,35 @@ cvloss.tvcm <- function(object, folds = folds_control(), rval$folds <- foldsMat } - if (verbose) cat("\n") + if (control$verbose) cat("\n") return(rval) } print.cvloss.tvcm <- function(x, ...) { - cat(ifelse(x$direction == "backward", "Backwards", "Forward"), - "cross-validated average loss", "\n\n") - cat("Call: ", x$call, "\n\n") + cat("Cross-Validated Loss", "\n\n") + cat("Call: ", deparseCall(x$call), "\n\n") rval <- colMeans(x$oobloss, na.rm = TRUE) if (length(rval) > 10L) rval <- rval[seq(1L, length(rval), length.out = 10)] print(rval) cat("\n") - cat("dfsplit with minimal loss:", format(x$dfsplit.hat, digits = 3), "\n") + cat("cp with minimal oob-loss:", format(x$cp.hat, digits = 3), "\n") return(invisible(x)) } plot.cvloss.tvcm <- function(x, legend = TRUE, details = TRUE, ...) { - xlab <- "dfsplit" - ylab <- "average loss(dfsplit)" + xlab <- "cp" + ylab <- "average loss(cp)" type <- "s" lpos <- "topleft" - lsubs <- if (details) 1L:3L else 1L - if (x$dfsplit.hat < Inf) lsubs <- c(lsubs, 4L) - lcol <- c("black", "grey80", "black", "black") - llty <- c(1, 1, 3, 2) + lsubs <- if (details) 1L:2L else 1L + if (x$cp.hat < Inf) lsubs <- c(lsubs, 4L) + lcol <- c("black", "grey80", "black") + llty <- c(1, 1, 2) dotList <- list(...) defArgs <- list(type = type, xlab = xlab, ylab = ylab, @@ -477,14 +458,15 @@ plot.cvloss.tvcm <- function(x, legend = TRUE, details = TRUE, ...) { yy2 <- t(cbind(x$oobloss, x$oobloss[,ncol(x$oobloss)])) yy1 <- rowMeans(yy2, na.rm = TRUE) - yy3 <- rowMeans(t(cbind(x$ibloss, x$ibloss[,ncol(x$ibloss)])), na.rm = TRUE) - defArgs$ylim <- range(if (details) c(yy2, yy3) else yy1, na.rm = TRUE) + defArgs$ylim <- range(if (details) yy2 else yy1, na.rm = TRUE) ## set plot arguments call <- list(name = as.name("plot"), x = quote(xx[, 1]), y = quote(yy1)) call <- appendDefArgs(call, list(...)) call <- appendDefArgs(call, defArgs) + type <- call$type + call$type <- "n" llty[1L] <- call$lty lcol[1L] <- call$col mode(call) <- "call" @@ -493,24 +475,26 @@ plot.cvloss.tvcm <- function(x, legend = TRUE, details = TRUE, ...) { eval(call) ## plot details - if (details) { + if (details) matplot(x = xx, yy2, type = type, lty = llty[2L], col = lcol[2L], add = TRUE) - points(x = xx[,1], yy3, type = type, lty = llty[3L], col = lcol[3L]) - } + + ## plot average oob-loss as last + call$name <- as.name("points") + call$type <- type + eval(call) if (legend) { - ltext <- c("average oob estimate", - "foldwise oob estimates", - "in-sample estimate", - "dfsplit with minimal oob-loss") + ltext <- c("average oob-loss", + "foldwise oob-loss", + "cp with minimal oob-loss") legend(lpos, ltext[lsubs], col = lcol[lsubs], lty = llty[lsubs]) } - if (x$dfsplit.hat < Inf) { - subsMin <- max(which(x$grid <= x$dfsplit.hat)) + if (x$cp.hat < Inf) { + subsMin <- max(which(x$grid <= x$cp.hat)) minLoss <- colMeans(x$oobloss, na.rm = TRUE)[subsMin] - segments(x$dfsplit.hat, par()$usr[3], x$dfsplit.hat, minLoss, lty = 2) - axis(1, x$dfsplit.hat, format(x$dfsplit.hat, digits = 3), + segments(x$cp.hat, par()$usr[3], x$cp.hat, minLoss, lty = 2) + axis(1, x$cp.hat, format(x$cp.hat, digits = 3), line = 1, tick = FALSE) } } diff --git a/R/tvcm-methods.R b/R/tvcm-methods.R index 2546801..79eef14 100644 --- a/R/tvcm-methods.R +++ b/R/tvcm-methods.R @@ -1,7 +1,7 @@ ##' -------------------------------------------------------- # ##' Author: Reto Buergin +##' Date: 2014-10-18 ##' E-Mail: reto.buergin@unige.ch, rbuergin@gmx.ch -##' Date: 2014-09-08 ##' ##' Description: ##' S3 methods for tvcm objects @@ -12,6 +12,7 @@ ##' ##' Methods: ##' coef, coefficients: +##' depth: depth of trees ##' extract: ##' fitted: ##' formula: @@ -27,12 +28,21 @@ ##' resid, residuals: extract residuals ##' splitpath: show information of the splitting procedure ##' weights: extract the weights -##' +##' width: width of trees +##' ##' Modifications: +##' 2014-10-18: added 'depth' and 'width' methods +##' 2014-10-14: adapt print.splitpath to new dev-grid structure +##' 2014-10-03: add option 'cv' to 'extract.tvcm' +##' 2014-09-17: prune.tvcm: +##' - 'keepdev' argument in 'prune.tvcm' dropped (to complicated +##' to explain) +##' - add 'control' argument +##' 2014-09-15: 'tab', 'ntab' and 'otab' are not matrices ##' 2014-09-02: added 'prunepath.tvcm' and 'print.prunepath.tvcm' functions ##' 2014-09-02: various modifications in 'prune.tvcm' for accelerations: ##' - 'do.call' was replaced by 'eval' -##' - new option 'keeploss' (reuses information of the previous +##' - new option 'keepdev' (reuses information of the previous ##' pruning step) ##' - new option 'papply' (even though the accrelation is not ##' as efficient as expected) @@ -46,14 +56,20 @@ coef.tvcm <- function(object, ...) tvcm_get_estimates(object, ...) coefficients.tvcm <- coef.tvcm +depth.tvcm <- function(x, root = FALSE, ...) { + rval <- sapply(x$info$node, depth, root = root) + names(rval) <- LETTERS[seq_along(rval)] + return(rval) +} + + extract.tvcm <- function(object, what = c("control", "model", "nodes", "sctest", "p.value", - "lossgrid", "selected", + "devgrid", "cv", "selected", "coef", "sd", "var"), steps = NULL, ...) { what <- match.arg(what) - splitpath <- object$info$splitpath if (length(splitpath) > 0 && is.null(steps)) steps <- seq(1L, object$info$nstep) @@ -69,10 +85,17 @@ extract.tvcm <- function(object, what = c("control", "model", rval <- lapply(splitpath[steps], function(x) x$sctest) return(rval) - } else if (what == "lossgrid" && !is.null(splitpath)) { + } else if (what == "devgrid" && !is.null(splitpath)) { - rval <- lapply(splitpath[steps], function(x) x$lossgrid) + rval <- lapply(splitpath[steps], function(x) x$grid) return(rval) + + } else if (what == "cv") { + if (is.null(object$info$cv)) { + warning("no information on cross-validation") + } else { + return(object$info$cv) + } } else if (what == "model") { @@ -83,13 +106,13 @@ extract.tvcm <- function(object, what = c("control", "model", rval <- lapply(object$info$node, function(node) { - if (depth(node) > 0L) { - ids <- setdiff(nodeids(node), nodeids(node, terminal = TRUE)) - rval <- unlist(nodeapply(node, ids, function(node) node$split$varid)) - if (length(rval) > 0L) rval <- unique(colnames(object$data)[rval]) - } else { - rval <- NULL - } + if (depth(node) > 0L) { + ids <- setdiff(nodeids(node), nodeids(node, terminal = TRUE)) + rval <- unlist(nodeapply(node, ids, function(node) node$split$varid)) + if (length(rval) > 0L) rval <- unique(colnames(object$data)[rval]) + } else { + rval <- NULL + } return(rval) }) return(rval) @@ -263,19 +286,9 @@ tvcm_print <- function(x, type = c("print", "summary"), cat(paste(" Tests: alpha = ", format(x$info$control$alpha, ...), if (x$info$control$bonferroni) ", nodewise Bonferroni corrected\n", sep = "")) - - if (type == "summary") { - cat("\nGoodness of fit:\n") - lLik <- logLik(x) - AICtab <- data.frame(AIC = AIC(lLik), - BIC = BIC(lLik), - logLik = as.vector(lLik), - row.names = "") - print(AICtab, ...) - } if (length(coef$re) > 0L) { - cat("\nRandom effects:\n") + cat("\nRandom Effects:\n") VarCorr <- VarCorr(extract(x, "model")) if (x$info$fit == "olmm") { VarCorr <- olmm_rename(VarCorr, yLevs, x$info$family, etalab) @@ -286,7 +299,7 @@ tvcm_print <- function(x, type = c("print", "summary"), } if (length(coef$fe) > 0L) { - cat("\nFixed effects:\n") + cat("\nFixed Effects:\n") if (type == "print") { coefMat <- matrix(coef$fe, 1) colnames(coefMat) <- names(coef$fe) @@ -326,10 +339,11 @@ tvcm_print <- function(x, type = c("print", "summary"), class(terminal_panel) <- "grapcon_generator" - vcLabs <- tvcm_print_vclabs(x) + vcLabs <- tvcm_print_vclabs(x$info$formula) for (pid in seq_along(coef$vc)) { - cat(paste("\nVarying coefficient: ", vcLabs[pid], "\n", sep = "")) + cat(paste("\nVarying Coefficient ", LETTERS[pid], + ": ", vcLabs[pid], "\n", sep = "")) x$node <- x$info$node[[pid]] print.party(x, terminal_panel = terminal_panel, tp_args = list(partid = pid)) @@ -355,32 +369,13 @@ print.tvcm <- function(x, ...) tvcm_print(x, type = "print", ...) -prune.tvcm <- function(tree, dfsplit = NULL, dfpar = NULL, - direction = c("backward", "forward"), - alpha = NULL, maxstep = NULL, terminal = NULL, - papply = mclapply, keeploss = FALSE, original = FALSE, - ...) { +prune.tvcm <- function(tree, cp = NULL, alpha = NULL, maxstep = NULL, + terminal = NULL, original = FALSE, ...) { mc <- match.call() - - ## checking arguments - direction <- match.arg(direction) - if (is.null(dfpar)) dfpar <- tree$info$control$dfpar - stopifnot(is.numeric(dfpar) && length(dfpar) == 1L) - stopifnot(is.character(papply) | is.function(papply)) - if (is.function(papply)) { - if ("papply" %in% names(mc)) { - papply <- deparse(mc$papply) - } else { - papply <- deparse(formals(prune.tvcm)$papply) - } - } - papplyArgs <- list(...)[names(list(...)) %in% names(formals(papply))] - stopifnot((is.logical(keeploss) | is.numeric(keeploss)) && - length(keeploss) == 1L) - keeploss <- as.numeric(keeploss) - - tunepar <- c(!is.null(dfsplit), + + ## checking arguments + tunepar <- c(!is.null(cp), !is.null(alpha), !is.null(maxstep), !is.null(terminal)) @@ -388,10 +383,10 @@ prune.tvcm <- function(tree, dfsplit = NULL, dfpar = NULL, if (sum(tunepar) < 1L) stop("no tuning parameter specified.") if (sum(tunepar) > 1L) stop("only one tuning parameter is allowed.") - tunepar <- c("dfsplit", "alpha", "maxstep", "terminal")[tunepar] + tunepar <- c("cp", "alpha", "maxstep", "terminal")[tunepar] - if (tunepar == "dfsplit") { - stopifnot(is.numeric(dfsplit) && length(dfsplit) == 1L) + if (tunepar == "cp") { + stopifnot(is.numeric(cp) && length(cp) == 1L) } else if (tunepar == "alpha") { if (!tree$info$control$sctest) { @@ -426,252 +421,209 @@ prune.tvcm <- function(tree, dfsplit = NULL, dfpar = NULL, refit <- function(tree) { - ## extract new formula - env <- environment() - formList <- tree$info$formula - vcRoot <- sapply(tree$info$node, width) == 1L - ff <- tvcm_formula(formList, vcRoot, tree$info$family, env) - - ## overwrite node predictors - data <- model.frame(tree) - data[, paste("Node", LETTERS[seq_along(tree$info$node)], sep = "")] <- - tvcm_get_node(tree, tree$data, TRUE, tree$fitted[,"(weights)"], formList) - - ## refit model - call <- list(name = as.name(tree$info$fit), - formula = quote(ff$full), - data = quote(data), - family = quote(tree$info$family), - weights = tree$fitted[,"(weights)"]) - call[names(tree$info$dotargs)] <- tree$info$dotargs - mode(call) <- "call" - tree$info$model <- suppressWarnings(try(eval(call), TRUE)) - - ## check if the refitting failed - if (inherits(tree$info$model, "try-error")) - stop("tree model fitting failed.") - - ## heavy terms - if (inherits(tree$info$model, "glm")) { - attr(attr(tree$info$model$model, "terms"), ".Environment") <- NULL - environment(tree$info$model$formula) <- NULL - attr(tree$info$model$terms, ".Environment") <- NULL - } - - ## update control - tree$info$control <- - tvcm_grow_setcontrol(tree$info$control, tree$info$model, formList, vcRoot) - - return(tree) + ## extract new formula + env <- environment() + formList <- tree$info$formula + vcRoot <- sapply(tree$info$node, width) == 1L + ff <- tvcm_formula(formList, vcRoot, tree$info$family, env) + + ## overwrite node predictors + data <- model.frame(tree) + data[, paste("Node", LETTERS[seq_along(tree$info$node)], sep = "")] <- + tvcm_get_node(tree, tree$data, TRUE, tree$fitted[,"(weights)"], formList) + + ## refit model + call <- list(name = as.name(tree$info$fit), + formula = quote(ff$full), + data = quote(data), + family = quote(tree$info$family), + weights = tree$fitted[,"(weights)"]) + call[names(tree$info$dotargs)] <- tree$info$dotargs + mode(call) <- "call" + tree$info$model <- suppressWarnings(try(eval(call), TRUE)) + + ## check if the refitting failed + if (inherits(tree$info$model, "try-error")) + stop("tree model fitting failed.") + + ## heavy terms + if (inherits(tree$info$model, "glm")) { + attr(attr(tree$info$model$model, "terms"), ".Environment") <- NULL + environment(tree$info$model$formula) <- NULL + attr(tree$info$model$terms, ".Environment") <- NULL + } + + ## update control + tree$info$control <- + tvcm_grow_setcontrol(tree$info$control, tree$info$model, formList, vcRoot) + + return(tree) } - + ## get the original tree if (original && !identical(tree$info$node, tree$info$grownode)) { - tree$node <- tree$info$grownode[[1L]] - tree$info$node <- tree$info$grownode - tree <- refit(tree) + tree$node <- tree$info$grownode[[1L]] + tree$info$node <- tree$info$grownode + tree <- refit(tree) } if (tunepar %in% c("alpha", "maxstep", "terminal")) { - - ## prune the tree structure - node <- tvcm_prune_node(tree, alpha, maxstep, terminal) - ## refit the model if something has changed - if (!identical(node, tree$info$node)) { + ## prune the tree structure + node <- tvcm_prune_node(tree, alpha, maxstep, terminal) + + ## refit the model if something has changed + if (!identical(node, tree$info$node)) { - ## attach nodes - tree$node <- node[[1L]] - tree$info$node <- node - - ## refit the model - tree <- refit(tree) - } + ## attach nodes + tree$node <- node[[1L]] + tree$info$node <- node - } else if (tunepar == "dfsplit") { + ## refit the model + tree <- refit(tree) + } + + } else if (tunepar == "cp") { + + ## prune as long as there remain 'weak' links + run <- 1L; step <- 0L; + cols <- c("part", "node", "loss", "npar", "nsplit", "dev") + evalcols <- c("loss", "npar", "nsplit") + prunepath <- list() - if (direction == "backward") { + while (run > 0) { - ## prune as long as there remain 'weak' links - run <- 1L; step <- 0L; - cols <- c("loss", "npar", "nspl") - prunepath <- list() - keeplosscount <- 0 + run <- 0 ; step <- step + 1L; + ncollapse <- NULL - while (run > 0) { - - run <- 0 ; step <- step + 1L; - ncollapse <- NULL - - ids <- lapply(tree$info$node, function(node) { - setdiff(nodeids(node), nodeids(node, terminal = TRUE)) - }) - - ids00 <- lapply(seq_along(tree$info$node), - function(pid) { - unlist(nodeapply(tree$info$node[[pid]], ids[[pid]], - function(node) node$info$id$original)) - }) - - ## 'call' to evaluate the collapses - prStatCall <- list(name = as.name(papply), X = quote(subs), - FUN = quote(prStat)) - prStatCall[names(papplyArgs)] <- papplyArgs - mode(prStatCall) <- "call" + ids <- lapply(tree$info$node, function(node) { + setdiff(nodeids(node), nodeids(node, terminal = TRUE)) + }) + + ids00 <- lapply(seq_along(tree$info$node), + function(pid) { + unlist(nodeapply(tree$info$node[[pid]], ids[[pid]], + function(node) node$info$id$original)) + }) + + ## 'call' to evaluate the collapses + prStatCall <- list(name = as.name(tree$info$control$papply), + X = quote(subs), + FUN = quote(prStat)) + prStatCall[names(tree$info$control$papply.args)] <- + tree$info$control$papply.args + mode(prStatCall) <- "call" + + ntab <- matrix(, length(unlist(ids)), length(cols)) + colnames(ntab) <- cols + ntab[, "part"] <- rep(seq_along(tree$info$node), sapply(ids, length)) + ntab[, "node"] <- unlist(ids) + ntab[, "loss"] <- rep.int(Inf, length(unlist(ids))) + + if (nrow(ntab) > 0L) { - ntab <- data.frame( - part = rep(seq_along(tree$info$node), sapply(ids, length)), - node = unlist(ids), - loss = rep.int(Inf, length(unlist(ids))), - npar = rep.int(NA, length(unlist(ids))), - nspl = rep.int(NA, length(unlist(ids))), - dfsplit = rep.int(NA, length(unlist(ids)))) - - if (nrow(ntab) > 0L) { + if (step > 1L) { - if (step > 1L) { - - ## search for the parents of the previously selecte node - spart <- otab$part[ocollapse] - snode <- otab$node[ocollapse] - root <- 1L - npath <- c(root); opath <- c(root); - while (root != snode) { - nkids <- unlist(nodeapply(tree$info$node[[spart]], root, - function(node) - sapply(node$kids, function(kids) kids$id))) - okids <- unlist(nodeapply(tree$info$node[[spart]], nkids, - function(node) node$info$id$last)) - kidkids <- nodeapply(tree$info$node[[spart]], nkids, nodeids) - kid <- sapply(kidkids, function(x) snode %in% x) - root <- nkids[kid] - if (root != snode) { - npath <- c(npath, nkids[kid]) - opath <- c(opath, okids[kid]) - } + ## search for the parents of the previously selecte node + spart <- otab[ocollapse, "part"] + snode <- otab[ocollapse, "node"] + root <- 1L + npath <- c(root); opath <- c(root); + while (root != snode) { + nkids <- + unlist(nodeapply(tree$info$node[[spart]], root, + function(node) + sapply(node$kids, function(kids) kids$id))) + okids <- unlist(nodeapply(tree$info$node[[spart]], nkids, + function(node) node$info$id$last)) + kidkids <- nodeapply(tree$info$node[[spart]], nkids, nodeids) + kid <- sapply(kidkids, function(x) snode %in% x) + root <- nkids[kid] + if (root != snode) { + npath <- c(npath, nkids[kid]) + opath <- c(opath, okids[kid]) } - - ## insert results of parent models - ntab[ntab$node %in% npath & ntab$part == spart, cols] <- - otab[otab$node %in% opath & otab$part == spart, cols] - - - if (keeplosscount < keeploss) { - - ## approximate the remaining ids from the selected partition - otab$loss <- otab$loss + (otab$loss[ocollapse] - loss0) - otab$npar <- otab$npar - (npar0 - otab$npar[ocollapse]) - otab$nspl <- otab$nspl - (nspl0 - otab$nspl[ocollapse]) - nids <- nodeids(tree$info$node[[spart]]) - oids <- unlist(nodeapply(tree$info$node[[spart]], - nodeids(tree$info$node[[spart]]), - function(node) node$info$id$last)) - subs <- ntab$node %in% nids & - ntab$part %in% spart & is.infinite(ntab$loss) - subs <- nids %in% ntab$node[subs] - nids <- nids[subs] - oids <- oids[subs] - ntab[ntab$node %in% nids & ntab$part == spart, cols] <- - otab[otab$node %in% oids & otab$part == spart, cols] - keeplosscount <- keeplosscount + 1 - - } else keeplosscount <- 0 } - subs <- which(is.infinite(ntab$loss)) - if (length(subs) > 0L) { - - ## reestimate all models which collapse each on inner node - prStat <- function(i) { + ## insert results of parent models + ntab[ntab[, "node"] %in% npath & ntab[, "part"] == spart, evalcols] <- + otab[otab[, "node"] %in% opath & otab[, "part"] == spart, evalcols] + } + } + + loss0 <- tree$info$control$lossfun(tree) + npar0 <- extractAIC(tree)[1L] + nsplit0 <- sum(sapply(tree$info$node, width) - 1L) + + subs <- which(is.infinite(ntab[, "loss"])) + if (length(subs) > 0L) { + + ## re-estimate all models which collapse each on inner node + prStat <- function(i) { + term <- lapply(seq_along(tree$info$node), + function(pid) { + if (pid == ntab[i, "part"]) return(ntab[i, "node"]) + return(NULL) + }) + prTree <- try(prune(tree, terminal = term, papply = lapply), TRUE) + if (!inherits(prTree, "try-error")) + return(c(prTree$info$control$lossfun(prTree), + extractAIC(prTree$info$model)[1L], + sum(sapply(prTree$info$node, width) - 1L))) + return(c(Inf, NA, NA)) + } + stat <- eval(prStatCall) + ntab[subs, evalcols] <- t(sapply(stat, function(x) x)) + } + + if (nrow(ntab) > 0L) { + if (any(ntab[, "loss"] < Inf)) { + + ## minimum dfsplit such that the smaller model improves the fit + ntab[, "dev"] <- + (ntab[, "loss"] - loss0) / + (tvcm_complexity(npar0, tree$info$control$dfpar, + nsplit0, tree$info$control$dfsplit) - + tvcm_complexity(ntab[, "npar"], tree$info$control$dfpar, + ntab[, "nsplit"], tree$info$control$dfsplit)) + + ## prune selected inner node + if (any(ntab[, "dev"] <= cp)) { + ncollapse <- which(!is.na(ntab[, "dev"]) & + !is.nan(ntab[, "dev"]) & + ntab[, "dev"] == min(ntab[, "dev"])) + if (length(ncollapse) > 1L) ncollapse <- sample(ncollapse, 1L) + if (length(ncollapse) > 0L) { term <- lapply(seq_along(tree$info$node), function(pid) { - if (pid == ntab$part[i]) return(ntab$node[i]) + if (pid == ntab[ncollapse, "part"]) + return(ntab[ncollapse, "node"]) return(NULL) }) - prTree <- try(prune(tree, terminal = term, papply = lapply), TRUE) - if (!inherits(prTree, "try-error")) - return(c(prTree$info$control$lossfun(prTree), - extractAIC(prTree$info$model)[1L], - sum(sapply(prTree$info$node, width) - 1L))) - return(c(Inf, NA, NA)) + tree <- prune(tree, terminal = term) + run <- 1 } - stat <- eval(prStatCall) - ntab[subs, cols] <- t(sapply(stat, function(x) x)) } + otab <- ntab + ocollapse <- ncollapse - if (any(ntab$loss < Inf)) { - - ## minimum dfsplit such that the smaller model improves the fit - loss0 <- tree$info$control$lossfun(tree) - npar0 <- extractAIC(tree)[1L] - nspl0 <- sum(sapply(tree$info$node, width) - 1L) - ntab$dfsplit <- (ntab$loss + dfpar * ntab$npar - loss0 - dfpar * npar0) / - (nspl0 - ntab$nspl) - - ## prune selected inner node - if (any(ntab$dfsplit <= dfsplit)) { - ncollapse <- which(!is.na(ntab$dfsplit) & - !is.nan(ntab$dfsplit) & - ntab$dfsplit == min(ntab$dfsplit)) - if (length(ncollapse) > 1L) ncollapse <- sample(ncollapse, 1L) - if (length(ncollapse) > 0L) { - term <- lapply(seq_along(tree$info$node), - function(pid) { - if (pid == ntab$part[ncollapse]) - return(ntab$node[ncollapse]) - return(NULL) - }) - tree <- prune(tree, terminal = term, keeploss = TRUE) - run <- 1 - } - } - otab <- ntab - ocollapse <- ncollapse - - } else { - stop("fitting of nested models failed.") - } - } - - ## create a 'data.frame' for the 'prunepath' method - tab <- data.frame( - part = NA, - node = NA, - loss = tree$info$control$lossfun(tree), - npar = extractAIC(tree)[1L], - nspl = sum(sapply(tree$info$node, width) - 1L), - dfsplit = NA, - row.names = "") - if (nrow(ntab) > 0L) { - ntab$node <- unlist(ids00) - rownames(ntab) <- seq_along(rownames(ntab)) - tab <- rbind(tab, ntab) + } else { + stop("fitting of nested models failed.") } - prunepath[[step]] <- list(step = step, tab = tab) } - } - tree$info$prunepath <- prunepath - - } else if (direction == "forward") { - - ## table with information from the latest step - tab <- data.frame( - step = seq(0, tree$info$nstep, 1L), - loss = sapply(tree$info$splitpath, function(x) x$loss), - npar = sapply(tree$info$splitpath, function(x) x$npar), - nspl = sapply(tree$info$splitpath, function(x) x$nspl)) - - ## maximum dfsplit such that the larger model improves the fit - tab$dfsplit <- c(-(diff(tab$loss) + dfpar * diff(tab$npar)) / diff(tab$nspl), 0) - subs <- tab$dfsplit > dfsplit - - if (any(subs)) { - maxstep <- max(tab$step[subs]) - tree <- prune(tree, maxstep = maxstep) - tab <- tab[subs, ] + ## create a 'data.frame' for the 'prunepath' method + + tab <- matrix(c(NA, NA, loss0, npar0, nsplit0, NA), + ncol = length(cols), dimnames = list("", cols)) + if (nrow(ntab) > 0L) { + ntab[, "node"] <- unlist(ids00) + rownames(ntab) <- seq(1L, nrow(ntab), length.out = nrow(ntab)) + tab <- rbind(tab, ntab) + } + prunepath[[step]] <- list(step = step, tab = tab) } - tree$info$prunepath <- tab + tree$info$prunepath <- prunepath } ## return pruned model @@ -681,17 +633,22 @@ prune.tvcm <- function(tree, dfsplit = NULL, dfpar = NULL, prunepath.tvcm <- function(tree, steps = 1L, ...) { rval <- tree$info$prunepath[steps] + rval <- lapply(rval, function(x) { + x$tab <- as.data.frame(x$tab) + x$tab$part <- LETTERS[x$tab$part] + return(x) + }) class(rval) <- "prunepath.tvcm" return(rval) } -print.prunepath.tvcm <- function(x, ...) { +print.prunepath.tvcm <- function(x, na.print = "", ...) { for (i in seq_along(x)) { if (!is.null(x[[i]])) { if (i != 1L) cat("\n") cat("Step:", x[[i]]$step, "\n") - print(x[[i]]$tab, ...) + print(as.matrix(x[[i]]$tab), na.print = na.print, quote = FALSE, ...) } } } @@ -720,7 +677,7 @@ splitpath.tvcm <- function(tree, steps = 1L, if (!details) { for (i in seq_along(steps)) { rval[[i]]$sctest <- NULL - rval[[i]]$lossgrid <- NULL + rval[[i]]$grid <- NULL } } class(rval) <- "splitpath.tvcm" @@ -735,33 +692,36 @@ print.splitpath.tvcm <- function(x, ...) { if (is.null(unlist(x[[i]]$varid))) { cat(" (no splitting processed)\n") } else { + cat("\n\nSelected Split:") cat("\nPartition:", LETTERS[x[[i]]$partid]) - cat("\nSplitting variable:", x[[i]]$var) cat("\nNode:", x[[i]]$node) + cat("\nVariable:", x[[i]]$var) cat("\nCutpoint: ") cat(paste("{",paste(x[[i]]$cutpoint, collapse = "}, {"), "}", sep = "")) - cat("\nPenalized loss reduction:", format(x[[i]]$plossred, ...), "\n") } - if (!is.null(x[[i]]$sctest) | !is.null(x[[i]]$lossgrid)) - cat("\nDetails:\n") if (!is.null(x[[i]]$sctest)) { - cat("\nCoefficient constancy tests (p-value):\n") + cat("\nCoefficient Constancy Tests (p-value):\n") for (pid in seq_along(x[[i]]$sctest)) { cat(paste("\nPartition ", LETTERS[pid], ":\n", sep = "")) print(as.data.frame(x[[i]]$sctest[[pid]]), ...) } - } - if (!is.null(x[[i]]$lossgrid)) { - cat("\nLoss-minimizing grid search (loss reduction):\n") - for (pid in seq_along(x[[i]]$lossgrid)) { - for (nid in seq_along(x[[i]]$lossgrid[[pid]])) { - for (vid in seq_along(x[[i]]$lossgrid[[pid]][[nid]])) { - if (length(x[[i]]$lossgrid[[pid]][[nid]][[vid]]) > 0L) { - cat("\nPartition:", names(x[[i]]$lossgrid)[pid], - "Node:", sub("Node", "",names(x[[i]]$lossgrid[[pid]])[nid]), - "Variable:", names(x[[i]]$lossgrid[[pid]][[nid]])[vid], "\n") - print(as.data.frame(x[[i]]$lossgrid[[pid]][[nid]][[vid]]), ...) + } else cat("\n") + + if (!is.null(x[[i]]$grid)) { + cat("\nLoss Reduction Statistics:") + for (pid in seq_along(x[[i]]$grid)) { + for (nid in seq_along(x[[i]]$grid[[pid]])) { + for (vid in seq_along(x[[i]]$grid[[pid]][[nid]])) { + if (is.null(x[[i]]$sctest) | !is.null(x[[i]]$sctest) && + any(x[[i]]$grid[[pid]][[nid]][[vid]][[2L]] > -Inf)) { + cat("\nPartition:", LETTERS[pid], + "Node:", sub("Node", "",names(x[[i]]$grid[[pid]])[nid]), + "Variable:", names(x[[i]]$grid[[pid]][[nid]])[vid], "\n") + print(as.data.frame( + cbind(x[[i]]$grid[[pid]][[nid]][[vid]][[1L]], + "dev" = x[[i]]$grid[[pid]][[nid]][[vid]][[2L]], + "npar" = x[[i]]$grid[[pid]][[nid]][[vid]][[3L]])), ...) } } } @@ -774,3 +734,10 @@ print.splitpath.tvcm <- function(x, ...) { weights.tvcm <- function(object, ...) { weights(extract(object, "model")) } + + +width.tvcm <- function(x, ...) { + rval <- sapply(x$info$node, width) + names(rval) <- LETTERS[seq_along(rval)] + return(rval) +} diff --git a/R/tvcm-plot.R b/R/tvcm-plot.R index 8c3db09..50408a2 100644 --- a/R/tvcm-plot.R +++ b/R/tvcm-plot.R @@ -33,14 +33,13 @@ plot.tvcm <- function(x, type = c("default", "coef", "simple", "partdep", "cv"), - main = NULL, part = NULL, + main, part = NULL, drop_terminal = TRUE, tnex = 1, newpage = TRUE, ask = NULL, pop = TRUE, gp = gpar(), ...) { ## checks type <- match.arg(type) - stopifnot(is.null(main) | is.character(main)) stopifnot(is.logical(drop_terminal) && length(drop_terminal) == 1L) stopifnot(is.numeric(tnex) && length(tnex) == 1L) stopifnot(is.logical(newpage) && length(newpage) == 1L) @@ -49,13 +48,15 @@ plot.tvcm <- function(x, type = c("default", "coef", if (type == "partdep") { - call <- list(as.name("panel_partdep"), object = quote(x), ask = ask, main = main) - call <- append(call, list(...)) - mode(call) <- "call" - eval(call) + if (missing(main)) main <- NULL + call <- list(as.name("panel_partdep"), object = quote(x), ask = ask, main = main) + call <- append(call, list(...)) + mode(call) <- "call" + eval(call) } else if (type == "cv") { + if (missing(main)) main <- NULL if (is.null(x$info$cv)) { warning("no information on cross validation.") } else { @@ -65,29 +66,29 @@ plot.tvcm <- function(x, type = c("default", "coef", } else { ## tree plots - if (is.null(part)) part <- seq_along(x$info$node) if (is.character(part)) { - levs <- LETTERS[seq_along(x$info$node)] - if (any(!part %in% levs)) stop("unknown 'part'.") - part <- which(part %in% levs) + part <- which(LETTERS[seq_along(x$info$node)] %in% part) } else if (is.numeric(part)) { part <- as.integer(part) } else { stop("'part' must be a 'character' or a 'integer'.") } + if (length(part) < 1L) stop("no valid 'part' specified.") ## whether an input is expected before plotting the next tree if (is.null(ask)) ask <- ifelse(length(part) == 1L, FALSE, TRUE) ## repeat the title - if (is.null(main)) { - main <- tvcm_print_vclabs(x) - main <- main[part] + if (missing(main)) { + main <- tvcm_print_vclabs(x$info$formula)[part] + } else if (is.character(main)){ + main <- rep(main, length.out = length(part)) } else { - main <- rep(main, length.out = length(part)) + main <- NULL } + ## terminal panel tp_fun <- switch(type, @@ -99,8 +100,11 @@ plot.tvcm <- function(x, type = c("default", "coef", }, "coef" = panel_coef, "simple" = panel_empty) - tp_args <- + tp_args <- if ("tp_args" %in% names(list(...))) { + list(...)$tp_args + } else { list(...)[names(list(...)) %in% names(formals(tp_fun))[-1]] + } tp_args$part <- part tp_args$gp <- gp @@ -110,17 +114,30 @@ plot.tvcm <- function(x, type = c("default", "coef", "default" = node_inner, "coef" = node_inner, "simple" = node_inner) - ip_args <- + ip_args <- if ("ip_args" %in% names(list(...))) { + list(...)$ip_args + } else { list(...)[names(list(...)) %in% names(formals(ip_fun))[-1]] + } + + ## edge panel + ep_fun <- edge_default + ep_args <- if ("ep_args" %in% names(list(...))) { + list(...)$ep_args + } else { + list(...)[names(list(...)) %in% names(formals(ep_fun))[-1]] + } ## other arguments dotargs <- list(...)[names(list(...)) %in% names(formals(plot.party))[-1]] - + dotargs <- dotargs[setdiff(names(dotargs), c("tp_args", "ip_args", "ep_args"))] + ## prepare call call <- list(name = as.name("plot.party"), x = quote(x), terminal_panel = quote(tp_fun), tp_args = quote(tp_args), inner_panel = quote(ip_fun), ip_args = quote(ip_args), + edge_panel = quote(ep_fun), ep_args = quote(ep_args), drop_terminal = quote(drop_terminal), tnex = quote(tnex), newpage = quote(newpage), main = quote(main[pid]), pop = pop, gp = gp) @@ -271,7 +288,7 @@ panel_get_main <- function(object, node, id, nobs) { if (id) rval <- paste(rval, paste(names(object)[id_node(node)], sep = "")) if (id && nobs) rval <- paste(rval, ": ", sep = "") - if (nobs) rval <- paste(rval, "nobs = ", node$info$dims["n"], sep = "") + if (nobs) rval <- paste(rval, "n = ", node$info$dims["n"], sep = "") return(rval) } @@ -310,10 +327,12 @@ panel_coef <- function(object, parm = NULL, rval <- function(node) { pushViewport(viewport()) grid.text("(no split)") - upViewport() + upViewport(2L) } return(rval) } + + ## extract subset of coefficients if (is.null(parm)) parm <- colnames(coef) if (!is.list(parm)) parm <- list(parm) if (is.numeric(unlist(parm))) { @@ -401,6 +420,7 @@ panel_coef <- function(object, parm = NULL, } ## population mean + meanCoef <- NULL if (mean) { mean_gp <- argsToList(mean_gp) @@ -413,10 +433,6 @@ panel_coef <- function(object, parm = NULL, sum(weights(object)) meanCoef <- colSums(coef * matrix(w, nrow(coef), ncol(coef))) meanCoef <- lapply(parm, function(trms) meanCoef[trms, drop = FALSE]) - if (conf.int) { - meanSd <- sqrt(colSums(sd^2 * matrix(w, nrow(coef), ncol(coef))^2)) - meanSd <- lapply(parm, function(trms) sqrt(meanSd[trms, drop = FALSE])) - } } qN <- qnorm(0.975) @@ -459,63 +475,79 @@ panel_coef <- function(object, parm = NULL, unit(plot_gp[[i]]$xlim[2], "native"), unit(0, "native"), gp = gpar(col = "black")) - if (conf.int) { - - grid.segments(unit(1:ncol(coefList[[i]]), "native"), - unit(coefList[[i]][as.character(id_node(node)),] - - qN * sdList[[i]][as.character(id_node(node)),], "native"), - unit(1:ncol(coefList[[i]]), "native"), - unit(coefList[[i]][as.character(id_node(node)),] + - qN * sdList[[i]][as.character(id_node(node)),], "native"), - arrow = arrow(angle = conf.int_gp[[i]]$angle, - length = conf.int_gp[[i]]$length, - ends = conf.int_gp[[i]]$ends, - type = conf.int_gp[[i]]$type), - gp = plot_gp[[i]]$gp) + subs <- coefList[[i]][as.character(id_node(node)),] >= plot_gp[[i]]$ylim[1L] & + coefList[[i]][as.character(id_node(node)),]<= plot_gp[[i]]$ylim[2L] + + subsMean <- NULL + if (mean) { + subsMean <- meanCoef[[i]] > plot_gp[[i]]$ylim[1L] & + meanCoef[[i]] > plot_gp[[i]]$ylim[1L] + } + + ## option 'conf.int = TRUE' + if (conf.int) { + + ## crop the lines + nCoef <- length(coefList[[i]][as.character(id_node(node)),]) + endCi <- rep(conf.int_gp[[i]]$ends, length.out = nCoef) + lenCi <- rep(conf.int_gp[[i]]$length, length.out = nCoef) + lwr <- coefList[[i]][as.character(id_node(node)),] - + qN * sdList[[i]][as.character(id_node(node)),] + endCi[lwr < plot_gp[[i]]$ylim[1L]] <- "last" + lwr[lwr < plot_gp[[i]]$ylim[1L]] <- plot_gp[[i]]$ylim[1L] + lwr[lwr > plot_gp[[i]]$ylim[2L]] <- NA + upr <- coefList[[i]][as.character(id_node(node)),] + + qN * sdList[[i]][as.character(id_node(node)),] + endCi[upr > plot_gp[[i]]$ylim[2L] & endCi == "last"] <- "none" + endCi[upr > plot_gp[[i]]$ylim[2L] & endCi == "both"] <- "first" + upr[upr > plot_gp[[i]]$ylim[2L]] <- plot_gp[[i]]$ylim[2] + upr[upr < plot_gp[[i]]$ylim[1L]] <- NA + subsCi <- !is.na(lwr) & !is.na(upr) + lenCi[endCi == "none"] <- 0 + endCi[endCi == "none"] <- "both" - if (FALSE) { - - grid.segments(unit(1:ncol(coefList[[i]]), "native"), - unit(meanCoef[[i]] - qN * meanSd[[i]], "native"), - unit(1:ncol(coefList[[i]]), "native"), - unit(meanCoef[[i]] + qN * meanSd[[i]], "native"), - arrow = arrow(angle = conf.int_gp[[1]]$angle, - length = conf.int_gp[[i]]$length, - ends = conf.int_gp[[i]]$ends, - type = conf.int_gp[[i]]$type), - gp = mean_gp[[i]]$gp) - - } + ## plot + if (any(subsCi)) + grid.segments(unit(which(subsCi), "native"), + unit(lwr[subsCi], "native"), + unit(which(subsCi), "native"), + unit(upr[subsCi], "native"), + arrow = arrow(angle = conf.int_gp[[i]]$angle, + length = lenCi, + ends = endCi, + type = conf.int_gp[[i]]$type), + gp = plot_gp[[i]]$gp) } + ## option 'type = "p"' if (plot_gp[[i]]$type %in% c("p", "b")) { - if (mean) { - grid.points(unit(1:ncol(coefList[[i]]), "native"), - unit(meanCoef[[i]], "native"), + if (mean && any(subsMean)) + grid.points(unit(which(subsMean), "native"), + unit(meanCoef[[i]][subsMean], "native"), pch = mean_gp[[i]]$pch, gp = mean_gp[[i]]$gp) - } - grid.points(unit(1:ncol(coefList[[i]]), "native"), - unit(coefList[[i]][as.character(id_node(node)),], - "native"), - pch = plot_gp[[i]]$pch, gp = plot_gp[[i]]$gp) + if (any(subs)) + grid.points(unit(which(subs), "native"), + unit(coefList[[i]][as.character(id_node(node)),][subs], + "native"), + pch = plot_gp[[i]]$pch, gp = plot_gp[[i]]$gp) } - + ## option 'type = "l"' if (plot_gp[[i]]$type %in% c("l", "b")) { - - if (mean) { - grid.lines(unit(1:ncol(coefList[[i]]), "native"), - unit(meanCoef[[i]], "native"), - gp = mean_gp[[i]]$gp) - } - grid.lines(unit(1:ncol(coefList[[i]]), "native"), - unit(coefList[[i]][as.character(id_node(node)),], - "native"), - gp = plot_gp[[i]]$gp) + if (mean && any(subsMean)) + grid.lines(unit(which(subsMean), "native"), + unit(meanCoef[[i]][subsMean], "native"), + gp = mean_gp[[i]]$gp) + + if (any(subs)) + grid.lines(unit(which(subs), "native"), + unit(coefList[[i]][as.character(id_node(node)),][subs], + "native"), + gp = plot_gp[[i]]$gp) } if (id_node(node) == min(nodeids(object, terminal = TRUE))) { @@ -558,3 +590,42 @@ panel_empty <- function(object, part = 1L, id = TRUE, nobs = TRUE, ...) { return(rval) } class(panel_empty) <- "grapcon_generator" + +edge_default <- function(obj, digits = 3, abbreviate = FALSE, + justmin = Inf, + just = c("alternate", "increasing", "decreasing", "equal")) { + meta <- obj$data + + justfun <- function(i, split) { + myjust <- if(mean(nchar(split)) > justmin) { + match.arg(just, c("alternate", "increasing", "decreasing", "equal")) + } else { + "equal" + } + k <- length(split) + rval <- switch(myjust, + "equal" = rep.int(0, k), + "alternate" = rep(c(0.5, -0.5), length.out = k), + "increasing" = seq(from = -k/2, to = k/2, by = 1), + "decreasing" = seq(from = k/2, to = -k/2, by = -1) + ) + unit(0.5, "npc") + unit(rval[i], "lines") + } + + ## panel function for simple edge labelling + function(node, i) { + split <- character_split(split_node(node), meta, digits = digits)$levels + y <- justfun(i, split) + split <- split[i] + ## try() because the following won't work for split = "< 10 Euro", for example. + if(any(grep(">", split) > 0) | any(grep("<", split) > 0)) { + tr <- suppressWarnings(try(parse(text = paste("phantom(0)", split)), + silent = TRUE)) + if(!inherits(tr, "try-error")) split <- tr + } + grid.rect(y = y, gp = gpar(fill = "white", col = 0), + width = unit(1, "strwidth", split)) + grid.text(split, y = y, just = "center") + } +} +class(edge_default) <- "grapcon_generator" diff --git a/R/tvcm-utils.R b/R/tvcm-utils.R index e005c01..4650893 100644 --- a/R/tvcm-utils.R +++ b/R/tvcm-utils.R @@ -1,7 +1,7 @@ ##' -------------------------------------------------------- # ##' Author: Reto Buergin ##' E-Mail: reto.buergin@unige.ch, rbuergin@gmx.ch -##' Date: 2014-09-08 +##' Date: 2014-10-14 ##' ##' Description: ##' Workhorse functions for the 'tvcm' function @@ -9,17 +9,20 @@ ##' Overview: ##' ##' Workhorse functions for partitioning: +##' tvcm_complexity: computes the complexity of the model ##' tvcm_grow: main function for growing the trees ##' tvcm_grow_fit: fits the current model ##' tvcm_grow_update: refits the model (with utility function ##' 'glm.doNotFit') +##' tvcm_getNumSplits get cutpoints of numeric variables +##' tvcm_getOrdSplits get cutpoints of ordinal variables +##' tvcm_getNomSplits get cutpoints of nominal variables ##' tvcm_grow_setsplits: get current splits -##' tvcm_setsplits_validcats: validate categorical cuts ##' tvcm_setsplits_sctest: update splits after tests ##' tvcm_setsplits_splitnode: ##' tvcm_setsplits_rselect: randomly select partitions, variables and nodes ##' tvcm_grow_sctest: run coefficient constancy tests -##' tvcm_grow_gridsearch: grid based loss minimization +##' tvcm_grow_gridsearch: compute the dev statistics ##' tvcm_grow_splitnode: split in variable x. ##' tvcm_formula: extract separate formulas for ##' model and partitioning from @@ -46,6 +49,33 @@ ##' tvcm_grow_splitpath: creates a 'splitpath.tvcm' object ##' ##' Last modifications: +##' 2014-11-05: parallelized 'estfun.olmm' call in 'tvcm_grow_sctest' +##' 2014-10-14: - modify dev-grid structure obtained from 'tvcm_grow_gridsearch' +##' each combination of part/node/var has now a list of three +##' elements where the first contains the cuts, the second the +##' the loss reduction and the third the difference in the number +##' of parameters. Modifications concerned: +##' - tvcm_grow_setsplits +##' - tvcm_setsplits_sctest +##' - tvcm_setsplits_rselect +##' - print.splitpath +##' - tvcm_setsplits_splitnode allocates for the splitted +##' node a list structure (before it was a empty list +##' on the node level) +##' - small modifications in getSplits function +##' - deleted 'tvcm_setsplits_validcats' +##' - added 'tvcm_getNumSplits', 'tvcm_getOrdSplits' and +##' 'tvcm_getNomSplits' +##' 2014-09-22: deleted unnecessary 'subs' object in 'tvcm_grow_gridsearch' +##' which I didn't remove when removing the 'keepdev' +##' option +##' 2014-09-17: - delete 'keepdev' argument (also for prune.tvcm) +##' - add function 'tvcm_complexity' +##' 2014-09-15: changed 'dev' labels to 'dev' etc. +##' 2014-09-10: - add 'control' argument for 'tvcm_grow_update' +##' to allow the control of variable centering +##' - add variable centering in 'tvcm_grow_update' +##' (which was not implemented for some curious reasons) ##' 2014-09-08: substitute 'rep' function by 'rep.int' or 'rep_len' ##' 2014-09-07: - added 'tvcm_get_vcparm' function ##' - set default values in 'glm.doNotFit' @@ -66,7 +96,7 @@ ##' multiple vc() terms with equal 'by' arguments ##' are present ##' 2014-08-08: correct bug in 'tvcm_grow_setsplits' regarding -##' 'keeploss' +##' 'keepdev' ##' 2014-08-08: add suppressWarnings in tvcm_grow_fit ##' 2014-07-22: the list of splits is now of structure ##' partitions-nodes-variables @@ -85,7 +115,27 @@ ##' 2013-12-02: remove 'tvcm_grow_setupnode' ##' 2013-11-01: modify 'restricted' and 'terms' correctly in ##' 'tvcm_modify_modargs' +##' +##' Bottleneck functions: +##' - tvcm_grow_sctest +##' - tvcm_grow_setsplits +##' - tvcm_grow_gridsearch +##' -------------------------------------------------------- # + ##' -------------------------------------------------------- # +##' Compute the complexity of the model. +##' +##' @param npar the number of coefficients +##' @param dfpar the degree of freedom per parameter +##' @param nsplit the number of splits +##' @param dfsplit the degree of freedom per split +##' +##' @return a scalar giving the complexity of the model +##' -------------------------------------------------------- # + +tvcm_complexity <- function(npar, dfpar, nsplit, dfsplit) + return(dfsplit * nsplit + dfpar * npar) + ##' -------------------------------------------------------- # ##' \code{\link{tvcm_grow_fit}} fits the current node model. @@ -132,24 +182,33 @@ tvcm_grow <- function(object, subset = NULL, weights = NULL) { as.integer(sapply(x, function(x) which(colnames(partData) == x))) }) ## set the root node - nodes <- replicate(nPart, partynode(id = 1L, info = list(dims = nobs(model), depth = 0L))) + nodes <- + replicate(nPart, partynode(id = 1L, info = list(dims = nobs(model), depth = 0L))) names(nodes) <- names(formList$vc) where <- vector("list", length = nPart) partid <- seq(1, nPart, length.out = nPart) spart <- 0 # pseudo value - splits <- vector("list", length = nPart) - + + ## allocate 'splits' + splits <- lapply(seq_along(partid), function(pid) { + lapply(1L, function(nid, pid) { + lapply(seq_along(varid[[pid]]), function(x) { + vector("list", 3) + }) + }, pid = pid) + }) + + ## allocate 'splitpath' splitpath <- list() run <- 1L step <- 0L - noverstep <- 0L while (run > 0L) { step <- step + 1L; nstep <- step; - test <- NULL; loss <- NULL; + test <- NULL; dev <- NULL; ## get current partitions and add them to the model data for (pid in seq_along(nodes)) { @@ -186,7 +245,7 @@ tvcm_grow <- function(object, subset = NULL, weights = NULL) { } ## compute / update splits - splits <- tvcm_grow_setsplits(splits, spart, partid, nodeid, varid, model, + splits <- tvcm_grow_setsplits(splits, partid, nodeid, varid, model, nodes, where, partData, control) ## check if there is at least one admissible split @@ -221,9 +280,10 @@ tvcm_grow <- function(object, subset = NULL, weights = NULL) { if (inherits(test, "try-error")) { run <- 0L stopinfo <- test - + } else { - testAdj <- tvcm_sctest_bonf(test,ifelse(control$bonferroni,"nodewise", "none")) + testAdj <- + tvcm_sctest_bonf(test,ifelse(control$bonferroni,"nodewise", "none")) run <- 1L * (min(c(1.0 + .Machine$double.eps, unlist(testAdj)), na.rm = TRUE) <= control$alpha) } @@ -234,9 +294,10 @@ tvcm_grow <- function(object, subset = NULL, weights = NULL) { testAdjPart <- tvcm_sctest_bonf(test,ifelse(control$bonferroni,"partitionwise","none")) minpval <- min(unlist(testAdjPart), na.rm = TRUE) - spart <- which(sapply(testAdjPart, function(x)any(sapply(x,identical,minpval)))) + spart <- + which(sapply(testAdjPart, function(x)any(sapply(x,identical,minpval)))) if (length(spart) > 1L) spart <- sample(spart, 1L) - + ## select variable and node minsubs <- which(sapply(test[[spart]], identical, min(test[[spart]], na.rm = TRUE))) @@ -246,22 +307,22 @@ tvcm_grow <- function(object, subset = NULL, weights = NULL) { ## print results if (control$verbose) { - + ## tests cat("\nCoefficient constancy tests (p-value):\n") for (pid in seq_along(nodes)) { - cat(paste("\nPartition ", LETTERS[pid], ":\n", sep = "")) - print(data.frame(format(testAdj[[pid]], digits = 2L))) - } + cat(paste("\nPartition ", LETTERS[pid], ":\n", sep = "")) + print(data.frame(format(testAdj[[pid]], digits = 2L))) + } ## selections - cat("\nSplitting partition:", names(nodes)[spart]) - cat("\nSplitting variable:", names(partData)[varid[[spart]][svar]]) + cat("\nPartition:", LETTERS[spart]) cat("\nNode:", levels(where[[spart]])[snode]) + cat("\nVariable:", names(partData)[varid[[spart]][svar]], "\n") } - ## set deviance statistic of not to selected nodes to 'Inf' to avoid + ## set dev statistic of not to selected nodes to 'Inf' to avoid ## model evaluations splits <- tvcm_setsplits_sctest(splits, partid, spart, nodeid, snode, varid, svar) @@ -273,100 +334,96 @@ tvcm_grow <- function(object, subset = NULL, weights = NULL) { if (run > 0L) { - ## ------------------------------------------------- # - ## Step 3: search a cutpoint - ## ------------------------------------------------- # - - ## compute the loss of all candidate splits and extract the best split - loss <- try(tvcm_grow_gridsearch(splits, partid, nodeid, varid, - model, nodes, where, partData, - control, mcall, formList, step), silent = TRUE) + ## ------------------------------------------------- # + ## Step 3: search a cutpoint + ## ------------------------------------------------- # - ## handling stops - if (inherits(loss, "try-error")) { - run <- 0L - stopinfo <- loss - nstep <- step - 1L - - } else { - splits <- loss$lossgrid - spart <- loss$partid + ## compute the dev of all candidate splits and extract the best split + dev <- try(tvcm_grow_gridsearch(splits, partid, nodeid, varid, + model, nodes, where, partData, + control, mcall, formList, step), + silent = TRUE) - if (is.null(loss$cut)) { - run <- 0L - stopinfo <- "no split that decreases the loss found" - nstep <- step - 1L - } - - if (run > 0L) { - noverstep <- if (loss$plossred < control$dfsplit) noverstep + 1L else 0L - if (noverstep > control$maxoverstep) { + ## handling stops + if (inherits(dev, "try-error")) { run <- 0L - if (control$maxoverstep > 0L) { - stopinfo <- "'maxoverstep' reached" - } else { - stopinfo <- "minimal penalized loss-reduction reached" + stopinfo <- dev + nstep <- step - 1L + + } else { + splits <- dev$grid + spart <- dev$partid + + if (is.null(dev$cut)) { + run <- 0L + stopinfo <- "found no admissible split" + nstep <- step - 1L } - nstep <- nstep - 1L - } - } - } + if (run > 0L) { + if (dev$pendev < control$mindev) { + run <- 0 + stopinfo <- paste("no split with", + if (control$cp > 0) "penalized", + "loss reduction > mindev") + nstep <- nstep - 1L + } + } + } } ## incorporate the split into 'nodes' if (run > 0L) - nodes <- tvcm_grow_splitnode(nodes, where, loss, partData, - step, weights) + nodes <- tvcm_grow_splitnode(nodes, where, dev, partData, + step, weights) if (run > 0L) - splits <- tvcm_setsplits_splitnode(splits, loss$partid, loss$nodeid, - nodeid, loss, model, control) + splits <- tvcm_setsplits_splitnode(splits, dev$partid, dev$nodeid, nodeid) ## update 'splitpath' to make the splitting process traceable if (run >= 0L) splitpath[[step]] <- list(step = step, - loss = control$lossfun(model), + dev = control$lossfun(model), npar = extractAIC(model)[1L], - nspl = step - 1L) + nsplit = step - 1L) if (!inherits(test, "try-error")) splitpath[[step]]$sctest <- test - if (!inherits(loss, "try-error")) { - if (run > 0L) { - splitpath[[step]]$partid <- loss$partid - splitpath[[step]]$nodeid <- loss$nodeid - splitpath[[step]]$varid <- loss$varid - splitpath[[step]]$cutid <- loss$cutid - splitpath[[step]]$plossred <- loss$plossred - } - splitpath[[step]]$lossgrid <- loss$lossgrid - } + if (!inherits(dev, "try-error") && run > 0L) + splitpath[[step]] <- append(splitpath[[step]], dev) ## print the split if (control$verbose) { if (run > 0L) { if (!control$sctest) { - cat("\n\nSplitting partition:", names(nodes)[loss$partid]) - cat("\nNode:", levels(where[[loss$partid]])[loss$nodeid]) - cat("\nVariable:", names(partData)[loss$varid]) + cat("\n\nPartition:", LETTERS[dev$partid]) + cat("\nNode:", levels(where[[dev$partid]])[dev$nodeid]) + cat("\nVariable:", names(partData)[dev$varid]) } else { cat("\n") } cat("\nCutpoint:\n") - print(as.data.frame(matrix(loss$cut, 1L, - dimnames = list(loss$cutid, - names(loss$cut))))) + print(as.data.frame(matrix(dev$cut, 1L, + dimnames = list(dev$cutid, + names(dev$cut))))) cat("Model comparison:\n") - print(data.frame("loss" = c(control$lossfun(model), - control$lossfun(model) - loss$lossred), - "penalized loss reduction" = c("", format(loss$plossred)), - row.names = paste("step", step + c(-1, 0)), - check.names = FALSE)) + print(data.frame( + cbind("loss" = c( + round(control$lossfun(model), 2), + round(control$lossfun(model) - dev$dev, 2L)), + ## if 'cp == 0' + "dev" = if (control$cp == 0) + c("", round(dev$dev, 2L)), + ## if 'cp > 0' + "penalized dev" = if (control$cp > 0) + c("", round(dev$pendev, 2L)), + deparse.level = 2), + row.names = paste("step", step + c(-1, 0)), + check.names = FALSE)) } else { cat("\n\nStopping partitioning.\nMessage:", as.character(stopinfo), "\n") @@ -387,7 +444,7 @@ tvcm_grow <- function(object, subset = NULL, weights = NULL) { } ## prepare the title - title <- c("Tree-based varying-coefficients model") + title <- c("Tree-Based Varying Coefficients Model") ## modify splitpath splitpath <- tvcm_grow_splitpath(splitpath, varid, nodes, partData, control) @@ -526,7 +583,7 @@ tvcm_grow_fit <- function(mcall, doFit = TRUE) { ##' Improve performance for non 'olmm' objects ##' -------------------------------------------------------- # -tvcm_grow_update <- function(object) { +tvcm_grow_update <- function(object, control) { if (inherits(object, "olmm")) { @@ -551,27 +608,32 @@ tvcm_grow_update <- function(object) { olmm_merge_mm(x = model.matrix(termsFeCe, object$frame, conCe), y = model.matrix(termsFeGe, object$frame, conGe), TRUE) - ## extract interaction predictors to be centered - subsCe <- which(rownames(attr(termsFeCe, "factors")) %in% c("Left", "Right")) - if (any(subsCe)) { - subsCe <- - which(colSums(attr(termsFeCe, "factors")[subsCe,,drop = FALSE]) > 0 & - !colnames(attr(termsFeCe, "factors")) %in% c("Left", "Right")) - subsCe <- - which(attr(object$X, "assign") %in% subsCe & attr(object$X, "merge") == 1) - } - subsGe <- which(rownames(attr(termsFeGe, "factors")) %in% c("Left", "Right")) - if (any(subsGe)) { - subsGe <- - which(colSums(attr(termsFeGe, "factors")[subsGe,,drop = FALSE]) > 0 & - !colnames(attr(termsFeGe, "factors")) %in% c("Left", "Right")) - subsGe <- - which(attr(object$X, "assign") %in% subsGe & attr(object$X, "merge") == 2) + if (control$center) { + + ## extract interaction predictors to be centered + ## (the ones with 'Left' and 'Right') + cColsCe <- which(rownames(attr(termsFeCe, "factors")) %in% c("Left", "Right")) + if (length(cColsCe) > 0L) { + cColsCe <- + which(colSums(attr(termsFeCe, "factors")[cColsCe,,drop = FALSE]) > 0 & + !colnames(attr(termsFeCe, "factors")) %in% c("Left", "Right")) + cColsCe <- + which(attr(object$X, "assign") %in% cColsCe & attr(object$X, "merge") == 1) + } + cColsGe <- which(rownames(attr(termsFeGe, "factors")) %in% c("Left", "Right")) + if (length(cColsGe) > 0L) { + cColsGe <- + which(colSums(attr(termsFeGe, "factors")[cColsGe,,drop = FALSE]) > 0 & + !colnames(attr(termsFeGe, "factors")) %in% c("Left", "Right")) + cColsGe <- + which(attr(object$X, "assign") %in% cColsGe & attr(object$X, "merge") == 2) + } + + ## center the predictors + object$X[, c(cColsCe, cColsGe)] <- + scale(object$X[, c(cColsCe, cColsGe)], center = TRUE, scale = TRUE) } - ## center the predictors - for (v in c(subsCe, subsGe)) object$X[,v] <- object$X[,v] - mean(object$X[,v]) - ## prepare optimization optim <- object$optim optim[[1L]] <- object$coefficients @@ -599,10 +661,28 @@ tvcm_grow_update <- function(object) { } else { - ## modify components in 'object' - x <- model.matrix(object$formula, model.frame(object)) + ## extract interaction predictors to be centered + ## (the ones with 'Left' and 'Right') + X <- model.matrix(object$formula, model.frame(object)) + + if (control$center) { + + ## get the columns of 'X' to be centered + terms <- terms(object$formula) + cCols <- which(rownames(attr(terms, "factors")) %in% c("Left", "Right")) + if (length(cCols) > 0L) { + cCols <- which(colSums(attr(terms, "factors")[cCols,,drop = FALSE]) > 0 & + !colnames(attr(terms, "factors")) %in% c("Left", "Right")) + cCols <- which(attr(X, "assign") %in% cCols) + } + + ## centering + X[, cCols] <- scale(X[, cCols], center = TRUE, scale = TRUE) + } + + object <- try(suppressWarnings( - glm.fit(x = x, y = object$y, weights = object$prior.weights, + glm.fit(x = X, y = object$y, weights = object$prior.weights, start = object$coefficients, offset = object$offset, family = object$family, control = object$control, intercept = TRUE)), TRUE) @@ -620,6 +700,159 @@ tvcm_grow_update <- function(object) { tvcm_grow_gefp <- gefp.olmm # see 'olmm-methods.R' +##'-------------------------------------------------------- # +##' Computes cutpoints of 'numeric' partitioning variables +##' +##' @param z a numeric vector +##' @param w a numeric vector of weights +##' @param minsize numerical scalar. The minimum node size. +##' @param maxnumsplit integer scalar. The maximum number +##' of cutpoints. +##' +##' @return A matrix with one column and one row for each +##' cutpoint +##' +##' @details Used in 'tvcm_grow_setsplits'. +##'-------------------------------------------------------- # + +tvcm_getNumSplits <- function(z, w, minsize, maxnumsplit) { + + ## order the partitioning variable + ord <- order(z) + z <- z[ord]; w <- w[ord]; + + ## get first and last valid indices + cw <- cumsum(w) + subsL <- max(c(1, which(cw < minsize))) + subsR <- min(c(length(z), which(cw > (cw[length(cw)] - minsize)))) + + if (subsL <= subsR && z[subsL] <= z[subsR]) { + + ## valid splits available + rval <- unique(z[z >= z[subsL] & z < z[subsR]]) + maxz <- z[length(z)] + + ## apply a cutpoint reduction if necessary + if ((length(rval) - 1) > maxnumsplit) { + nq <- maxnumsplit - 1L + rval <- c() + cw <- cw / cw[length(cw)] + while (length(rval) < (maxnumsplit + 1L)) { + nq <- nq + 1L + q <- (1:nq) / (nq + 1L) + rval <- unique(c(sapply(q, function(p) z[max(which(cw <= p))]), maxz)) + } + } + + ## delete largest value + rval <- rval[rval < maxz] + + } else { + + ## no valid splits + rval <- numeric() + } + + ## prepare return value + rval <- matrix(rval, ncol = 1L) + colnames(rval) <- "cut" + attr(rval, "type") <- "dev" + + ## return matrix with cutpoints + return(rval) +} + + +##'-------------------------------------------------------- # +##' Computes cutpoints of 'ordinal' partitioning variables +##' +##' @param z a vector of class 'ordered' +##' @param w a numeric vector of weights +##' @param minsize numerical scalar. The minimum node size. +##' @param maxordsplit integer scalar. The maximum number +##' of cutpoints. +##' +##' @return A matrix with one column for each category and +##' one row for each cutpoint +##' +##' @details Used in 'tvcm_grow_setsplits'. +##'-------------------------------------------------------- # + +tvcm_getOrdSplits <- function(z, w, minsize, maxordsplit) { + + ## get integer cutpoints using 'tvcm_getNumSplits' + nl <- nlevels(z) # all levels + cuts <- tvcm_getNumSplits(as.integer(z), w, minsize, maxordsplit) + zdlev <- 1:nl %in% as.integer(cuts) + + ## create a matrix to apply categorical splits + rval <- diag(nl) + rval[lower.tri(rval)] <- 1L + rval <- rval[zdlev,, drop = FALSE] + + ## prepare return value + colnames(rval) <- levels(z) + attr(rval, "type") <- "dev" + + ## return matrix with cutpoints + return(rval) +} + + +##'-------------------------------------------------------- # +##' Computes cutpoints of 'nominal' partitioning variables +##' +##' @param z a vector of class 'factor' +##' @param w a numeric vector of weights +##' @param minsize numerical scalar. The minimum node size. +##' @param maxnomsplit integer scalar. The maximum number +##' of cutpoints. +##' +##' @return A matrix with one column for each category and +##' one row for each cutpoint +##' +##' @details Used in 'tvcm_grow_setsplits'. +##'-------------------------------------------------------- # + +tvcm_getNomSplits <- function(z, w, minsize, maxnomsplit) { + + zdlev <- 1 * (levels(z) %in% levels(droplevels(z))) + if (sum(zdlev) < maxnomsplit) { + + ## exhaustive search + rval <- .Call("tvcm_nomsplits", + as.integer(zdlev), + PACKAGE = "vcrpart") + type <- "dev" + + } else { + + ## Heuristic reduction of splits: in tvcm_grow_gridsearch, + ## the 'isolated' coefficients of each category are + ## computed. The coefficients are used for ordering + ## the categories and finally the variable is treated + ## as ordinal. See tvcm_grow_gridsearch + + rval <- diag(nlevels(z)) + type <- "coef" + } + + ## remove splits according to 'minsize' + sumWTot <- sum(w) + sumWCat <- tapply(w, z, sum) + sumWCat[is.na(sumWCat)] <- 0 + valid <- apply(rval, 1, function(x) { + all(c(sum(sumWCat[x > 0]), sumWTot - sum(sumWCat[x > 0])) > minsize) + }) + rval <- rval[valid,, drop = FALSE] + + ## return matrix with cutpoints + colnames(rval) <- levels(z) + attr(rval, "type") <- type + return(rval) +} + + ##'-------------------------------------------------------- # ##' Computes candidate splits for the current step ##' @@ -649,9 +882,9 @@ tvcm_grow_gefp <- gefp.olmm # see 'olmm-methods.R' ##' @details Used in 'tvcm'. ##'-------------------------------------------------------- # -tvcm_grow_setsplits <- function(splits, spart, partid, - nodeid, varid, model, nodes, - where, partData, control) { +tvcm_grow_setsplits <- function(splits, partid, nodeid, varid, + model, nodes, where, + partData, control) { ## get tree size criteria of current tree(s) width <- sapply(nodes, width) @@ -660,207 +893,71 @@ tvcm_grow_setsplits <- function(splits, spart, partid, info_node(node)$depth })) }) w <- weights(model) - - ##'------------------------------------------------------ # - ##' 'getSplits' extracts splits for each combination of - ##' partition, variable and node. - ##' - ##' @param pid integer. The partition identification number. - ##' @param vid integer. The row of the variable in 'partData'. - ##' @param nid integer. The node identification number. - ##' For example, if the tree has terminal nodes with - ##' identification numbers 2, 4 and 5, the index 1 is used - ##' to get splits in node 2, index 2 for node 4 and so on. - ##' - ##' @return a matrix with one row for each split. - ##'------------------------------------------------------ # getSplits <- function(pid, nid, vid) { - ## get subset + ## prepare data subs <- where[[pid]] == levels(where[[pid]])[nid] - - ## get partitioning variable - z <- partData[, vid] + z <- partData[subs, vid] + w <- w[subs] - ## return 'NULL' if tree classical tree growing parameters exceeded if (width[pid] >= control$maxwidth[pid] | depth[[pid]][nid] >= control$maxdepth[pid] | sum(subs) < 1L | - sum(w[subs]) < 2 * control$minsize[pid]) { - rval <- matrix(, 0, ifelse(is.numeric(z), 3L, nlevels(z) + 2L)) - colnames(rval) <- c(if (is.numeric(z)) "cut" else levels(z), - "lossred", "df") - attr(rval, "type") <- "loss" - attr(rval, "keeplosscount") <- 0 - return(rval) - } - type <- "loss" + sum(w) < 2 * control$minsize[pid]) { + + ## return an empty matrix if control parameters exceeded + rval <- matrix(, 0, ifelse(is.numeric(z), 1L, nlevels(z))) + colnames(rval) <- if (is.numeric(z)) "cut" else levels(z) + attr(rval, "type") <- "dev" - if (is.numeric(z)) { - ## continuous variables - - sz <- z[subs] - - ## get all splits that satisfy the 'minsize' criteria - subsL <- rev(which(cumsum(w[subs][order(sz)]) < control$minsize[pid]))[1L] - subsR <- which(cumsum(w[subs][order(sz)]) > - (sum(w[subs]) - control$minsize[pid]))[1L] - - if (subsL <= subsR && sort(sz)[subsL] < sort(sz)[subsR]) { - sz <- sort(sz) - sz <- sz[sz >= sz[subsL] & sz < sz[subsR]] - rval <- unique(sz) - - ## reduce the number of splits according to 'control$maxevalsplit' - if ((length(rval) - 1) > control$maxnumsplit) { - nq <- control$maxnumsplit - 1L - rval <- c() - while (length(rval) < (control$maxnumsplit + 1L)) { - nq <- nq + 1L - rval <- unique(quantile(sz, c((1:nq) / (nq + 1L)), 1), type = 1L) - } - rval <- rval[-length(rval)] - } - rval <- cbind(rval, rep.int(NA, length(rval)), rep.int(NA, length(rval))) - - } else { - rval <- matrix(, 0L, 3L) - } - colnames(rval) <- c("cut", "lossred", "df") - - } else if (is.factor(z)) { # categorical variables - - nl <- nlevels(z) # all levels - nld <- nlevels(droplevels(z[subs])) # observed levels in current node - zdlev <- which(levels(z) %in% levels(droplevels(z[subs]))) - - if (is.ordered(z)) { - ## ordinal variables - - rval <- diag(nl) - rval[lower.tri(rval)] <- 1L - rval <- rval[-nl,, drop = FALSE] - - } else { - ## nominal variables - - if (nld <= control$maxfacsplit) { - - ## exhaustive search - mi <- 2^(nld - 1L) - 1L - rval <- .Call("tvcm_nomsplits", - as.integer(nl), - as.integer(zdlev), as.integer(nld), - as.integer(mi), PACKAGE = "vcrpart") - rval <- matrix(rval, mi, nl, byrow = TRUE) - - } else { - - ## Heuristic reduction of splits: in tvcm_grow_gridsearch, - ## the 'isolated' coefficients of each category are - ## computed. The coefficients are used for ordering - ## the categories and finally the variable is treated - ## as ordinal. See tvcm_grow_gridsearch - rval <- diag(nl) - type <- "coef" - } - } - - ## delete indadmissible splits - valid <- tvcm_setsplits_validcats(rval, z, w, subs, control$minsize[pid]) - rval <- rval[valid,, drop = FALSE] - - ## delete ordinal splits if 'maxordsplit' is exceeded - if (is.ordered(z) && nrow(rval) > control$maxordsplit) { - nCat <- apply(rval, 1, function(x) sum(subs & z %in% levels(z)[x > 0L])) - nCat <- round(c(nCat[1L], diff(nCat))) - zd <- rep.int(1:nrow(rval), nCat) - nq <- control$maxordsplit - 1L; rows <- 1; - while(length(rows) < control$maxordsplit) { - nq <- nq + 1L - rows <- unique(quantile(zd, (1:nq) / (nq + 1L)), type = 1L) - } - rval <- rval[rows,,drop=FALSE] - } - - rval <- cbind(rval, rep.int(NA, nrow(rval)), rep.int(NA, nrow(rval))) - colnames(rval) <- c(levels(z), "lossred", "df") - } else { - - rval <- matrix(, 0L, 3L) - colnames(rval) <- c("cut", "lossred", "df") + + ## compute matrix according to their scale + rval <- switch(class(z)[1L], + numeric = tvcm_getNumSplits(z, w, + control$minsize[pid], + control$maxnumsplit), + integer = tvcm_getNumSplits(z, w, + control$minsize[pid], + control$maxnumsplit), + ordered = tvcm_getOrdSplits(z, w, + control$minsize[pid], + control$maxordsplit), + factor = tvcm_getNomSplits(z, w, + control$minsize[pid], + control$maxnomsplit), + stop("class of variable '", + colnames(partData)[vid], + "' not recognized")) } - attr(rval, "type") <- type - attr(rval, "keeplosscount") <- 0 return(rval) } ## compute splits in all partitions, partitioning variables and nodes - rval <- vector("list", length(partid)) for (pid in seq_along(partid)) { - rval[[pid]] <- vector("list", length(nodeid[[partid[pid]]])) for (nid in seq_along(nodeid[[partid[pid]]])) { - rval[[pid]][[nid]] <- vector("list", length(varid[[partid[pid]]])) for (vid in seq_along(varid[[partid[pid]]])) { split <- splits[[pid]][[nid]][[vid]] - if (is.null(split) | !is.null(split) && attr(split, "type") == "coef" | + if (is.null(split[[1L]]) | + (!is.null(split[[1L]]) && attr(split[[1L]], "type") == "coef") | width[pid] >= control$maxwidth[pid]) { - split <- getSplits(partid[pid], - nodeid[[partid[pid]]][nid], - varid[[partid[pid]]][vid]) + + split[[1L]] <- getSplits(partid[pid], + nodeid[[partid[pid]]][nid], + varid[[partid[pid]]][vid]) + split[[2L]] <- split[[3L]] <- rep(NA, nrow(split[[1L]])) } else { - if (nrow(split) > 0L && - (spart != partid[pid] | - attr(split, "keeplosscount") >= control$keeploss)) { - attr(split, "keeplosscount") <- 0 - split[, c("lossred", "df")] <- NA - } else { - attr(split, "keeplosscount") <- attr(split, "keeplosscount") + 1L - } + if (nrow(split[[1]]) > 0L) + split[[2L]][] <- split[[3L]][] <- NA } - rval[[pid]][[nid]][[vid]] <- split + splits[[pid]][[nid]][[vid]] <- split } } } ## return list of updated 'splits' - return(rval) -} - - -##'------------------------------------------------------ # -##' Computes the valid categorical splits defined in a -##' integer matrix with respect to the \code{minsize} -##' criteria -##' -##' @param cp an integer matrix that defines the splits -##' according to the levels in \code{z}. -##' @param z the categorical (nominal or ordered) partitioning -##' variable. -##' @param weights a weights vector -##' @param subs a logical vector that extracts the current -##' node. -##' @param minsize the minimum number of splits in a node -##' -##' @return a logical vector of length equal to the number -##' of rows of \code{cp}. -##'------------------------------------------------------ # - -tvcm_setsplits_validcats <- function(cp, z, weights, subs, minsize) { - - ## delete cuts that do not satisfy 'minsize' - rval <- rep.int(TRUE, nrow(cp)) - for (i in seq_along(rval)) { - Node <- factor(1 * (z[subs] %in% levels(z)[cp[i,]==1L])) - if (nlevels(Node) == 1L | (nlevels(Node) > 1L && - any(tapply(weights[subs], Node, sum) < minsize))) - rval[i] <- FALSE - } - - ## return logical vector - return(rval) + return(splits) } @@ -886,15 +983,15 @@ tvcm_setsplits_validcats <- function(cp, z, weights, subs, minsize) { tvcm_setsplits_sctest <- function(splits, partid, spart, nodeid, snode, varid, svar) { - + ## set loss of not selected parts to -Inf for (pid in seq_along(partid)) for (nid in seq_along(nodeid[[pid]])) for (vid in seq_along(varid[[pid]])) { if (pid == spart & nid == snode & vid == svar) { - splits[[pid]][[nid]][[vid]][, "lossred"] <- NA + splits[[pid]][[nid]][[vid]][[2L]][] <- NA } else { - splits[[pid]][[nid]][[vid]][, "lossred"] <- -Inf + splits[[pid]][[nid]][[vid]][[2L]][] <- -Inf } } ## return updated 'splits' @@ -918,13 +1015,9 @@ tvcm_setsplits_sctest <- function(splits, partid, spart, ##' @return An modified list of splits. ##' ##' @details Used in 'tvcm'. -##' -##' To do: -##' 2014-07-21: find a better rule! (function is not used currently!) ##'------------------------------------------------------ # -tvcm_setsplits_splitnode <- function(splits, spart, snode, - nodeid, loss, model, control) { +tvcm_setsplits_splitnode <- function(splits, spart, snode, nodeid) { ## expand the splits list lnodes <- nodeid[[spart]][nodeid[[spart]] < snode] @@ -933,6 +1026,9 @@ tvcm_setsplits_splitnode <- function(splits, spart, snode, split <- vector("list", length(split0) + 1L) if (length(lnodes) > 0L) split[lnodes] <- split0[lnodes] if (length(unodes) > 0L) split[unodes + 1L] <- split0[unodes] + nvar <- length(split0[[snode]]) + split[[snode]] <- split[[snode + 1]] <- + replicate(nvar, vector("list", 3L), simplify = FALSE) splits[[spart]] <- split ## return updated 'splits' @@ -997,7 +1093,7 @@ tvcm_setsplits_rselect <- function(splits, partid, nodeid, varid, control) { for (vid in seq_along(varid[[pid]])) if (nrow(splits[[pid]][[nid]][[vid]]) > 0 && !(pid %in% spart & vid %in% svar[[pid]] & nid %in% snode[[pid]])) - splits[[pid]][[nid]][[vid]][, "lossred"] <- -Inf + splits[[pid]][[nid]][[vid]][[2L]][] <- -Inf ## return updated 'splits' return(splits) @@ -1024,7 +1120,7 @@ tvcm_setsplits_rselect <- function(splits, partid, nodeid, varid, control) { ##'------------------------------------------------------ # tvcm_grow_sctest <- function(model, nodes, where, partid, nodeid, varid, - splits, partData, control) { + splits, partData, control) { ## get variable types functional <- sapply(partData, function(x) { @@ -1038,19 +1134,25 @@ tvcm_grow_sctest <- function(model, nodes, where, partid, nodeid, varid, ## prepare list with arguments for 'sctest' rval <- vector("list", length(nodes)) for (pid in seq_along(nodes)) { - dim <- c(nlevels(where[[pid]]), length(varid[[pid]]), control$ninpute) + dim <- c(nlevels(where[[pid]]), length(varid[[pid]]), control$nimpute) dn <- list(paste("Node", LETTERS[pid], levels(where[[pid]]), sep = ""), - colnames(partData)[varid[[pid]]], 1:control$ninpute) + colnames(partData)[varid[[pid]]], 1:control$nimpute) rval[[pid]] <- array(, dim = dim, dimnames = dn) } - ## call 'estfun' - eCall <- list(name = as.name(ifelse(inherits(model, "olmm"),"estfun.olmm", "estfun"))) + ## call 'estfun' (eventually parallelized) + eCall <- + list(name = as.name(ifelse(inherits(model, "olmm"),"estfun.olmm", "estfun"))) eCall$x <- quote(model) - eCall[names(control$estfun)] <- control$estfun + eCall[names(control$estfun.args)] <- control$estfun.args mode(eCall) <- "call" - scores <- replicate(control$ninpute, eval(eCall)) - + pCall <- list(name = as.name(control$papply), + X = quote(seq(1L, control$nimpute, 1L)), + FUN = quote(function(i) eval(eCall))) + pCall[names(control$papply.args)] <- control$papply.args + mode(pCall) <- "call" + scores <- simplify2array(eval(pCall)) + ## set the 'gefp' call (which is called in each iteration below) gCall <- call(name = "tvcm_grow_gefp", object = quote(model), scores = quote(sc), @@ -1073,7 +1175,7 @@ tvcm_grow_sctest <- function(model, nodes, where, partid, nodeid, varid, for (nid in seq_along(nodeid[[pid]])) { # loop over nodes ## check if there is a permitted split - if (length(splits[[pid]][[nid]][[vid]]) > 0L) { + if (nrow(splits[[pid]][[nid]][[vid]][[1L]]) > 0L) { ## observations of the current partition rows <- where[[pid]] == levels(where[[pid]])[nid] @@ -1087,7 +1189,7 @@ tvcm_grow_sctest <- function(model, nodes, where, partid, nodeid, varid, cols <- dimnames(scores)[[2L]][cols] } - for (k in 1:control$ninpute) { + for (k in 1:control$nimpute) { sc <- matrix(scores[,,k,drop=FALSE], dim(scores)[1], dim(scores)[2], dimnames = dimnames(scores)[1L:2L]) gefp <- try(eval(gCall), TRUE) @@ -1160,15 +1262,15 @@ tvcm_sctest_bonf <- function(test, type) { ##' @return A nested list with loss matrices. Partitions of nodes ##' are nested in partitions for variables. ##' -##' @details Used in 'tvcm'. 'tvcm_grow_lossRed' is a help +##' @details Used in 'tvcm'. 'tvcm_grow_dev' is a help ##' function of 'tvcm_grow_gridsearch' ##'-------------------------------------------------------- # -tvcm_grow_lossred <- function(cutpoint, type = "loss", - pid, nid, vid, - model, modelNuis, nuisance, - where, partData, - control, loss0, mfName) { +tvcm_grow_dev <- function(cutpoint, type = "dev", + pid, nid, vid, + model, modelNuis, nuisance, + where, partData, + control, loss0, mfName) { ## set node indicator subs <- where[[pid]] == levels(where[[pid]])[nid] @@ -1183,11 +1285,11 @@ tvcm_grow_lossred <- function(cutpoint, type = "loss", parm <- grep("Left", names(coef(model)), value = TRUE) ## fit the 'update' model - model <- tvcm_grow_update(model) + model <- tvcm_grow_update(model, control) - if (type == "loss") { + if (type == "dev") { if (!inherits(model, "try-error")) { - rval <- c(loss0 - control$lossfun(model), + rval <- c((loss0 - control$lossfun(model)), length(coef(model)[grep("Left", names(coef(model)))]) - length(nuisance)) if (is.null(modelNuis)) { @@ -1195,7 +1297,7 @@ tvcm_grow_lossred <- function(cutpoint, type = "loss", } else { modelNuis[[mfName]]$Left <- 1 * (subs & zs) modelNuis[[mfName]]$Right <- 1 * (subs & !zs) - modelNuis <- tvcm_grow_update(modelNuis) + modelNuis <- tvcm_grow_update(modelNuis, control) rval[1L] <- rval[1L] - (loss0 - control$lossfun(modelNuis)) return(rval) } @@ -1243,7 +1345,6 @@ tvcm_grow_gridsearch <- function(splits, partid, nodeid, varid, Right <- Left - 1 for (pid in seq_along(partid)) { - if (length(unlist(splits[[pid]])) > 0L) { mcall$formula <- ff$update[[pid]][[1L]] @@ -1268,24 +1369,27 @@ tvcm_grow_gridsearch <- function(splits, partid, nodeid, varid, for (nid in seq_along(splits[[pid]])) { if (length(unlist(splits[[pid]][[nid]])) > 0L) { for (vid in seq_along(splits[[pid]][[nid]])) { - cp <- splits[[pid]][[nid]][[vid]] + + cp <- splits[[pid]][[nid]][[vid]][[1L]] type <- attr(cp, "type") - subs <- is.na(cp[, "lossred"]) - cp <- cp[, !colnames(cp) %in% c("lossred", "df"), drop = FALSE] - if (any(subs)) { - st <- apply(cp, 1, tvcm_grow_lossred, type = type, + subs <- is.na(splits[[pid]][[nid]][[vid]][[2L]]) + cp <- cp[subs,, drop = FALSE] + if (nrow(cp) > 0L) { + st <- apply(cp, 1, tvcm_grow_dev, type = type, pid = partid[pid], nid = nodeid[[partid[pid]]][nid], vid = varid[[partid[pid]]][vid], model = sModel, modelNuis = sModelN, nuisance = control$nuisance[[pid]], where = where, partData = partData, - control = control, loss0 = loss0, mfName = mfName) + control = control, loss0 = loss0, + mfName = mfName) if (is.matrix(st)) st <- t(st) else st <- matrix(st, ncol = 1L) - if (type == "loss") { - splits[[pid]][[nid]][[vid]][subs, c("lossred", "df")] <- st - + if (type == "dev") { + splits[[pid]][[nid]][[vid]][[2L]][subs] <- st[, 1L] + splits[[pid]][[nid]][[vid]][[3L]][subs] <- st[, 2L] + } else if (type == "coef") { ## if 'z' is a nominal variable with many categories @@ -1301,35 +1405,21 @@ tvcm_grow_gridsearch <- function(splits, partid, nodeid, varid, score <- rep.int(0, nlevels(z)) score[colSums(cp) > 0] <- prcomp(st)$x[,1] - ## define 'z' as ordinal and retrieve the splits - zd <- factor(z, levels = levels(z)[order(score)], ordered = TRUE) - nl <- nlevels(zd) - cp <- diag(nl) - cp[lower.tri(cp)] <- 1L - cp <- cp[-nl,, drop = FALSE] - valid <- - tvcm_setsplits_validcats(cp, zd, w, subs, - control$minsize[partid[pid]]) - cp <- cp[valid,, drop = FALSE] + ## define 'z' as ordinal and retrieve the splits + z <- ordered(z, levels = levels(z)[order(score)]) + cp <- tvcm_getOrdSplits(z[subs], w[subs], + control$minsize[partid[pid]], + control$maxordsplit) + ## reorder the columns of 'cp' acc. to the original categories cp <- cp[,order(order(score)), drop = FALSE] - if (nrow(cp) > control$maxordsplit) { - nCat <- - apply(cp, 1, function(x) sum(subs & z %in% levels(z)[x > 0L])) - nCat <- round(c(nCat[1L], diff(nCat))) - zd <- rep.int(1:nrow(cp), nCat) - nq <- control$maxordsplit - 1L; rows <- 1; - while(length(rows) < control$maxordsplit) { - nq <- nq + 1L - rows <- unique(quantile(zd, (1:nq) / (nq + 1L)), type = 1L) - } - cp <- cp[rows,,drop=FALSE] - } + ## ensures that split is newly built + attr(cp, "type") <- "coef" ## compute the loss of the new splits - st <- apply(cp, 1, tvcm_grow_lossred, type = "loss", + st <- apply(cp, 1, tvcm_grow_dev, type = "dev", pid = partid[pid], nid = nodeid[[partid[pid]]][nid], vid = varid[[partid[pid]]][vid], @@ -1337,13 +1427,11 @@ tvcm_grow_gridsearch <- function(splits, partid, nodeid, varid, modelNuis = sModelN, nuisance = control$nuisance[[pid]], where = where, partData = partData, - control = control, loss0 = loss0, mfName = mfName) + control = control, loss0 = loss0, + mfName = mfName) if (is.matrix(st)) st <- t(st) else st <- matrix(st, ncol = 2L) - split <- cbind(cp, st) - colnames(split) <- c(levels(z), "lossred", "df") - attr(split, "type") <- "coef" - splits[[pid]][[nid]][[vid]] <- split + splits[[pid]][[nid]][[vid]] <- list(cp, st[, 1L], st[, 2L]) } } } @@ -1352,42 +1440,50 @@ tvcm_grow_gridsearch <- function(splits, partid, nodeid, varid, } } - ## function that extracts the loss reduction (eventually corrected by the - ## number of predictors) - getPenLossRed <- function(x) { - if (is.list(x)) return(lapply(x, getPenLossRed)) - if (is.matrix(x)) - if (nrow(x) > 0L) - return(x[, "lossred"] - control$dfpar * x[, "df"]) else return(numeric()) - return(x) - } - loss <- getPenLossRed(splits) + ## extracts the penalized loss reduction + pendev <- lapply(splits, function(part) { + ## partition level + lapply(part, function(node) { + ## node level + lapply(node, function(var) { + ## variable level + if (length(var[[2L]]) > 0L) { + return(var[[2L]] - control$cp * + tvcm_complexity(var[[3]], control$dfpar, 1, control$dfsplit)) + } else { + return(numeric()) + } + }) + }) + }) + - ## function that extracts the maximum loss reduction - getMaxPenLossRed <- function(x) { + ## function to extract the maximum loss reduction for each part/node/var + getMaxPenDev <- function(x) { x <- unlist(x) if (length(x) == 0L) return(-Inf) x <- na.omit(x) if (length(x) > 0L) return(max(x)) else return(-Inf) } - maxLossDiff <- max(c(-Inf, na.omit(unlist(loss)))) + ## the maximum loss reduction + maxpendev <- max(c(-Inf, na.omit(unlist(pendev)))) - if (maxLossDiff > -Inf) { + if (maxpendev > -Inf) { ## select the partition, node and variable - spart <- which(sapply(sapply(loss, getMaxPenLossRed), identical, maxLossDiff)) + spart <- which(sapply(sapply(pendev, getMaxPenDev), identical, maxpendev)) if (length(spart) > 1L) spart <- sample(spart, 1L) snode <- - which(sapply(sapply(loss[[spart]], getMaxPenLossRed), identical, maxLossDiff)) + which(sapply(sapply(pendev[[spart]], getMaxPenDev), identical, maxpendev)) if (length(snode) > 1L) snode <- sample(snode, 1L) - svar <- which(sapply(sapply(loss[[spart]][[snode]], getMaxPenLossRed), - identical, maxLossDiff)) + svar <- which(sapply(sapply(pendev[[spart]][[snode]], getMaxPenDev), + identical, maxpendev)) if (length(svar) > 1L) svar <- sample(svar, 1L) ## select the cut stat <- splits[[spart]][[snode]][[svar]] - cutid <- which(stat[, "lossred"] == max(stat[, "lossred"])) + cutid <- which(stat[[2L]] == max(stat[[2L]], na.rm = TRUE)) if (length(cutid) > 1L) cutid <- sample(cutid, 1L) if (verbose) cat("OK") @@ -1396,18 +1492,18 @@ tvcm_grow_gridsearch <- function(splits, partid, nodeid, varid, nodeid = nodeid[[partid[spart]]][snode], varid = varid[[partid[spart]]][svar], cutid = cutid, - cut = stat[cutid, !colnames(stat) %in% c("lossred", "df")], - lossred = as.numeric(stat[cutid, "lossred"]), - plossred = maxLossDiff, - df = as.numeric(stat[cutid, "df"]), - lossgrid = splits)) + cut = stat[[1L]][cutid, ], + dev = as.numeric(stat[[2L]][cutid]), + pendev = maxpendev, + npar = as.numeric(stat[[3L]][cutid]), + grid = splits)) } else { if (verbose) cat("failed") return(list(partid = NULL, nodeid = NULL, varid = NULL, - cutid = NULL, cut = NULL, lossred = NULL, - plossred = NULL, lossgrid = splits)) + cutid = NULL, cut = NULL, dev = NULL, + pendev = NULL, grid = splits)) } } @@ -1415,7 +1511,7 @@ tvcm_grow_gridsearch <- function(splits, partid, nodeid, varid, ##'-------------------------------------------------------- # ##' Incorporates a new binary split into an existing -##' tree structire. +##' tree structure. ##' ##' @param nodes an object of class 'partynode'. ##' @param loss a list produced by 'tvcm_grow_gridsearch'. @@ -1427,14 +1523,13 @@ tvcm_grow_gridsearch <- function(splits, partid, nodeid, varid, ##' Used in 'tvcm'. ##'-------------------------------------------------------- # -tvcm_grow_splitnode <- function(nodes, where, loss, partData, step, weights) { +tvcm_grow_splitnode <- function(nodes, where, dev, partData, step, weights) { - pid <- loss$partid - nid <- loss$nodeid - vid <- loss$varid + pid <- dev$partid + nid <- dev$nodeid + vid <- dev$varid nidLab <- nodeids(nodes[[pid]], terminal = TRUE)[nid] - stat <- loss$loss - cut <- loss$cut + cut <- dev$cut x <- partData[, vid] ## collect information for the split @@ -1517,6 +1612,9 @@ tvcm_grow_splitnode <- function(nodes, where, loss, partData, step, weights) { ##' @param family an object of class 'family' or 'family.olmm'. ##' @param env the environment where the output formula ##' is to be evaluated. +##' @param full whether the full formula should be derived +##' @param update whether the formula for the update model +##' should be derived. ##' @return A list of formulas ('root', 'tree' and 'original'). ##' ##' @details Used in \code{\link{predict.fvcm}} and @@ -1791,9 +1889,9 @@ tvcm_grow_setcontrol <- function(control, model, formList, root, parm.only = TRU ## set 'nuisance' slots control$nuisance <- lapply(formList$vc, function(x) x$nuisance) - control$estfun$nuisance <- - unique(c(control$estfun$nuisance, - setdiff(names(coef(model)), unlist(control$parm)))) + control$estfun.args$nuisance <- + unique(c(control$estfun.args$nuisance, + setdiff(names(coef(model)), names(fixef(model))))) return(control) } @@ -1867,7 +1965,7 @@ tvcm_get_terms <- function(names, ids, parm) { parm <- lapply(parm, function(x) { lapply(x, function(x) x[grepl("Node[A-Z]", x)]) - }) + }) if (any(unlist(ids) == 1L)) { getNames <- function(x) { @@ -1990,8 +2088,8 @@ tvcm_get_vcparm <- function(object) { tvcm_get_estimates <- function(object, what = c("coef", "sd", "var"), ...) { what <- match.arg(what) - model <- extract(object, "model") - + model <- object$info$model + rval <- list(fe = numeric(), vc = replicate(length(object$info$node), matrix(,0,0)), re = numeric()) @@ -2004,66 +2102,73 @@ tvcm_get_estimates <- function(object, what = c("coef", "sd", "var"), ...) { var = diag(vcov(model))) ids <- lapply(object$info$node, nodeids, terminal = TRUE) - + formList <- object$info$formula - + + ## the terms for which coefficients exist termsC <- tvcm_get_terms(names(coef(model)), ids, object$info$control$parm) + + ## the terms for which estimates for 'type' exist termsE <- tvcm_get_terms(names(estimates), ids, object$info$control$parm) - + ## restricted coefficients if (any(termsE$type == "fe")) rval$fe <- estimates[termsE$type == "fe"] - + ## random effects if (any(termsE$type == "re")) rval$re <- estimates[termsE$type == "re"] - + ## varying coefficients if (any(termsE$type == "vc")) { - + for (pid in seq_along(object$info$node)) { - - nnodes <- length(ids[[pid]]) # number of nodes ## extract the terms corresponding to the partition vcTermsC <- unique(termsC$terms[termsC$partition == LETTERS[pid]]) vcTermsE <- unique(termsE$terms[termsE$partition == LETTERS[pid]]) - - ## build a matrix to store the coefficients - rval$vc[[pid]] <- matrix(, nnodes, length(vcTermsC)) - rownames(rval$vc[[pid]]) <- ids[[pid]] - colnames(rval$vc[[pid]]) <- vcTermsC - - ## fill the matrix - for (i in seq_along(vcTermsC)) { - subs <- termsE$terms == vcTermsC[i] & termsE$partition == LETTERS[pid] - rval$vc[[pid]][termsE$node[subs], i] <- estimates[subs] - } - - ## add colnames for varying intercepts - subs <- which(colnames(rval$vc[[pid]]) %in% "") - if (length(subs) > 0L) colnames(rval$vc[[pid]])[subs] <- "(Intercept)" - if (ncol(rval$vc[[pid]]) > 0 & inherits(object$info$family, "family.olmm")) { - tmp <- strsplit(colnames(rval$vc[[pid]]), ":") - subs <- sapply(tmp, length) == 1L & - sapply(tmp, function(x) substr(x[1L], 1L, 3L) == "Eta") - colnames(rval$vc[[pid]])[subs] <- - paste(colnames(rval$vc[[pid]])[subs], "(Intercept)", sep = ":") - } - - ## fill the last row if necessary - subs <- is.na(rval$vc[[pid]][nnodes, ]) - if (any(subs)) { + + ## make a matrix only if specific terms exist for the partition + if (length(vcTermsC) > 0L) { - ## compute the coefficients of the omitted node - con <- model$contrasts[[paste("Node", LETTERS[pid], - sep = "")]][nnodes, ] - for (i in which(subs)) { - rval$vc[[pid]][nnodes, i] <- - switch(what, - coef = sum(con * rval$vc[[pid]][-nnodes, i], na.rm = TRUE), - sd = sum(con^2 * rval$vc[[pid]][-nnodes, i], na.rm = TRUE), - var = sum(con^2 * rval$vc[[pid]][-nnodes, i], na.rm = TRUE)) + nnodes <- length(ids[[pid]]) # number of nodes + + ## build a matrix to store the coefficients + rval$vc[[pid]] <- matrix(, nnodes, length(vcTermsC)) + rownames(rval$vc[[pid]]) <- ids[[pid]] + colnames(rval$vc[[pid]]) <- vcTermsC + + ## fill the matrix + for (i in seq_along(vcTermsC)) { + subs <- termsE$terms == vcTermsC[i] & termsE$partition == LETTERS[pid] + rval$vc[[pid]][termsE$node[subs], i] <- estimates[subs] + } + + ## add colnames for varying intercepts + subs <- which(colnames(rval$vc[[pid]]) %in% "") + if (length(subs) > 0L) colnames(rval$vc[[pid]])[subs] <- "(Intercept)" + if (ncol(rval$vc[[pid]]) > 0 & inherits(object$info$family, "family.olmm")) { + tmp <- strsplit(colnames(rval$vc[[pid]]), ":") + subs <- sapply(tmp, length) == 1L & + sapply(tmp, function(x) substr(x[1L], 1L, 3L) == "Eta") + colnames(rval$vc[[pid]])[subs] <- + paste(colnames(rval$vc[[pid]])[subs], "(Intercept)", sep = ":") + } + + ## fill the last row if necessary + subs <- is.na(rval$vc[[pid]][nnodes, ]) + if (any(subs)) { + + ## compute the coefficients of the omitted node + con <- model$contrasts[[paste("Node", LETTERS[pid], + sep = "")]][nnodes, ] + for (i in which(subs)) { + rval$vc[[pid]][nnodes, i] <- + switch(what, + coef = sum(con * rval$vc[[pid]][-nnodes, i], na.rm = TRUE), + sd = sum(con^2 * rval$vc[[pid]][-nnodes, i], na.rm = TRUE), + var = sum(con^2 * rval$vc[[pid]][-nnodes, i], na.rm = TRUE)) + } } } } @@ -2072,16 +2177,15 @@ tvcm_get_estimates <- function(object, what = c("coef", "sd", "var"), ...) { getSqrt <- function(x) { if (is.list(x)) { return(lapply(x, getSqrt)) - } else if (!is.null(x)) { - if (length(x) == 0) return(NA) else return(sqrt(x)) + } else if (length(x) > 0) { + return(sqrt(x)) } else { return(x) } } - rval <- lapply(rval, getSqrt) - + rval <- lapply(rval, getSqrt) } -} + } return(rval) } @@ -2097,20 +2201,19 @@ tvcm_get_estimates <- function(object, what = c("coef", "sd", "var"), ...) { ##' @details Used in 'tvcm_print' and 'plot.tvcm'. ##'-------------------------------------------------------- # -tvcm_print_vclabs <- function(object) { +tvcm_print_vclabs <- function(formList) { - formula <- object$info$formula - if (length(formula$vc) == 0) return(NULL) + if (length(formList$vc) == 0) return(NULL) ## conditioning variables - cond <- lapply(formula$vc, function(x) { + cond <- lapply(formList$vc, function(x) { rval <- all.vars(x$cond) if (length(rval) > 2L) rval <- c(rval[1L], "...") return(rval) }) ## 'by' terms - vcLabs <- terms(formula$original, specials = "vc") + vcLabs <- terms(formList$original, specials = "vc") if (length(attr(vcLabs, "specials")$vc) == 0L) return(NULL) vcLabs <- rownames(attr(vcLabs, "factors"))[attr(vcLabs, "specials")$vc] vcLabs <- paste("getBy", vcLabs, sep = "_") @@ -2122,7 +2225,7 @@ tvcm_print_vclabs <- function(object) { by <- sapply(vcLabs, function(x) eval(parse(text = x))) ## collapse the short labels - rval <- rep.int("vc(", length(formula$vc)) + rval <- rep.int("vc(", length(formList$vc)) for (pid in seq_along(rval)) { if (length(cond) > 0L) rval[pid] <- paste(rval[pid], paste(cond[[pid]], collapse = ", "), sep = "") @@ -2162,8 +2265,6 @@ tvcm_prune_node <- function(object, alpha = NULL, maxstep = NULL, terminal = NUL if (all(c(is.null(alpha), is.null(maxstep), is.null(terminal)))) return(object$info$node) - control <- extract(object, "control") - if (!is.null(alpha) && depth(rval[[pid]]) > 0L) { splitpath <- object$info$splitpath p.value <- extract(object, "p.value") @@ -2176,6 +2277,7 @@ tvcm_prune_node <- function(object, alpha = NULL, maxstep = NULL, terminal = NUL for (pid in seq_along(rval)) { ## prune the tree + if (!is.null(maxstep)) rval[[pid]] <- tvcm_prune_maxstep(rval[[pid]], maxstep) @@ -2325,14 +2427,14 @@ tvcm_grow_splitpath <- function(splitpath, varid, nodes, partData, control) { } - ## change the names of the loss grid elements !!! - if (!is.null(splitpath[[step]]$lossgrid)) { - names(splitpath[[step]]$lossgrid) <- LETTERS[seq_along(nodes)] - for (pid in seq_along(splitpath[[step]]$lossgrid)) { - names(splitpath[[step]]$lossgrid[[pid]]) <- + ## change the names of the dev grid elements !!! + if (!is.null(splitpath[[step]]$grid)) { + names(splitpath[[step]]$grid) <- LETTERS[seq_along(nodes)] + for (pid in seq_along(splitpath[[step]]$grid)) { + names(splitpath[[step]]$grid[[pid]]) <- paste("Node", kidids[[pid]], sep = "") - for (nid in seq_along(splitpath[[step]]$lossgrid[[pid]])) { - names(splitpath[[step]]$lossgrid[[pid]][[nid]]) <- + for (nid in seq_along(splitpath[[step]]$grid[[pid]])) { + names(splitpath[[step]]$grid[[pid]][[nid]]) <- colnames(partData)[varid[[pid]]] } } diff --git a/R/tvcm.R b/R/tvcm.R index 355cefd..f42e0db 100644 --- a/R/tvcm.R +++ b/R/tvcm.R @@ -1,19 +1,31 @@ ##' -------------------------------------------------------- # ##' Author: Reto Buergin ##' E-Mail: reto.buergin@unige.ch, rbuergin@gmx.ch -##' Date: 2014-09-06 +##' Date: 2014-10-23 ##' ##' Description: ##' The 'tvcm' function ##' -##' tvcolmm convenience function for 'tvcm' -##' tvcglm convenience function for 'tvcm' -##' tvcm the main fitting function -##' tvcm_control control function for 'tvcm' +##' tvcolmm convenience function for 'tvcm' +##' tvcolmm_control control function for 'tvcolmm' +##' tvcglm convenience function for 'tvcm' +##' tvcglm_control control function for 'tvglm' +##' tvcm the main fitting function +##' tvcm_control control function for 'tvcm' ##' ##' all functions are documented as *.Rd files ##' ##' Last modifications: +##' 2014-11-05: - set seed at start of 'tvcm' and re-establish old seed +##' at the end +##' 2014-10-23: - improved extraction of fitting arguments (see 'fitargs') +##' - added 'tvcolmm_control' and 'tvcglm_control' to better +##' distinguish between the articles. +##' 2014-09-20: - add argument 'ninupute' the 'tvcm_control' +##' 2014-09-19: - do not call 'cvloss' if no varying coefficients +##' 2014-09-17: - defined definition of penalization +##' - deleted parameter 'maxoverstep' +##' - added parameters 'mindev', 'cp' ##' 2014-09-08: - resolved a problem with 'offset' ##' - removed the environment from the model, which ##' require a lot of memory @@ -24,7 +36,7 @@ ##' produced by 'vcrpart_formula'. The modification ##' was due to acceleration techniques ('vcrpart_formula' ##' is usually slow!) -##' 2014-08-29: - implement adjustment of loss reduction by number +##' 2014-08-29: - implement adjustment of deviance by number ##' of predictor of coefficient-group ##' 2014-07-31: - set 'sctest = FALSE' as the default ##' - return an error if multiple trees and 'sctest = TRUE' @@ -36,32 +48,68 @@ ##' and 'maxoverstep' ##' 2014-06-26: incorporate new function 'tvcm_grow_setsplits' ##' 2014-06-16: allow coefficient-wise trees +##' +##' To do: +##' - ##' -------------------------------------------------------- # tvcolmm <- function(formula, data, family = cumulative(), weights, subset, offset, na.action, - control = tvcm_control(), ...) { + control = tvcolmm_control(), ...) { mc <- match.call() mc[[1L]] <- as.name("tvcm") if (!"family" %in% names(mc) & (length(mc) < 4L | length(mc) >= 4L && !inherits(eval.parent(mc[[4L]]), "family.olmm"))) - mc$family <- formals(tvcolmm)$family + mc$family <- formals(tvcolmm)$family + if (!"control" %in% names(mc) & + (length(mc) < 9L | + length(mc) >= 9L && !inherits(eval.parent(mc[[4L]]), "tvcm_control"))) + mc$control <- formals(tvcolmm)$control mc$fit <- "olmm" return(eval.parent(mc)) } +tvcolmm_control <- function(alpha = 0.05, bonferroni = TRUE, minsize = 50, + maxnomsplit = 5, maxordsplit = 9, maxnumsplit = 9, + trim = 0.1, estfun.args = list(), nimpute = 5, + seed = NULL, ...) { + + mc <- match.call() + mc[[1L]] <- as.name("tvcm_control") + if (!"minsize" %in% names(mc) & length(mc) < 7L) + mc$minsize <- formals(tvcolmm_control)$minsize + mc$sctest <- TRUE + return(eval.parent(mc)) +} + + tvcglm <- function(formula, data, family, weights, subset, offset, na.action, - control = tvcm_control(), ...) { + control = tvcglm_control(), ...) { mc <- match.call() - mc[[1L]] <- as.name("tvcm") + mc[[1L]] <- as.name("tvcm") + if (!"control" %in% names(mc) & + (length(mc) < 9L | + length(mc) >= 9L && !inherits(eval.parent(mc[[4L]]), "tvcm_control"))) + mc$control <- formals(tvcglm)$control mc$fit <- "glm" return(eval.parent(mc)) } +tvcglm_control <- function(minsize = 30, mindev = 2.0, + maxnomsplit = 5, maxordsplit = 9, maxnumsplit = 9, + cv = TRUE, folds = folds_control("kfold", 5), + prune = cv, center = TRUE, ...) { + mc <- match.call() + mc[[1L]] <- as.name("tvcm_control") + return(eval.parent(mc)) +} + + + tvcm <- function(formula, data, fit, family, weights, subset, offset, na.action, control = tvcm_control(), ...) { @@ -73,6 +121,21 @@ tvcm <- function(formula, data, fit, family, if (control$verbose) cat("* checking arguments ... ") stopifnot(inherits(formula, "formula")) stopifnot(inherits(control, "tvcm_control")) + + ## set seed + if (!exists(".Random.seed", envir = .GlobalEnv)) runif(1) + oldSeed <- get(".Random.seed", mode="numeric", envir=globalenv()) + if (!is.null(control$seed)) set.seed(control$seed) + RNGstate <- .Random.seed + + ## check and set 'family' + if (missing(family)) stop("no 'family'.") + if (is.character(family)) { + family <- get(family, mode = "function", envir = parent.frame()) + } else if (is.function(family)) { + family <- family() + } + if (!class(family) %in% c("family", "family.olmm")) stop("'family' not recognized") ## check and set 'fit' if (missing(fit)) { @@ -85,15 +148,6 @@ tvcm <- function(formula, data, fit, family, if (is.function(fit)) fit <- deparse(mc$fit) } if (!fit %in% c("glm", "olmm")) stop("'fit' not recognized.") - - ## check and set 'family' - if (missing(family)) stop("no 'family'.") - if (is.character(family)) { - family <- get(family, mode = "function", envir = parent.frame()) - } else if (is.function(family)) { - family <- family() - } - if (!class(family) %in% c("family", "family.olmm")) stop("'family' not recognized") if (fit != "olmm") control$estfun <- NULL ## set formulas @@ -106,7 +160,8 @@ tvcm <- function(formula, data, fit, family, env <- environment(eval.parent(mc$formula)) formList <- vcrpart_formula(formula, family, env) nPart <- length(formList$vc) - + if (nPart < 1L) control$cv <- FALSE + direct <- any(sapply(formList$vc, function(x) x$direct)) if (length(direct) == 0L) direct <- FALSE control$direct <- direct @@ -140,7 +195,10 @@ tvcm <- function(formula, data, fit, family, data = quote(mf)) mce <- match.call(expand.dots = TRUE) dotargs <- setdiff(names(mce), names(mc)) - dotargs <- intersect(dotargs, names(formals(fit))) + fitargs <- + switch(fit,olmm = union(names(formals(olmm)), names(formals(olmm_control))), + glm = union(names(formals(glm)), names(formals(glm.control))), "") + dotargs <- intersect(fitargs, dotargs) dotargs <- setdiff(dotargs, names(mcall)) dotargs <- list(...)[dotargs] mcall[names(dotargs)] <- dotargs @@ -167,7 +225,8 @@ tvcm <- function(formula, data, fit, family, if (nPart > 1L) stop("coefficient constancy tests can be used only ", "if a single 'vc' term is specified.") - if (!is.null(formList$vc) && (formList$vc[[1L]]$direct & formList$fe$intercept != "none")) + if (!is.null(formList$vc) && (formList$vc[[1L]]$direct & + formList$fe$intercept != "none")) stop("if 'sctest = TRUE', searching for intercept is only possible if the", "global intercept is removed. Use something like 'formula = y ~ -1 + ...'") } @@ -177,8 +236,8 @@ tvcm <- function(formula, data, fit, family, ## set imputation in 'control' if (!inherits(model, "olmm") | inherits(model, "olmm") && - length(unique(table(model$subject))) == 1L) - control$ninpute <- 1L + length(unique(table(model$subject))) == 1L) + control$nimpute <- 1L ## specify which coefficients are considered as 'nuisance' parameters if (control$verbose && control$sctest) @@ -245,7 +304,7 @@ tvcm <- function(formula, data, fit, family, ## call cvloss tree <- eval(cvCall) if (control$verbose) - cat("\nestimated dfsplit =", format(tree$info$cv$dfsplit.hat, digits = 3), "\n") + cat("\nestimated cp =", format(tree$info$cv$cp.hat, digits = 3), "\n") } else { @@ -257,7 +316,7 @@ tvcm <- function(formula, data, fit, family, ## pruning if (control$prune && inherits(tree, "tvcm")) { if (control$verbose) cat("\n* pruning ... ") - tree <- prune(tree, dfsplit = tree$info$cv$dfsplit.hat, papply = control$papply) + tree <- prune(tree, cp = tree$info$cv$cp.hat, papply = control$papply) if (control$verbose) cat("OK") } @@ -265,6 +324,9 @@ tvcm <- function(formula, data, fit, family, cat("\n\nFitted model:\n") print(tree) } + + ## reset seed + assign(".Random.seed", oldSeed, envir=globalenv()) if (control$verbose) cat("* computations finished, return object\n") @@ -272,56 +334,63 @@ tvcm <- function(formula, data, fit, family, return(tree) } - -tvcm_control <- function(lossfun = neglogLik2, - maxstep = Inf, maxwidth = Inf, - minsize = 30, maxdepth = Inf, - dfpar = 2.0, dfsplit = 0.0, - maxoverstep = ifelse(sctest, Inf, 0), +tvcm_control <- function(minsize = 30, mindev = ifelse(sctest, 0.0, 2.0), sctest = FALSE, alpha = 0.05, bonferroni = TRUE, - trim = 0.1, estfun = list(), - maxfacsplit = 5L, maxordsplit = 10, maxnumsplit = 10, + trim = 0.1, estfun.args = list(), nimpute = 5, + maxnomsplit = 5, maxordsplit = 9, maxnumsplit = 9, + maxstep = 1e3, maxwidth = 1e9, maxdepth = 1e9, + lossfun = neglogLik2, ooblossfun = NULL, + cp = 0.0, dfpar = 0.0, dfsplit = 1.0, cv = !sctest, folds = folds_control("kfold", 5), - prune = cv, keeploss = FALSE, papply = mclapply, - verbose = FALSE, ...) { + prune = cv, papply = mclapply, papply.args = list(), + center = TRUE, seed = NULL, verbose = FALSE, ...) { mc <- match.call() ## check available arguments - stopifnot(is.function(lossfun)) - stopifnot(is.numeric(maxstep) && length(maxstep) == 1L && maxstep >= 0L) - stopifnot(is.numeric(maxwidth) && all(maxwidth > 0L)) stopifnot(is.null(minsize) | (is.numeric(minsize) && all(minsize > 0))) - stopifnot(is.numeric(maxdepth) && all(maxdepth >= 0)) - stopifnot(is.numeric(dfpar) && length(dfpar) == 1L) - stopifnot(is.numeric(dfsplit) && length(dfsplit) == 1L) - stopifnot(is.numeric(maxoverstep) && length(maxoverstep) == 1L) + stopifnot(is.numeric(mindev) && length(mindev) == 1L) + stopifnot(is.logical(sctest) && length(sctest) == 1L) - if (length(alpha) != 1L) stopifnot(is.numeric(alpha) && length(alpha) == 1L && alpha >= 0.0 && alpha <= 1.0) stopifnot(is.logical(bonferroni) && length(bonferroni) == 1L) + stopifnot(is.numeric(trim) && length(trim) == 1L && trim >= 0.0 & trim < 0.5) - stopifnot(is.list(estfun)) - stopifnot(is.numeric(maxfacsplit) && length(maxfacsplit) == 1L && maxfacsplit > 1L) + stopifnot(is.list(estfun.args)) + stopifnot(is.numeric(nimpute) && length(nimpute) == 1L && nimpute > 0) + nimpute <- max(1.0, round(nimpute)) + + stopifnot(is.numeric(maxnomsplit) && length(maxnomsplit) == 1L && maxnomsplit > 1L) stopifnot(is.numeric(maxordsplit) && length(maxordsplit) == 1L && maxordsplit > 1L) stopifnot(is.numeric(maxnumsplit) && length(maxnumsplit) == 1L && maxnumsplit > 1L) + + stopifnot(is.numeric(maxstep) && length(maxstep) == 1L && maxstep >= 0L) + stopifnot(is.numeric(maxwidth) && all(maxwidth > 0L)) + stopifnot(is.numeric(maxdepth) && all(maxdepth >= 0)) + + stopifnot(is.function(lossfun)) + stopifnot(is.null(ooblossfun) | is.function(ooblossfun)) + + stopifnot(is.numeric(cp) && length(cp) == 1L) + stopifnot(is.numeric(dfpar) && length(dfpar) == 1L) + stopifnot(is.numeric(dfsplit) && length(dfsplit) == 1L) + stopifnot(is.logical(cv) && length(cv) == 1L) stopifnot(inherits(folds, "folds")) + stopifnot(is.logical(prune) && length(prune) == 1L) if (!cv & prune) stop("'prune = TRUE' requires 'cv = TRUE'") - stopifnot((is.logical(keeploss) | is.numeric(keeploss)) && - length(keeploss) == 1L) - keeploss <- as.numeric(keeploss) - if (is.numeric(verbose)) verbose <- as.logical(verbose) + + stopifnot(is.logical(center) && length(center) == 1L) stopifnot(is.logical(verbose) && length(verbose) == 1L) - - ## check hidden arguments - ptry <- ifelse(is.null(list(...)$ptry), Inf, list(...)$ptry) - stopifnot(is.numeric(ptry) && length(ptry) == 1L && ptry > 0) - ntry <- if (is.null(list(...)$ntry)) Inf else list(...)$ntry - stopifnot(is.numeric(ntry) && all(ntry > 0)) - vtry <- if (is.null(list(...)$vtry)) Inf else list(...)$vtry - stopifnot(is.numeric(vtry) && all(vtry > 0)) + ## set the default parameters for 'gefp.estfun' calls + estfun.args <- appendDefArgs(estfun.args, list(predecor = TRUE, + nuisance = NULL, + silent = FALSE)) + if (!is.null(estfun.args$level) && estfun.args$level != "observation") + warning("'level' argument for 'estfun' is set to 'observation'") + estfun.args$level <- "observation" + ## check and set 'papply' stopifnot(is.character(papply) | is.function(papply)) if (is.function(papply)) { @@ -331,51 +400,62 @@ tvcm_control <- function(lossfun = neglogLik2, papply <- deparse(formals(tvcm_control)$papply) } } + stopifnot(is.list(papply.args)) + + ## check hidden arguments + ptry <- ifelse(is.null(list(...)$ptry), Inf, list(...)$ptry) + stopifnot(is.numeric(ptry) && length(ptry) == 1L && ptry > 0) + ntry <- if (is.null(list(...)$ntry)) Inf else list(...)$ntry + stopifnot(is.numeric(ntry) && all(ntry > 0)) + vtry <- if (is.null(list(...)$vtry)) Inf else list(...)$vtry + stopifnot(is.numeric(vtry) && all(vtry > 0)) - ## set the default parameters for 'gefp.estfun' calls - estfun <- appendDefArgs(estfun, list(predecor = TRUE, - nuisance = NULL, - silent = FALSE)) - if (!is.null(estfun$level) && estfun$level != "observation") - warning("'level' argument for 'estfun' is set to 'observation'") - estfun$level <- "observation" - ## ensure backward compability if ("maxevalsplit" %in% names(list(...))) maxnumsplit <- list(...)$maxevalsplit if ("minbucket" %in% names(list(...))) minsize <- list(...)$minbucket + + functional.factor <- ifelse("functional.factor" %in% names(list(...)), + list(...)$functional.factor, "LMuo") + functional.ordered <- ifelse("functional.ordered" %in% names(list(...)), + list(...)$functional.ordered, "LMuo") + functional.numeric <- ifelse("functional.numeric" %in% names(list(...)), + list(...)$functional.numeric, "supLM") ## create a list of parameters of class 'tvcm_control' return(structure( - appendDefArgs( - list(...), - list(lossfun = lossfun, - maxstep = maxstep, - maxwidth = maxwidth, - minsize = minsize, - maxdepth = maxdepth, - dfpar = dfpar, - dfsplit = dfsplit, - maxoverstep = maxoverstep, - ptry = ptry, - ntry = ntry, - vtry = vtry, - trim = trim, - sctest = sctest, - alpha = alpha, - bonferroni = bonferroni, - estfun = estfun, - maxfacsplit = maxfacsplit, - maxordsplit = maxordsplit, - maxnumsplit = maxnumsplit, - cv = cv, - folds = folds, - prune = prune, - keeploss = keeploss, - papply = papply, - verbose = verbose, - parm = NULL, intercept = NULL, - functional.factor = "LMuo", - functional.ordered = "LMuo", - functional.numeric = "supLM")), + list(minsize = minsize, + mindev = mindev, + sctest = sctest, + alpha = alpha, + bonferroni = bonferroni, + trim = trim, + estfun.args = estfun.args, + nimpute = nimpute, + maxnomsplit = as.integer(maxnomsplit), + maxordsplit = as.integer(maxordsplit), + maxnumsplit = as.integer(maxnumsplit), + maxstep = as.integer(maxstep), + maxwidth = as.integer(maxwidth), + maxdepth = as.integer(maxdepth), + lossfun = lossfun, + ooblossfun = ooblossfun, + cp = cp, + dfpar = dfpar, + dfsplit = dfsplit, + cv = cv, + folds = folds, + prune = prune, + papply = papply, + papply.args = papply.args, + center = center, + verbose = verbose, + ptry = ptry, + ntry = ntry, + vtry = vtry, + parm = NULL, intercept = NULL, + seed = seed, + functional.factor = "LMuo", + functional.ordered = "LMuo", + functional.numeric = "supLM"), class = "tvcm_control")) } diff --git a/R/utils.R b/R/utils.R index 8422923..40cb2b2 100644 --- a/R/utils.R +++ b/R/utils.R @@ -513,7 +513,7 @@ vcrpart_formula <- function(formula, family = cumulative(), types <- c("fe", "vc", "re") terms <- terms(formula, specials = types, keep.order = TRUE) - yName <- rownames(attr(terms(formula), "factors")) + yName <- deparse((formula)[[2L]]) termLabs <- attr(terms, "term.labels") termFac <- attr(terms, "factors") type <- rep.int("NA", length(termLabs)) diff --git a/data/PL.RData b/data/PL.RData new file mode 100644 index 0000000000000000000000000000000000000000..61a3de08f625815debd2ca79c5d8a564470f9d2a GIT binary patch literal 223752 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

cb{J^n-e9?3gb#gW^=*DdsO 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 #define CALLDEF(name, n) {#name, (DL_FUNC) &name, n} @@ -10,7 +11,7 @@ static R_CallMethodDef CallEntries[] = { CALLDEF(olmm_update_u, 1), CALLDEF(olmm_pred_marg, 5), CALLDEF(vcrpart_duplicate, 1), - CALLDEF(tvcm_nomsplits, 4), + CALLDEF(tvcm_nomsplits, 1), {NULL, NULL, 0} }; diff --git a/src/olmm.c b/src/olmm.c index 633254f..3b68422 100644 --- a/src/olmm.c +++ b/src/olmm.c @@ -1,4 +1,5 @@ #include "olmm.h" +#include "utils.h" #include #include #include @@ -103,17 +104,6 @@ double olmm_GLink(double x, int link) { } } -SEXP getListElement(SEXP list, const char *str) { - SEXP elmt = R_NilValue, - names = getAttrib(list, R_NamesSymbol); - for (int i = 0; i < length(list); i++) - if(strcmp(CHAR(STRING_ELT(names, i)), str) == 0) { - elmt = VECTOR_ELT(list, i); - break; - } - return elmt; -} - /** * --------------------------------------------------------- * Store a new parameter vector in a olmm object @@ -204,15 +194,22 @@ SEXP olmm_update_marg(SEXP x, SEXP par) { R_CheckStack(); /* numeric valued objects */ - double *X = X_SLOT(x), *W = W_SLOT(x), - *weights_obs = WEIGHTS_SLOT(x), *weights_sbj = WEIGHTSSBJ_SLOT(x), - *offset = OFFSET_SLOT(x), *eta = ETA_SLOT(x), - *fixef = FIXEF_SLOT(x), *ranefCholFac = RANEFCHOLFAC_SLOT(x), - *logLik_sbj = LOGLIKSBJ_SLOT(x), *logLik = LOGLIK_SLOT(x), - *score_obs = SCOREOBS_SLOT(x), *score_sbj = SCORESBJ_SLOT(x), + double *X = X_SLOT(x), + *W = W_SLOT(x), + *weights_obs = WEIGHTS_SLOT(x), + *weights_sbj = WEIGHTSSBJ_SLOT(x), + *offset = OFFSET_SLOT(x), + *eta = ETA_SLOT(x), + *fixef = FIXEF_SLOT(x), + *ranefCholFac = RANEFCHOLFAC_SLOT(x), + *logLik_sbj = LOGLIKSBJ_SLOT(x), + *logLik = LOGLIK_SLOT(x), + *score_obs = SCOREOBS_SLOT(x), + *score_sbj = SCORESBJ_SLOT(x), *score = SCORE_SLOT(x), *info = INFO_SLOT(x), - *ghw = GHW_SLOT(x), *ghx = GHX_SLOT(x), + *ghw = GHW_SLOT(x), + *ghx = GHX_SLOT(x), *ranefElMat = RANEFELMAT_SLOT(x); R_CheckStack(); diff --git a/src/tvcm.c b/src/tvcm.c new file mode 100644 index 0000000..5a3f63e --- /dev/null +++ b/src/tvcm.c @@ -0,0 +1,43 @@ +#include "tvcm.h" + +/** + * --------------------------------------------------------- + * Computes the split matrix for nominal variables for + * the 'tvcm' algorithm. + * + * @param nLevs total number of categories. + * @param xdlev integer vector levels observed have a + 1 and the other a 0 + * @param nCuts number of possible splits with the categories + * observed in the current node. + * + * @return an integer vector of length nCuts x nLevs to be used + * to construct the split matrix. + * --------------------------------------------------------- + */ + +SEXP tvcm_nomsplits(SEXP zdlev) { + zdlev = PROTECT(coerceVector(zdlev, INTSXP)); + int *rzdlev = INTEGER(zdlev); + int nLevs = length(zdlev), nLevsD = 0, nCuts = 1; + for (int l = 0; l < nLevs; l++) nLevsD += rzdlev[l]; + for (int l = 0; l < (nLevsD - 1); l++) nCuts *= 2; + nCuts = nCuts - 1; + SEXP indz = PROTECT(allocMatrix(INTSXP, nCuts, nLevs)); + int *rindz = INTEGER(indz); + for (int i = 0; i < (nLevs * nCuts); i++) rindz[i] = 0; + int ii = 0; + if (nCuts > 0) { + for (int i = 0; i < nCuts; i++) { + ii = i + 1; + for (int l = 0; l < nLevs; l++) { + if (rzdlev[l] > 0) { + rindz[i + nCuts * l] = ii % 2; + ii = ii / 2; + } + } + } + } + UNPROTECT(2); + return indz; +} diff --git a/src/tvcm.h b/src/tvcm.h new file mode 100644 index 0000000..df84c3c --- /dev/null +++ b/src/tvcm.h @@ -0,0 +1,9 @@ +#ifndef VCRPART_TVCM_H +#define VCRPART_TVCM_H + +#include +#include + +SEXP tvcm_nomsplits(SEXP zdlev); + +#endif diff --git a/src/utils.c b/src/utils.c index 0af1c4c..516a8f6 100644 --- a/src/utils.c +++ b/src/utils.c @@ -19,42 +19,22 @@ SEXP vcrpart_duplicate(SEXP x) { /** * --------------------------------------------------------- - * Computes the split matrix for nominal variables for - * the 'tvcm' alorithm. + * Get list elements * - * @param xlev integer vector, all observed categories. - * @param nl total number of categories. - * @param xdlev integer vector, the levels observed. - * in the current node. - * @param nld number of categories observed in the current - * node. - * @param mi number of possible splits with the categories - * observed in the current node. + * @param list a list. + * @param str a character string. * - * @return an integer vector of length nld * mi to be used - * to construct the split matrix. + * @return a list element * --------------------------------------------------------- */ -SEXP tvcm_nomsplits(SEXP nl, SEXP xdlev, SEXP nld, SEXP mi) { - - int *xdlev_c = INTEGER(xdlev); - const int nl_c = INTEGER(nl)[0], - nld_c = INTEGER(nld)[0], - mi_c = INTEGER(mi)[0]; - - SEXP indx = PROTECT(allocVector(INTSXP, nl_c * mi_c)); - for (int i = 0; i < (nl_c * mi_c); i++) INTEGER(indx)[i] = 0; - int ii = 0; - if (mi_c > 0) { - for (int i = 0; i < mi_c; i++) { - ii = i + 1; - for (int l = 0; l < nld_c; l++) { - INTEGER(indx)[i * nl_c + xdlev_c[l] - 1] = ii % 2; - ii = ii / 2; - } +SEXP getListElement(SEXP list, const char *str) { + SEXP elmt = R_NilValue, + names = getAttrib(list, R_NamesSymbol); + for (int i = 0; i < length(list); i++) + if(strcmp(CHAR(STRING_ELT(names, i)), str) == 0) { + elmt = VECTOR_ELT(list, i); + break; } - } - UNPROTECT(1); - return indx; + return elmt; } diff --git a/src/utils.h b/src/utils.h index 2c2184a..832e326 100644 --- a/src/utils.h +++ b/src/utils.h @@ -5,6 +5,6 @@ #include SEXP vcrpart_duplicate(SEXP x); -SEXP tvcm_nomsplits(SEXP nl, SEXP xdlev, SEXP nld, SEXP mi); +SEXP getListElement(SEXP list, const char *str); #endif

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