functions.R 40.2 KB
Newer Older
Armin Rauschenberger's avatar
Armin Rauschenberger committed
1

Armin Rauschenberger's avatar
Armin Rauschenberger committed
2 3
# import unexported functions:
# FUNCTION <- get("FUNCTION",envir=asNamespace("PACKAGE"))
Armin Rauschenberger's avatar
Armin Rauschenberger committed
4

Armin Rauschenberger's avatar
Armin Rauschenberger committed
5
#--- Main function -------------------------------------------------------------
Armin Rauschenberger's avatar
Armin Rauschenberger committed
6

Armin Rauschenberger's avatar
Armin Rauschenberger committed
7 8
#' @export
#' @title
Armin Rauschenberger's avatar
Armin Rauschenberger committed
9
#' Multivariate Elastic Net Regression
Armin Rauschenberger's avatar
Armin Rauschenberger committed
10 11
#' 
#' @description
Armin Rauschenberger's avatar
Armin Rauschenberger committed
12 13 14 15 16 17 18
#' Implements multivariate elastic net regression.
#'  
#' @param Y
#' outputs\strong{:}
#' numeric matrix with \eqn{n} rows (samples)
#' and \eqn{q} columns (variables),
#' with positive correlation (see details)
Armin Rauschenberger's avatar
Armin Rauschenberger committed
19 20
#' 
#' @param X
Armin Rauschenberger's avatar
Armin Rauschenberger committed
21
#' inputs\strong{:}
Armin Rauschenberger's avatar
Armin Rauschenberger committed
22
#' numeric matrix with \eqn{n} rows (samples)
Armin Rauschenberger's avatar
Armin Rauschenberger committed
23
#' and \eqn{p} columns (variables)
Armin Rauschenberger's avatar
Armin Rauschenberger committed
24 25 26 27 28 29 30 31 32
#'
#' @param family
#' distribution\strong{:}
#' vector of length \eqn{1} or \eqn{q} with entries
#' \code{"gaussian"}, \code{"binomial"} or \code{"poisson"}
#'
#' @param nfolds
#' number of folds
#'
Armin Rauschenberger's avatar
Armin Rauschenberger committed
33 34
#' @param foldid
#' fold identifiers\strong{:}
Armin Rauschenberger's avatar
Armin Rauschenberger committed
35
#' vector of length \eqn{n} with entries between \eqn{1} and \code{nfolds};
Armin Rauschenberger's avatar
Armin Rauschenberger committed
36
#' or \code{NULL} (balance)
Armin Rauschenberger's avatar
Armin Rauschenberger committed
37
#' 
Armin Rauschenberger's avatar
Armin Rauschenberger committed
38
#' @param type.measure
Armin Rauschenberger's avatar
Armin Rauschenberger committed
39 40 41 42 43 44 45 46
#' loss function\strong{:}
#' vector of length \eqn{1} or \eqn{q} with entries
#' \code{"deviance"}, \code{"class"}, \code{"mse"} or \code{"mae"}
#' (see \code{\link[glmnet]{cv.glmnet}})
#'
#' @param alpha.base
#' elastic net mixing parameter for base learners\strong{:}
#' numeric between \eqn{0} (ridge) and \eqn{1} (lasso)
Armin Rauschenberger's avatar
Armin Rauschenberger committed
47
#' 
Armin Rauschenberger's avatar
Armin Rauschenberger committed
48 49
#' @param alpha.meta
#' elastic net mixing parameter for meta learner\strong{:}
Armin Rauschenberger's avatar
Armin Rauschenberger committed
50
#' numeric between \eqn{0} (ridge) and \eqn{1} (lasso)
Armin Rauschenberger's avatar
Armin Rauschenberger committed
51
#' 
Armin Rauschenberger's avatar
Armin Rauschenberger committed
52 53 54
#' @param ...
#' further arguments passed to \code{\link[glmnet]{glmnet}}
#' 
Armin Rauschenberger's avatar
Armin Rauschenberger committed
55
#' @references 
Armin Rauschenberger's avatar
Armin Rauschenberger committed
56
#' Armin Rauschenberger, Enrico Glaab (2019)
Armin Rauschenberger's avatar
Armin Rauschenberger committed
57 58
#' "joinet: predicting correlated outcomes jointly
#' to improve clinical prognosis"
Armin Rauschenberger's avatar
Armin Rauschenberger committed
59
#' \emph{Manuscript in preparation}.
Armin Rauschenberger's avatar
Armin Rauschenberger committed
60 61
#' 
#' @details
Armin Rauschenberger's avatar
Armin Rauschenberger committed
62
#' \strong{correlation:}
Armin Rauschenberger's avatar
Armin Rauschenberger committed
63 64
#' The \eqn{q} outcomes should be positively correlated.
#' Avoid negative correlations by changing the sign of the variable.
Armin Rauschenberger's avatar
Armin Rauschenberger committed
65
#' 
Armin Rauschenberger's avatar
Armin Rauschenberger committed
66
#' \strong{elastic net:}
Armin Rauschenberger's avatar
Armin Rauschenberger committed
67 68
#' \code{alpha.base} controls input-output effects,
#' \code{alpha.meta} controls output-output effects;
Armin Rauschenberger's avatar
Armin Rauschenberger committed
69 70 71 72 73 74 75 76 77 78 79
#' lasso renders sparse models (\code{alpha}\eqn{=1}),
#' ridge renders dense models (\code{alpha}\eqn{=0})
#' 
#' @return
#' This function returns an object of class \code{joinet}.
#' Available methods include
#' \code{\link[=predict.joinet]{predict}},
#' \code{\link[=coef.joinet]{coef}},
#' and \code{\link[=weights.joinet]{weights}}.
#' The slots \code{base} and \code{meta} each contain
#' \eqn{q} \code{\link[glmnet]{cv.glmnet}}-like objects.
Armin Rauschenberger's avatar
Armin Rauschenberger committed
80
#' 
Armin Rauschenberger's avatar
Armin Rauschenberger committed
81
#' @seealso
Armin Rauschenberger's avatar
Armin Rauschenberger committed
82
#' \code{\link{cv.joinet}}, vignette
Armin Rauschenberger's avatar
Armin Rauschenberger committed
83
#' 
Armin Rauschenberger's avatar
Armin Rauschenberger committed
84
#' @examples
Armin Rauschenberger's avatar
Armin Rauschenberger committed
85
#' n <- 50; p <- 100; q <- 3
Armin Rauschenberger's avatar
Armin Rauschenberger committed
86
#' X <- matrix(rnorm(n*p),nrow=n,ncol=p)
Armin Rauschenberger's avatar
Armin Rauschenberger committed
87
#' Y <- replicate(n=q,expr=rnorm(n=n,mean=rowSums(X[,1:5])))
Armin Rauschenberger's avatar
Armin Rauschenberger committed
88
#' object <- joinet(Y=Y,X=X)
Armin Rauschenberger's avatar
Armin Rauschenberger committed
89
#' 
Armin Rauschenberger's avatar
Armin Rauschenberger committed
90 91 92
#' \dontrun{
#' browseVignettes("joinet") # further examples}
#' 
Armin Rauschenberger's avatar
Armin Rauschenberger committed
93
joinet <- function(Y,X,family="gaussian",nfolds=10,foldid=NULL,type.measure="deviance",alpha.base=1,alpha.meta=1,...){
Armin Rauschenberger's avatar
Armin Rauschenberger committed
94
  
Armin Rauschenberger's avatar
Armin Rauschenberger committed
95
  #--- temporary ---
Armin Rauschenberger's avatar
Armin Rauschenberger committed
96
  # family <- "gaussian"; nfolds <- 10; foldid <- NULL; type.measure <- "deviance"
Armin Rauschenberger's avatar
Armin Rauschenberger committed
97
  
Armin Rauschenberger's avatar
Armin Rauschenberger committed
98
  #--- checks ---
Armin Rauschenberger's avatar
Armin Rauschenberger committed
99 100 101
  Y <- as.matrix(Y)
  X <- as.matrix(X)
  
Armin Rauschenberger's avatar
Armin Rauschenberger committed
102
  cornet:::.check(x=Y,type="matrix",miss=TRUE)
Armin Rauschenberger's avatar
Armin Rauschenberger committed
103 104 105 106 107 108 109 110 111 112 113 114 115
  
  ### trial start ###
  for(i in 1:(ncol(Y)-1)){
    for(j in i:ncol(Y)){
      cor <- stats::cor.test(Y[,i],Y[,j],use="pairwise.complete.obs")
      if(cor$statistic<0 & cor$p.value<0.05){
        warning(paste("Columns",i,"and",j,"are negatively correlated."))
      }
    }
  }
  ### trial end ###
  
  #if(any(stats::cor(Y,use="pairwise.complete.obs")<0,na.rm=TRUE)){warning("Negative correlation!",call.=FALSE)}
Armin Rauschenberger's avatar
Armin Rauschenberger committed
116
  cornet:::.check(x=X,type="matrix")
Armin Rauschenberger's avatar
Armin Rauschenberger committed
117
  #cornet:::.check(x=family,type="vector",values=c("gaussian","binomial","poisson"))
Armin Rauschenberger's avatar
Armin Rauschenberger committed
118 119 120 121 122 123 124 125
  if(nrow(Y)!=nrow(X)){stop("Contradictory sample size.",call.=FALSE)}
  cornet:::.check(x=nfolds,type="scalar",min=3)
  cornet:::.check(x=foldid,type="vector",values=seq_len(nfolds),null=TRUE)
  cornet:::.check(x=type.measure,type="string",values=c("deviance","class","mse","mae")) # not auc (min/max confusion)
  cornet:::.check(x=alpha.base,type="scalar",min=0,max=1)
  cornet:::.check(x=alpha.meta,type="scalar",min=0,max=1)
  if(!is.null(c(list(...)$lower.limits,list(...)$upper.limits))){
    stop("Reserved arguments \"lower.limits\" and \"upper.limits\".",call.=FALSE)
Armin Rauschenberger's avatar
Armin Rauschenberger committed
126 127
  }
  
Armin Rauschenberger's avatar
Armin Rauschenberger committed
128 129 130 131 132 133 134 135 136 137
  #--- dimensionality ---
  n <- nrow(Y)
  q <- ncol(Y)
  p <- ncol(X)
  
  #--- family ---
  if(length(family)==1){
    family <- rep(family,times=q)
  } else if(length(family)!=q){
    stop("Invalid argument family",call.=FALSE)
Armin Rauschenberger's avatar
Armin Rauschenberger committed
138
  }
Armin Rauschenberger's avatar
Armin Rauschenberger committed
139
  
Armin Rauschenberger's avatar
Armin Rauschenberger committed
140 141 142 143
  #--- fold identifiers ---
  # provide foldid as matrix?
  if(is.null(foldid)){
    foldid <- palasso:::.folds(y=Y[,1],nfolds=nfolds) # temporary Y[,1]
Armin Rauschenberger's avatar
Armin Rauschenberger committed
144
  } else {
Armin Rauschenberger's avatar
Armin Rauschenberger committed
145
    nfolds <- length(unique(foldid))
Armin Rauschenberger's avatar
Armin Rauschenberger committed
146 147
  }
  
Armin Rauschenberger's avatar
Armin Rauschenberger committed
148 149 150 151 152 153 154 155 156 157
  #--- full fit ---
  nlambda <- numeric()
  base <- lapply(seq_len(q),function(x) list())
  for(i in seq_len(q)){
    cond <- !is.na(Y[,i])
    #if(sum(cond)==0){nlambda[i] <- 0; next}
    base[[i]]$glmnet.fit <- glmnet::glmnet(y=Y[cond,i],x=X[cond,],family=family[i],alpha=alpha.base,...) # ellipsis
    base[[i]]$lambda <- base[[i]]$glmnet.fit$lambda
    nlambda[i] <- length(base[[i]]$glmnet.fit$lambda)
  }
Armin Rauschenberger's avatar
Armin Rauschenberger committed
158
  
Armin Rauschenberger's avatar
Armin Rauschenberger committed
159 160 161 162
  #--- predictions ---
  link <- list()
  for(i in seq_len(q)){
    link[[i]] <- matrix(data=NA,nrow=n,ncol=nlambda[i])
Armin Rauschenberger's avatar
Armin Rauschenberger committed
163
  }
Armin Rauschenberger's avatar
Armin Rauschenberger committed
164 165
  
  #--- base cross-validation ---
Armin Rauschenberger's avatar
Armin Rauschenberger committed
166
  for(k in seq_len(nfolds)){
Armin Rauschenberger's avatar
Armin Rauschenberger committed
167 168
    Y0 <- Y[foldid!=k,,drop=FALSE]
    Y1 <- Y[foldid==k,,drop=FALSE]
Armin Rauschenberger's avatar
Armin Rauschenberger committed
169 170
    X0 <- X[foldid!=k,,drop=FALSE]
    X1 <- X[foldid==k,,drop=FALSE]
Armin Rauschenberger's avatar
Armin Rauschenberger committed
171 172 173 174 175 176 177
    for(i in seq_len(q)){
      cond <- !is.na(Y0[,i])
      #if(sum(cond)==0){next}
      object <- glmnet::glmnet(y=Y0[cond,i],x=X0[cond,],family=family[i],alpha=alpha.base,...) # ellipsis
      temp <- stats::predict(object=object,newx=X1,type="link",
                             s=base[[i]]$glmnet.fit$lambda)
      link[[i]][foldid==k,seq_len(ncol(temp))] <- temp
Armin Rauschenberger's avatar
Armin Rauschenberger committed
178
    }
Armin Rauschenberger's avatar
Armin Rauschenberger committed
179 180
  }
  
Armin Rauschenberger's avatar
Armin Rauschenberger committed
181 182 183 184 185 186 187
  #--- tune base lambdas ---
  for(i in seq_len(q)){
    fit <- .mean.function(link[[i]],family=family[i])
    cond <- !is.na(Y[,i])
    base[[i]]$cvm <- palasso:::.loss(y=Y[cond,i],fit=fit[cond,],
                                     family=family[i],type.measure=type.measure)[[1]]
    base[[i]]$lambda.min <- base[[i]]$lambda[which.min(base[[i]]$cvm)]
Armin Rauschenberger's avatar
Armin Rauschenberger committed
188
    class(base[[i]]) <- "cv.glmnet" # trial 2020-01-10
Armin Rauschenberger's avatar
Armin Rauschenberger committed
189
  }
Armin Rauschenberger's avatar
Armin Rauschenberger committed
190
  
Armin Rauschenberger's avatar
Armin Rauschenberger committed
191 192 193 194 195
  #--- predictions ---
  hat <- matrix(NA,nrow=n,ncol=q)
  for(i in seq_len(q)){
    hat[,i] <- link[[i]][,base[[i]]$lambda==base[[i]]$lambda.min]
  }
Armin Rauschenberger's avatar
Armin Rauschenberger committed
196
  
Armin Rauschenberger's avatar
Armin Rauschenberger committed
197 198 199 200 201 202 203 204 205 206 207 208 209
  #--- meta cross-validation ---
  meta <- list()
  for(i in seq_len(q)){
    cond <- !is.na(Y[,i])
    meta[[i]] <- glmnet::cv.glmnet(y=Y[cond,i],x=hat[cond,],
                                   lower.limits=0, # important: 0
                                   upper.limits=Inf, # important: Inf
                                   foldid=foldid[cond],
                                   family=family[i],
                                   type.measure=type.measure,
                                   intercept=TRUE, # with intercept
                                   alpha=alpha.meta,...) # ellipsis
    # consider trying different alpha.meta and selecting best one
Armin Rauschenberger's avatar
Armin Rauschenberger committed
210 211
  }
  
Armin Rauschenberger's avatar
Armin Rauschenberger committed
212
  #--- return ---
Armin Rauschenberger's avatar
Armin Rauschenberger committed
213 214 215
  names(base) <- names(meta) <- paste0("y",seq_len(q))
  info <- data.frame(q=q,p=p,family=family,type.measure=type.measure)
  list <- list(base=base,meta=meta,info=info)
Armin Rauschenberger's avatar
Armin Rauschenberger committed
216
  class(list) <- "joinet"
Armin Rauschenberger's avatar
Armin Rauschenberger committed
217
  return(list)
Armin Rauschenberger's avatar
Armin Rauschenberger committed
218 219
}

Armin Rauschenberger's avatar
Armin Rauschenberger committed
220
.mean.function <- function(x,family){
Armin Rauschenberger's avatar
Armin Rauschenberger committed
221
  if(family %in% c("gaussian","cox")){
Armin Rauschenberger's avatar
Armin Rauschenberger committed
222 223 224 225 226 227 228 229 230
    return(x)
  } else if(family=="binomial"){
    return(1/(1+exp(-x)))
  } else if(family=="poisson"){
    return(exp(x))
  } else {
    stop("Family not implemented.",call.=FALSE)
  }
}
Armin Rauschenberger's avatar
Armin Rauschenberger committed
231

Armin Rauschenberger's avatar
Armin Rauschenberger committed
232
.link.function <- function(x,family){
Armin Rauschenberger's avatar
Armin Rauschenberger committed
233
  if(family %in% c("gaussian","cox")){
Armin Rauschenberger's avatar
Armin Rauschenberger committed
234 235
    return(x)
  } else if(family=="binomial"){
Armin Rauschenberger's avatar
Armin Rauschenberger committed
236 237
    if(any(x<0|x>1)){stop("Invalid!",call.=FALSE)}
    return(log(x/(1-x)))
Armin Rauschenberger's avatar
Armin Rauschenberger committed
238
  } else if(family=="poisson"){
Armin Rauschenberger's avatar
Armin Rauschenberger committed
239
    if(any(x<0)){stop("Invalid!",call.=FALSE)}
Armin Rauschenberger's avatar
Armin Rauschenberger committed
240 241 242 243
    return(log(x))
  } else {
    stop("Family not implemented.",call.=FALSE)
  }
Armin Rauschenberger's avatar
Armin Rauschenberger committed
244 245
}

Armin Rauschenberger's avatar
Armin Rauschenberger committed
246
#--- Methods for class "joinet" -----------------------------------------------
Armin Rauschenberger's avatar
Armin Rauschenberger committed
247

Armin Rauschenberger's avatar
Armin Rauschenberger committed
248 249
#' @export
#' @title
Armin Rauschenberger's avatar
Armin Rauschenberger committed
250
#' Make Predictions
Armin Rauschenberger's avatar
Armin Rauschenberger committed
251 252
#'
#' @description
Armin Rauschenberger's avatar
Armin Rauschenberger committed
253
#' Predicts outcome from features with stacked model.
Armin Rauschenberger's avatar
Armin Rauschenberger committed
254
#' 
Armin Rauschenberger's avatar
Armin Rauschenberger committed
255
#' @param object
Armin Rauschenberger's avatar
Armin Rauschenberger committed
256
#' \link[joinet]{joinet} object
Armin Rauschenberger's avatar
Armin Rauschenberger committed
257
#' 
Armin Rauschenberger's avatar
Armin Rauschenberger committed
258 259 260 261
#' @param newx
#' covariates\strong{:}
#' numeric matrix with \eqn{n} rows (samples)
#' and \eqn{p} columns (variables)
Armin Rauschenberger's avatar
Armin Rauschenberger committed
262
#' 
Armin Rauschenberger's avatar
Armin Rauschenberger committed
263 264
#' @param type
#' character "link" or "response"
Armin Rauschenberger's avatar
Armin Rauschenberger committed
265 266
#' 
#' @param ...
Armin Rauschenberger's avatar
Armin Rauschenberger committed
267
#' further arguments (not applicable)
Armin Rauschenberger's avatar
Armin Rauschenberger committed
268
#' 
Armin Rauschenberger's avatar
Armin Rauschenberger committed
269 270 271 272 273
#' @return 
#' This function returns predictions from base and meta learners.
#' The slots \code{base} and \code{meta} each contain a matrix
#' with \eqn{n} rows (samples) and \eqn{q} columns (variables).
#' 
Armin Rauschenberger's avatar
Armin Rauschenberger committed
274
#' @examples
Armin Rauschenberger's avatar
Armin Rauschenberger committed
275
#' n <- 50; p <- 100; q <- 3
Armin Rauschenberger's avatar
Armin Rauschenberger committed
276
#' X <- matrix(rnorm(n*p),nrow=n,ncol=p)
Armin Rauschenberger's avatar
Armin Rauschenberger committed
277
#' Y <- replicate(n=q,expr=rnorm(n=n,mean=rowSums(X[,1:5])))
Armin Rauschenberger's avatar
Armin Rauschenberger committed
278 279
#' Y[,1] <- 1*(Y[,1]>median(Y[,1]))
#' object <- joinet(Y=Y,X=X,family=c("binomial","gaussian","gaussian"))
Armin Rauschenberger's avatar
Armin Rauschenberger committed
280
#' predict(object,newx=X)
Armin Rauschenberger's avatar
Armin Rauschenberger committed
281
#' 
Armin Rauschenberger's avatar
Armin Rauschenberger committed
282
predict.joinet <- function(object,newx,type="response",...){
Armin Rauschenberger's avatar
Armin Rauschenberger committed
283
  if(length(list(...))!=0){warning("Ignoring argument.",call.=FALSE)}
Armin Rauschenberger's avatar
Armin Rauschenberger committed
284
  
Armin Rauschenberger's avatar
Armin Rauschenberger committed
285
  x <- object; rm(object)
Armin Rauschenberger's avatar
Armin Rauschenberger committed
286
  
Armin Rauschenberger's avatar
Armin Rauschenberger committed
287
  newx <- as.matrix(newx)
Armin Rauschenberger's avatar
Armin Rauschenberger committed
288
  cornet:::.check(x=newx,type="matrix")
Armin Rauschenberger's avatar
Armin Rauschenberger committed
289
  
Armin Rauschenberger's avatar
Armin Rauschenberger committed
290 291 292
  q <- length(x$base)
  n <- nrow(newx)
  base <- meta <- matrix(NA,nrow=n,ncol=q)
Armin Rauschenberger's avatar
Armin Rauschenberger committed
293
  
Armin Rauschenberger's avatar
Armin Rauschenberger committed
294 295
  # base learners
  for(i in seq_len(q)){
Armin Rauschenberger's avatar
Armin Rauschenberger committed
296 297
    base[,i] <- as.numeric(stats::predict(object=x$base[[i]]$glmnet.fit,newx=newx,s=x$base[[i]]$lambda.min,type="link"))
    #base[,i] <- as.numeric(glmnet:::predict.cv.glmnet(object=x$base[[i]],newx=newx,s="lambda.min",type="link"))
Armin Rauschenberger's avatar
Armin Rauschenberger committed
298 299
    # check whether fine for "binomial" family
  }
Armin Rauschenberger's avatar
Armin Rauschenberger committed
300
  
Armin Rauschenberger's avatar
Armin Rauschenberger committed
301 302 303 304 305
  # meta learners
  for(i in seq_len(q)){
    meta[,i] <- as.numeric(stats::predict(object=x$meta[[i]],
                                          newx=base,s="lambda.min",type="link"))
  }
Armin Rauschenberger's avatar
Armin Rauschenberger committed
306
  
Armin Rauschenberger's avatar
Armin Rauschenberger committed
307
  list <- list(base=base,meta=meta)
Armin Rauschenberger's avatar
Armin Rauschenberger committed
308
  
Armin Rauschenberger's avatar
Armin Rauschenberger committed
309 310 311 312 313 314 315
  if(type=="response"){
    for(i in seq_len(q)){
      base[,i] <- .mean.function(x=base[,i],family=x$info$family[i])
      meta[,i] <- .mean.function(x=meta[,i],family=x$info$family[i])
    }
  } else if(type!="link"){
    stop("Invalid type.",call.=FALSE)
Armin Rauschenberger's avatar
Armin Rauschenberger committed
316 317
  }
  
Armin Rauschenberger's avatar
Armin Rauschenberger committed
318 319 320
  list <- list(base=base,meta=meta)
  return(list)
  
Armin Rauschenberger's avatar
Armin Rauschenberger committed
321 322
}

Armin Rauschenberger's avatar
Armin Rauschenberger committed
323 324
#' @export
#' @title
Armin Rauschenberger's avatar
Armin Rauschenberger committed
325
#' Extract Coefficients
Armin Rauschenberger's avatar
Armin Rauschenberger committed
326 327
#'
#' @description
Armin Rauschenberger's avatar
Armin Rauschenberger committed
328
#' Extracts pooled coefficients.
Armin Rauschenberger's avatar
Armin Rauschenberger committed
329 330
#' (The meta learners linearly combines
#' the coefficients from the base learners.)
Armin Rauschenberger's avatar
Armin Rauschenberger committed
331
#' 
Armin Rauschenberger's avatar
Armin Rauschenberger committed
332
#' @param object
Armin Rauschenberger's avatar
Armin Rauschenberger committed
333
#' \link[joinet]{joinet} object
Armin Rauschenberger's avatar
Armin Rauschenberger committed
334 335
#' 
#' @param ...
Armin Rauschenberger's avatar
Armin Rauschenberger committed
336
#' further arguments (not applicable)
Armin Rauschenberger's avatar
Armin Rauschenberger committed
337
#' 
Armin Rauschenberger's avatar
Armin Rauschenberger committed
338 339 340 341 342 343 344
#' @return
#' This function returns the pooled coefficients.
#' The slot \code{alpha} contains the intercepts
#' in a vector of length \eqn{q},
#' and the slot \code{beta} contains the slopes
#' in a matrix with \eqn{p} rows (inputs) and \eqn{q} columns.
#' 
Armin Rauschenberger's avatar
Armin Rauschenberger committed
345
#' @examples
Armin Rauschenberger's avatar
Armin Rauschenberger committed
346
#' n <- 50; p <- 100; q <- 3
Armin Rauschenberger's avatar
Armin Rauschenberger committed
347
#' X <- matrix(rnorm(n*p),nrow=n,ncol=p)
Armin Rauschenberger's avatar
Armin Rauschenberger committed
348
#' Y <- replicate(n=q,expr=rnorm(n=n,mean=rowSums(X[,1:5])))
Armin Rauschenberger's avatar
Armin Rauschenberger committed
349
#' object <- joinet(Y=Y,X=X)
Armin Rauschenberger's avatar
Armin Rauschenberger committed
350 351
#' coef <- coef(object)
#' 
Armin Rauschenberger's avatar
Armin Rauschenberger committed
352
coef.joinet <- function(object,...){
Armin Rauschenberger's avatar
Armin Rauschenberger committed
353 354 355 356
  if(length(list(...))!=0){warning("Ignoring argument.",call.=FALSE)}
  
  # base coefficients
  base <- list()
Armin Rauschenberger's avatar
Armin Rauschenberger committed
357
  coef <- sapply(object$base,function(x) stats::coef(object=x$glmnet.fit,s=x$lambda.min))
Armin Rauschenberger's avatar
Armin Rauschenberger committed
358 359 360 361 362 363
  base$alpha <- sapply(coef,function(x) x[1,])
  base$beta <- sapply(coef,function(x) x[-1,])
  names(base$alpha) <- colnames(base$beta) <- names(object$base)
  
  # meta coefficients
  meta <- list()
Armin Rauschenberger's avatar
Armin Rauschenberger committed
364
  weights <- weights.joinet(object)
Armin Rauschenberger's avatar
Armin Rauschenberger committed
365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384
  meta$alpha <- weights[1,]
  meta$beta <- weights[-1,]
  
  # pooled coefficients
  pool <- list()
  pool$alpha <- meta$alpha + base$alpha %*% meta$beta
  pool$beta <- base$beta %*% meta$beta
  
  # q <- unique(object$info$q)
  # p <- unique(object$info$p)
  # pool$alpha <- rep(NA,times=q)
  # for(i in seq_len(q)){
  #   pool$alpha[i] <-  meta$alpha[i] + sum(meta$beta[,i] * base$alpha)
  # }
  # pool$beta <- matrix(NA,nrow=p,ncol=q)
  # for(i in seq_len(p)){
  #   for(j in seq_len(q)){
  #     pool$beta[i,j] <-  sum(meta$beta[,j] * base$beta[i,])
  #   }
  # }
Armin Rauschenberger's avatar
Armin Rauschenberger committed
385
  
Armin Rauschenberger's avatar
Armin Rauschenberger committed
386
  return(pool)
Armin Rauschenberger's avatar
Armin Rauschenberger committed
387 388
}

Armin Rauschenberger's avatar
Armin Rauschenberger committed
389 390
#' @export
#' @importFrom stats weights
Armin Rauschenberger's avatar
Armin Rauschenberger committed
391
#' @title
Armin Rauschenberger's avatar
Armin Rauschenberger committed
392
#' Extract Weights
Armin Rauschenberger's avatar
Armin Rauschenberger committed
393 394
#'
#' @description
Armin Rauschenberger's avatar
Armin Rauschenberger committed
395 396
#' Extracts coefficients from the meta learner,
#' i.e. the weights for the base learners.
Armin Rauschenberger's avatar
Armin Rauschenberger committed
397
#' 
Armin Rauschenberger's avatar
Armin Rauschenberger committed
398
#' @param object
Armin Rauschenberger's avatar
Armin Rauschenberger committed
399
#' \link[joinet]{joinet} object
Armin Rauschenberger's avatar
Armin Rauschenberger committed
400
#' 
Armin Rauschenberger's avatar
Armin Rauschenberger committed
401 402
#' @param ...
#' further arguments (not applicable)
Armin Rauschenberger's avatar
Armin Rauschenberger committed
403
#' 
Armin Rauschenberger's avatar
Armin Rauschenberger committed
404 405 406 407 408 409 410 411
#' @return
#' This function returns a matrix with
#' \eqn{1+q} rows and \eqn{q} columns.
#' The first row contains the intercepts,
#' and the other rows contain the slopes,
#' which are the effects of the outcomes
#' in the row on the outcomes in the column.
#' 
Armin Rauschenberger's avatar
Armin Rauschenberger committed
412
#' @examples
Armin Rauschenberger's avatar
Armin Rauschenberger committed
413
#' n <- 50; p <- 100; q <- 3
Armin Rauschenberger's avatar
Armin Rauschenberger committed
414
#' X <- matrix(rnorm(n*p),nrow=n,ncol=p)
Armin Rauschenberger's avatar
Armin Rauschenberger committed
415
#' Y <- replicate(n=q,expr=rnorm(n=n,mean=rowSums(X[,1:5])))
Armin Rauschenberger's avatar
Armin Rauschenberger committed
416
#' object <- joinet(Y=Y,X=X)
Armin Rauschenberger's avatar
Armin Rauschenberger committed
417 418
#' weights(object)
#' 
Armin Rauschenberger's avatar
Armin Rauschenberger committed
419
weights.joinet <- function(object,...){
Armin Rauschenberger's avatar
Armin Rauschenberger committed
420 421
  if(length(list(...))!=0){warning("Ignoring argument.",call.=FALSE)}
  x <- object$meta
Armin Rauschenberger's avatar
Armin Rauschenberger committed
422
  coef <- lapply(object$meta,function(x) stats::coef(object=x,s=x$lambda.min))
Armin Rauschenberger's avatar
Armin Rauschenberger committed
423 424 425 426
  coef <- do.call(what="cbind",args=coef)
  coef <- as.matrix(coef)
  colnames(coef) <- names(object$meta)
  return(coef)
Armin Rauschenberger's avatar
Armin Rauschenberger committed
427 428
}

Armin Rauschenberger's avatar
Armin Rauschenberger committed
429 430
print.joinet <- function(x,...){
  cat(paste0("joinet object"),"\n")
Armin Rauschenberger's avatar
Armin Rauschenberger committed
431 432 433 434
}

#--- Manuscript functions ------------------------------------------------------

Armin Rauschenberger's avatar
Armin Rauschenberger committed
435
#' @export
Armin Rauschenberger's avatar
Armin Rauschenberger committed
436
#' @title
Armin Rauschenberger's avatar
Armin Rauschenberger committed
437
#' Model comparison
Armin Rauschenberger's avatar
Armin Rauschenberger committed
438 439
#'
#' @description
Armin Rauschenberger's avatar
Armin Rauschenberger committed
440
#' Compares univariate and multivariate regression.
Armin Rauschenberger's avatar
Armin Rauschenberger committed
441
#' 
Armin Rauschenberger's avatar
Armin Rauschenberger committed
442
#' @inheritParams joinet
Armin Rauschenberger's avatar
Armin Rauschenberger committed
443
#' 
Armin Rauschenberger's avatar
Armin Rauschenberger committed
444 445
#' @param nfolds.ext
#' number of external folds
Armin Rauschenberger's avatar
Armin Rauschenberger committed
446
#' 
Armin Rauschenberger's avatar
Armin Rauschenberger committed
447 448
#' @param nfolds.int
#' number of internal folds
Armin Rauschenberger's avatar
Armin Rauschenberger committed
449
#' 
Armin Rauschenberger's avatar
Armin Rauschenberger committed
450 451 452 453 454
#' @param foldid.ext
#' external fold identifiers\strong{:}
#' vector of length \eqn{n} with entries
#' between \eqn{1} and \code{nfolds.ext};
#' or \code{NULL}
Armin Rauschenberger's avatar
Armin Rauschenberger committed
455
#' 
Armin Rauschenberger's avatar
Armin Rauschenberger committed
456 457 458 459 460
#' @param foldid.int
#' internal fold identifiers\strong{:}
#' vector of length \eqn{n} with entries
#' between \eqn{1} and \code{nfolds.int};
#' or \code{NULL}
Armin Rauschenberger's avatar
Armin Rauschenberger committed
461
#' 
Armin Rauschenberger's avatar
Armin Rauschenberger committed
462
#' @param compare
Armin Rauschenberger's avatar
Armin Rauschenberger committed
463
#' experimental arguments\strong{:}
Armin Rauschenberger's avatar
Armin Rauschenberger committed
464 465 466
#' character vector with entries "mnorm", "spls", "mrce",
#' "sier", "mtps", "rmtl", "gpm" and others
#' (requires packages \code{spls}, \code{MRCE}, \code{SiER}, \code{MTPS}, \code{RMTL} or \code{GPM})
Armin Rauschenberger's avatar
Armin Rauschenberger committed
467 468 469 470
#' 
#' @param mice
#' missing data imputation\strong{:}
#' logical (\code{mice=TRUE} requires package \code{mice})
Armin Rauschenberger's avatar
Armin Rauschenberger committed
471
#' 
Armin Rauschenberger's avatar
Armin Rauschenberger committed
472
#' @param cvpred
Armin Rauschenberger's avatar
Armin Rauschenberger committed
473
#' return cross-validated predicitions: logical
Armin Rauschenberger's avatar
Armin Rauschenberger committed
474
#' 
Armin Rauschenberger's avatar
Armin Rauschenberger committed
475
#' @param ...
Armin Rauschenberger's avatar
Armin Rauschenberger committed
476 477 478 479 480
#' further arguments passed to \code{\link[glmnet]{glmnet}}
#' and \code{\link[glmnet]{cv.glmnet}}
#' 
#' @return 
#' This function returns a matrix with \eqn{q} columns,
Armin Rauschenberger's avatar
Armin Rauschenberger committed
481 482 483
#' including the cross-validated loss from the univariate models
#' (\code{base}), the multivariate models (\code{meta}),
#' and the intercept-only models (\code{none}).
Armin Rauschenberger's avatar
Armin Rauschenberger committed
484
#' 
Armin Rauschenberger's avatar
Armin Rauschenberger committed
485
#' @examples
Armin Rauschenberger's avatar
Armin Rauschenberger committed
486
#' n <- 50; p <- 100; q <- 3
Armin Rauschenberger's avatar
Armin Rauschenberger committed
487
#' X <- matrix(rnorm(n*p),nrow=n,ncol=p)
Armin Rauschenberger's avatar
Armin Rauschenberger committed
488 489 490 491 492
#' Y <- replicate(n=q,expr=rnorm(n=n,mean=rowSums(X[,1:5])))
#' cv.joinet(Y=Y,X=X)
#' 
#' \dontrun{
#' # correlated features
Armin Rauschenberger's avatar
Armin Rauschenberger committed
493
#' n <- 50; p <- 100; q <- 3
Armin Rauschenberger's avatar
Armin Rauschenberger committed
494 495 496 497
#' mu <- rep(0,times=p)
#' Sigma <- 0.90^abs(col(diag(p))-row(diag(p)))
#' X <- MASS::mvrnorm(n=n,mu=mu,Sigma=Sigma)
#' mu <- rowSums(X[,sample(seq_len(p),size=5)])
Armin Rauschenberger's avatar
Armin Rauschenberger committed
498 499 500
#' Y <- replicate(n=q,expr=rnorm(n=n,mean=mu))
#' #Y <- t(MASS::mvrnorm(n=q,mu=mu,Sigma=diag(n)))
#' cv.joinet(Y=Y,X=X)}
Armin Rauschenberger's avatar
Armin Rauschenberger committed
501 502 503
#' 
#' \dontrun{
#' # other distributions
Armin Rauschenberger's avatar
Armin Rauschenberger committed
504
#' n <- 50; p <- 100; q <- 3
Armin Rauschenberger's avatar
Armin Rauschenberger committed
505
#' X <- matrix(rnorm(n*p),nrow=n,ncol=p)
Armin Rauschenberger's avatar
Armin Rauschenberger committed
506
#' eta <- rowSums(X[,1:5])
Armin Rauschenberger's avatar
Armin Rauschenberger committed
507 508 509
#' Y <- replicate(n=q,expr=rbinom(n=n,size=1,prob=1/(1+exp(-eta))))
#' cv.joinet(Y=Y,X=X,family="binomial")
#' Y <- replicate(n=q,expr=rpois(n=n,lambda=exp(scale(eta))))
Armin Rauschenberger's avatar
Armin Rauschenberger committed
510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528
#' cv.joinet(Y=Y,X=X,family="poisson")}
#' 
#' \dontrun{
#' # uncorrelated outcomes
#' n <- 50; p <- 100; q <- 3
#' X <- matrix(rnorm(n*p),nrow=n,ncol=p)
#' y <- rnorm(n=n,mean=rowSums(X[,1:5]))
#' Y <- cbind(y,matrix(rnorm(n*(q-1)),nrow=n,ncol=q-1))
#' cv.joinet(Y=Y,X=X)}
#' 
#' \dontrun{
#' # sparse and dense models
#' n <- 50; p <- 100; q <- 3
#' X <- matrix(rnorm(n*p),nrow=n,ncol=p)
#' Y <- replicate(n=q,expr=rnorm(n=n,mean=rowSums(X[,1:5])))
#' set.seed(1) # fix folds
#' cv.joinet(Y=Y,X=X,alpha.base=1) # lasso
#' set.seed(1)
#' cv.joinet(Y=Y,X=X,alpha.base=0) # ridge}
Armin Rauschenberger's avatar
Armin Rauschenberger committed
529
#' 
Armin Rauschenberger's avatar
Armin Rauschenberger committed
530
cv.joinet <- function(Y,X,family="gaussian",nfolds.ext=5,nfolds.int=10,foldid.ext=NULL,foldid.int=NULL,type.measure="deviance",alpha.base=1,alpha.meta=1,compare=NULL,mice=FALSE,cvpred=FALSE,times=FALSE,...){
Armin Rauschenberger's avatar
Armin Rauschenberger committed
531
  
Armin Rauschenberger's avatar
Armin Rauschenberger committed
532
  if(FALSE){
Armin Rauschenberger's avatar
Armin Rauschenberger committed
533 534 535 536 537 538
  family <- "gaussian"; nfolds.ext <- 5; nfolds.int <- 10; foldid.ext <- foldid.int <- NULL; type.measure <- "deviance"; alpha.base <- alpha.meta <- 1; mice <- cvpred <- times <- FALSE
  #nfolds.ext <- 1; foldid.ext <- fold; nfolds.int <- 10; foldid.int <- NULL; compare <- TRUE
  }
  
  if(!is.null(compare) && length(compare)==1 && compare){
    compare <- c("mnorm","spls","mars","mrf","map","rmtl","mtps","mcen", "gpm","sier","mrce")
Armin Rauschenberger's avatar
Armin Rauschenberger committed
539
  }
Armin Rauschenberger's avatar
Armin Rauschenberger committed
540
  
Armin Rauschenberger's avatar
Armin Rauschenberger committed
541 542 543
  n <- nrow(Y)
  q <- ncol(Y)
  p <- ncol(X)
Armin Rauschenberger's avatar
Armin Rauschenberger committed
544
  
Armin Rauschenberger's avatar
Armin Rauschenberger committed
545 546 547 548
  #--- fold identifiers ---
  if(is.null(foldid.ext)){
    foldid.ext <- palasso:::.folds(y=Y[,1],nfolds=nfolds.ext) # temporary Y[,1]
  } else {
Armin Rauschenberger's avatar
Armin Rauschenberger committed
549 550
    #nfolds.ext <- length(unique(foldid.ext))
    nfolds.ext <- max(foldid.ext)
Armin Rauschenberger's avatar
Armin Rauschenberger committed
551 552
  }
  
Armin Rauschenberger's avatar
Armin Rauschenberger committed
553 554 555 556 557
  #--- family ---
  if(length(family)==1){
    family <- rep(family,times=q)
  } else if(length(family)!=q){
    stop("Invalid argument family",call.=FALSE)
Armin Rauschenberger's avatar
Armin Rauschenberger committed
558 559
  }
  
Armin Rauschenberger's avatar
Armin Rauschenberger committed
560 561 562 563 564
  # check packages
  pkgs <- .packages(all.available=TRUE)
  for(i in seq_along(compare)){
    pkg <- switch(compare[i],mnorm="glmnet",mars="earth",spls="spls",mtps="MTPS",
           rmtl="RMTL",mrf="MultivariateRandomForest",mcen="mcen",
Armin Rauschenberger's avatar
Armin Rauschenberger committed
565
           map="remMap",sier="SiER",gpm="GPM",mrce="MRCE",ecc="MLPUGS",stop("Invalid method.",call.=FALSE))
Armin Rauschenberger's avatar
Armin Rauschenberger committed
566 567 568
    if(!pkg %in% pkgs){
      stop("Method \"",compare[i],"\" requires package \"",pkg,"\".",call.=FALSE)
    }
Armin Rauschenberger's avatar
Armin Rauschenberger committed
569
  }
Armin Rauschenberger's avatar
Armin Rauschenberger committed
570 571 572 573 574 575 576 577

  #--- checks ---
  #if(any( & any(family!="gaussian")){
  #  stop("\"mnorm\", \"spls\", \"mrce\" and \"sier\" require family \"gaussian\".",call.=FALSE)
  #}
  #if(any(mtps,rmtl) & any(!family %in% c("gaussian","binomial"))){
  #  stop("\"mtps\" and \"rmtl\" require family \"gaussian\" or \"binomial\".",call.=FALSE)
  #}
Armin Rauschenberger's avatar
Armin Rauschenberger committed
578
  
Armin Rauschenberger's avatar
Armin Rauschenberger committed
579
  #--- cross-validated predictions ---
Armin Rauschenberger's avatar
Armin Rauschenberger committed
580
  
Armin Rauschenberger's avatar
Armin Rauschenberger committed
581
  models <- c("base","meta",compare,"none")
Armin Rauschenberger's avatar
Armin Rauschenberger committed
582
  pred <- lapply(X=models,function(x) matrix(data=NA,nrow=n,ncol=q))
Armin Rauschenberger's avatar
Armin Rauschenberger committed
583 584
  time <- lapply(X=models,function(x) NA)
  names(pred) <- names(time) <- models
Armin Rauschenberger's avatar
Armin Rauschenberger committed
585
  
Armin Rauschenberger's avatar
Armin Rauschenberger committed
586 587 588
  for(i in seq_len(nfolds.ext)){
    
    Y0 <- Y[foldid.ext!=i,,drop=FALSE]
Armin Rauschenberger's avatar
Armin Rauschenberger committed
589
    #Y1 <- Y[foldid.ext==i,,drop=FALSE]
Armin Rauschenberger's avatar
Armin Rauschenberger committed
590 591
    X0 <- X[foldid.ext!=i,,drop=FALSE]
    X1 <- X[foldid.ext==i,,drop=FALSE]
Armin Rauschenberger's avatar
Armin Rauschenberger committed
592
    
Armin Rauschenberger's avatar
Armin Rauschenberger committed
593 594 595 596 597 598 599
    # standardise features (trial)
    #mu <- apply(X=X0,MARGIN=2,FUN=function(x) mean(x))
    #sd <- apply(X=X0,MARGIN=2,FUN=function(x) stats::sd(x))
    #X0 <- t((t(X0)-mu)/sd)[,sd!=0]
    #X1 <- t((t(X1)-mu)/sd)[,sd!=0]
    # or standardise once before cv?
    
Armin Rauschenberger's avatar
Armin Rauschenberger committed
600 601 602 603
    # remove constant features
    cond <- apply(X=X0,MARGIN=2,FUN=function(x) stats::sd(x)!=0)
    X0 <- X0[,cond]; X1 <- X1[,cond]
    
Armin Rauschenberger's avatar
Armin Rauschenberger committed
604 605 606 607 608 609 610
    if(is.null(foldid.int)){
      foldid <- palasso:::.folds(y=Y0[,1],nfolds=nfolds.int) # temporary Y0[,1]
    } else {
      foldid <- foldid.int[foldid.ext!=i]
    }
    
    # base and meta learners
Armin Rauschenberger's avatar
Armin Rauschenberger committed
611
    start <- Sys.time()
Armin Rauschenberger's avatar
Armin Rauschenberger committed
612
    fit <- joinet(Y=Y0,X=X0,family=family,type.measure=type.measure,alpha.base=alpha.base,alpha.meta=alpha.meta,foldid=foldid) # add ellipsis (...)
Armin Rauschenberger's avatar
Armin Rauschenberger committed
613
    # also do not standardise!
Armin Rauschenberger's avatar
Armin Rauschenberger committed
614
    temp <- predict.joinet(fit,newx=X1)
Armin Rauschenberger's avatar
Armin Rauschenberger committed
615 616
    pred$base[foldid.ext==i,] <- temp$base
    pred$meta[foldid.ext==i,] <- temp$meta
Armin Rauschenberger's avatar
Armin Rauschenberger committed
617 618
    end <- Sys.time()
    time$meta <- as.numeric(difftime(end,start,units="secs"))
Armin Rauschenberger's avatar
Armin Rauschenberger committed
619
    
Armin Rauschenberger's avatar
Armin Rauschenberger committed
620
    # missing values
Armin Rauschenberger's avatar
Armin Rauschenberger committed
621 622 623 624
    if(mice & any(is.na(Y0))){
      if(requireNamespace("mice",quietly=TRUE)){
        y0 <- as.matrix(mice::complete(data=mice::mice(Y0,m=1,method="pmm",seed=1,printFlag=FALSE),action="all")[[1]])
      } else {
Armin Rauschenberger's avatar
Armin Rauschenberger committed
625
        stop("Imputation by PMM requires install.packages(\"mice\").",call.=FALSE)
Armin Rauschenberger's avatar
Armin Rauschenberger committed
626 627 628 629
      }
    } else {
      y0 <- apply(X=Y0,MARGIN=2,FUN=function(x) ifelse(is.na(x),stats::median(x[!is.na(x)]),x))
    }
Armin Rauschenberger's avatar
Armin Rauschenberger committed
630
    all(Y0==y0,na.rm=TRUE)
Armin Rauschenberger's avatar
Armin Rauschenberger committed
631
    
Armin Rauschenberger's avatar
Armin Rauschenberger committed
632
    # other learners
Armin Rauschenberger's avatar
Armin Rauschenberger committed
633 634
    
    if("mnorm" %in% compare){
Armin Rauschenberger's avatar
Armin Rauschenberger committed
635 636 637 638 639
      cat("mnorm"," ")
      start <- Sys.time()
      if(any(family!="gaussian")){
        stop("mnorm requires \"gaussian\" family.",call.=FALSE)
      }
Armin Rauschenberger's avatar
Armin Rauschenberger committed
640
      net <- glmnet::cv.glmnet(x=X0,y=y0,family="mgaussian",foldid=foldid,alpha=alpha.base) # add ellipsis (...)
Armin Rauschenberger's avatar
Armin Rauschenberger committed
641
      pred$mnorm[foldid.ext==i,] <- stats::predict(object=net,newx=X1,s="lambda.min",type="response")
Armin Rauschenberger's avatar
Armin Rauschenberger committed
642 643 644 645
      end <- Sys.time()
      time$mnorm <- as.numeric(difftime(end,start,units="secs"))
    } else {
      net <- glmnet::glmnet(x=X0,y=y0,family="mgaussian")
Armin Rauschenberger's avatar
Armin Rauschenberger committed
646
    }
Armin Rauschenberger's avatar
Armin Rauschenberger committed
647 648
    
    if("mars" %in% compare){
Armin Rauschenberger's avatar
Armin Rauschenberger committed
649 650
      cat("mars"," ")
      start <- Sys.time()
Armin Rauschenberger's avatar
Armin Rauschenberger committed
651
      if(all(family=="gaussian")){
Armin Rauschenberger's avatar
Armin Rauschenberger committed
652 653
        object <- earth::earth(x=X0,y=y0,nfold=nfolds.int) # add:pmethod="cv"; new: nfolds and pmethod
        # equivalent: object <- mda::mars(x=X0,y=y0)
Armin Rauschenberger's avatar
Armin Rauschenberger committed
654
      } else if(all(family=="binomial")){
Armin Rauschenberger's avatar
Armin Rauschenberger committed
655
        object <- earth::earth(x=X0,y=y0,glm=list(family=stats::binomial),nfold=nfolds.int) # add pmethod="cv", new: nfolds and pmethod
Armin Rauschenberger's avatar
Armin Rauschenberger committed
656
      } else {
Armin Rauschenberger's avatar
Armin Rauschenberger committed
657
        stop("MARS requires either \"gaussian\" or \"binomial\" family.",call.=FALSE)
Armin Rauschenberger's avatar
Armin Rauschenberger committed
658 659
      }
      pred$mars[foldid.ext==i,] <- earth:::predict.earth(object=object,newdata=X1,type="response")
Armin Rauschenberger's avatar
Armin Rauschenberger committed
660 661 662
      end <- Sys.time()
      time$mars <- as.numeric(difftime(end,start,units="secs"))
      #mean((Y-pred$mars)^2,na.rm=TRUE)
Armin Rauschenberger's avatar
Armin Rauschenberger committed
663
    }
Armin Rauschenberger's avatar
Armin Rauschenberger committed
664 665
    
    if("spls" %in% compare){
Armin Rauschenberger's avatar
Armin Rauschenberger committed
666 667 668 669 670 671 672 673
      cat("spls"," ")
      start <- Sys.time()
      if(any(family!="gaussian")){
        stop("spls requires \"gaussian\" family.")
      }
      invisible(utils::capture.output(cv.spls <- spls::cv.spls(x=X0,y=y0,fold=nfolds.int,K=seq_len(min(ncol(X0),10)),
                               eta=seq(from=0.1,to=0.9,by=0.1),plot.it=FALSE))) #scale.x=FALSE
      object <- spls::spls(x=X0,y=y0,K=cv.spls$K.opt,eta=cv.spls$eta.opt) #scale.x=FALSE
Armin Rauschenberger's avatar
Armin Rauschenberger committed
674
      pred$spls[foldid.ext==i,] <- spls::predict.spls(object=object,newx=X1,type="fit")
Armin Rauschenberger's avatar
Armin Rauschenberger committed
675 676
      end <- Sys.time()
      time$spls <- as.numeric(difftime(end,start,units="secs"))
Armin Rauschenberger's avatar
Armin Rauschenberger committed
677
    }
Armin Rauschenberger's avatar
Armin Rauschenberger committed
678 679
    
    if("mtps" %in% compare){
Armin Rauschenberger's avatar
Armin Rauschenberger committed
680 681 682
      cat("mtps"," ")
      start <- Sys.time()
      
Armin Rauschenberger's avatar
Armin Rauschenberger committed
683 684 685 686 687 688 689
      if(alpha.base==0){
        step1 <- MTPS::glmnet.ridge
      } else if(alpha.base==1){
        step1 <- MTPS::glmnet.lasso
      } else {
        stop("MTPS requires alpha.base 0 or 1.",call.=FALSE)
      }
Armin Rauschenberger's avatar
Armin Rauschenberger committed
690
      
Armin Rauschenberger's avatar
Armin Rauschenberger committed
691 692 693 694 695 696 697 698 699 700 701 702 703 704
      #-------------------------------
      #--- manual lambda.min start ---
      #body <- body(step1)
      #body[[6]][[3]][[2]][[3]] <- "lambda.min"
      #body[[7]][[3]][[4]] <- "lambda.min"
      #body[[8]][[3]][[3]][[2]][[4]] <- "lambda.min"
      #body(step1) <- body
      #if(alpha.base==0){
      #  assignInNamespace(x="glmnet.ridge",value=step1,ns="MTPS")
      #} else if(alpha.base==1){
      #  assignInNamespace(x="glmnet.lasso",value=step1,ns="MTPS")
      #}
      #--- manual lambda.min end ---
      #-----------------------------
Armin Rauschenberger's avatar
Armin Rauschenberger committed
705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723
      
      #---------------------------
      #--- manual kmeans start ---
      #fun <- MTPS::cv.multiFit
      #body <- body(fun)
      #body[[11]][[3]][[3]] <- nrow(unique(y0))
      #body(fun) <- body
      #assignInNamespace(x="cv.multiFit",value=step1,ns="MTPS")
      #--- manual kmeans end ---
      #-------------------------
      
      if(all(family=="gaussian")){
        step2 <- MTPS::rpart1
      } else if(all(family=="binomial")){
        step2 <- MTPS::glm1
      } else {
        stop("MTPS requires family gaussian or binomial.",call.=FALSE)
      }
      
Armin Rauschenberger's avatar
Armin Rauschenberger committed
724
      object <- MTPS::MTPS(xmat=X0,ymat=y0,family=family,nfold=nfolds.int,
Armin Rauschenberger's avatar
Armin Rauschenberger committed
725
                           method.step1=step1,method.step2=step2,cv=TRUE,residual=TRUE)
Armin Rauschenberger's avatar
Armin Rauschenberger committed
726
      pred$mtps[foldid.ext==i,] <- MTPS::predict.MTPS(object=object,newdata=X1,type="response")
Armin Rauschenberger's avatar
Armin Rauschenberger committed
727 728 729
      end <- Sys.time()
      time$mtps <- as.numeric(difftime(end,start,units="secs"))
      # now using cross-validation residual stacking (CVRS) 
Armin Rauschenberger's avatar
Armin Rauschenberger committed
730
    }
Armin Rauschenberger's avatar
Armin Rauschenberger committed
731 732
    
    if("rmtl" %in% compare){
Armin Rauschenberger's avatar
Armin Rauschenberger committed
733 734
      cat("rmtl"," ")
      start <- Sys.time()
Armin Rauschenberger's avatar
Armin Rauschenberger committed
735 736
      if(all(family=="gaussian")){
        type <- "Regression"
Armin Rauschenberger's avatar
Armin Rauschenberger committed
737
        y0l <- lapply(seq_len(ncol(y0)),function(i) y0[,i,drop=FALSE])
Armin Rauschenberger's avatar
Armin Rauschenberger committed
738 739
      } else if(all(family=="binomial")){
        type <- "Classification"
Armin Rauschenberger's avatar
Armin Rauschenberger committed
740
        y0l <- lapply(seq_len(ncol(y0)),function(i) 2*y0[,i,drop=FALSE]-1)
Armin Rauschenberger's avatar
Armin Rauschenberger committed
741 742 743
      } else {
        stop("RMTL requires either \"gaussian\" or \"binomial\".",call.=FALSE)
      }
Armin Rauschenberger's avatar
Armin Rauschenberger committed
744 745 746 747
      X0l <- lapply(seq_len(ncol(y0)),function(i) X0)
      X1l <- lapply(seq_len(ncol(y0)),function(i) X1)
      #---------------------------
      #--- manual tuning start ---
Armin Rauschenberger's avatar
Armin Rauschenberger committed
748 749 750 751
      Lam1_seq <- c(10^seq(from=1,to=-4,length.out=11),0)
      Lam2_seq <- c(10^seq(from=1,to=-4,length.out=11),0)
      cvMTL <- list()
      seed <- .Random.seed
Armin Rauschenberger's avatar
Armin Rauschenberger committed
752
      for(j in seq_along(Lam2_seq)){
Armin Rauschenberger's avatar
Armin Rauschenberger committed
753 754
        .Random.seed <- seed
        cvMTL[[j]] <- RMTL::cvMTL(X=X0l,Y=y0l,type=type,Lam1_seq=Lam1_seq,Lam2=Lam2_seq[j])
Armin Rauschenberger's avatar
Armin Rauschenberger committed
755
      }
Armin Rauschenberger's avatar
Armin Rauschenberger committed
756 757
      cvm <- vapply(X=cvMTL,FUN=function(x) min(x$cvm),FUN.VALUE=numeric(1))
      Lam1 <- cvMTL[[which.min(cvm)]]$Lam1.min
Armin Rauschenberger's avatar
Armin Rauschenberger committed
758
      Lam2 <- Lam2_seq[which.min(cvm)]
Armin Rauschenberger's avatar
Armin Rauschenberger committed
759 760
      #graphics::plot(x=Lam2_seq,y=cvm)
      #cat(Lam1," ",Lam2,"\n")
Armin Rauschenberger's avatar
Armin Rauschenberger committed
761 762
      #--- manual tuning end ----
      #--------------------------
Armin Rauschenberger's avatar
Armin Rauschenberger committed
763
      MTL <- RMTL::MTL(X=X0l,Y=y0l,type=type,Lam1=Lam1,Lam2=Lam2)
Armin Rauschenberger's avatar
Armin Rauschenberger committed
764 765
      temp <- RMTL:::predict.MTL(object=MTL,newX=X1l)
      pred$rmtl[foldid.ext==i,] <- do.call(what="cbind",args=temp)
Armin Rauschenberger's avatar
Armin Rauschenberger committed
766 767
      end <- Sys.time()
      time$rmtl <- as.numeric(difftime(end,start,units="secs"))
Armin Rauschenberger's avatar
Armin Rauschenberger committed
768
    }
Armin Rauschenberger's avatar
Armin Rauschenberger committed
769 770
    
    if("mrf" %in% compare){
Armin Rauschenberger's avatar
Armin Rauschenberger committed
771 772 773 774 775 776 777 778
      cat("mrf"," ")
      start <- Sys.time()
      if(any(family!="gaussian")){
        stop("mrf requires \"gaussian\" family.")
      }
      pred$mrf[foldid.ext==i,] <- MultivariateRandomForest::build_forest_predict(trainX=X0,trainY=y0,
                                   n_tree=100,m_feature=min(ncol(X)-1,5),min_leaf=5,testX=X1)
      # use n_tree=500, m_feature=floor(ncol(X0)/3)
Armin Rauschenberger's avatar
Armin Rauschenberger committed
779
      # alternative: IntegratedMRF
Armin Rauschenberger's avatar
Armin Rauschenberger committed
780 781 782
      # Check why this does not work well!
      end <- Sys.time()
      time$mrf <- as.numeric(difftime(end,start,units="secs"))
Armin Rauschenberger's avatar
Armin Rauschenberger committed
783 784 785
    }
    
    if("mcen" %in% compare){
Armin Rauschenberger's avatar
Armin Rauschenberger committed
786 787 788 789 790 791 792 793 794 795 796 797
      cat("mcen"," ")
      start <- Sys.time()
      if(all(family=="gaussian")){
        type <- "mgaussian"
      } else if(all(family=="binomial")){
        type <- "mbinomial"
      } else {
        stop("mcen requires either \"gaussian\" or \"binomial\".",call.=FALSE)
      }
      object <- mcen::cv.mcen(x=X0,y=y0,family=type,folds=foldid,ky=1,
                              gamma_y=seq(from=0.1,to=5.1,length.out=3),ndelta=3)
      # TEMPORARY gamma_y and ndelta (for speed-up)
Armin Rauschenberger's avatar
Armin Rauschenberger committed
798 799
      temp <- mcen:::predict.cv.mcen(object=object,newx=X1)
      pred$mcen[foldid.ext==i,] <- as.matrix(temp)
Armin Rauschenberger's avatar
Armin Rauschenberger committed
800 801 802
      # single cluster (ky=1) due to setting and error
      end <- Sys.time()
      time$mcen <- as.numeric(difftime(end,start,units="secs"))
Armin Rauschenberger's avatar
Armin Rauschenberger committed
803 804 805
    }
    
    if("map" %in% compare){
Armin Rauschenberger's avatar
Armin Rauschenberger committed
806 807 808 809 810
      cat("map"," ")
      start <- Sys.time()
      if(any(family!="gaussian")){
        stop("map requires \"gaussian\" family.")
      }
Armin Rauschenberger's avatar
Armin Rauschenberger committed
811 812
      mean <- colMeans(y0)
      y0s <- y0-matrix(data=mean,nrow=nrow(X0),ncol=ncol(y0),byrow=TRUE)
Armin Rauschenberger's avatar
Armin Rauschenberger committed
813 814
      lamL1.v <- exp(seq(from=log(10),to=log(20),length.out=11))
      lamL2.v <- seq(from=0,to=5,length.out=11)
Armin Rauschenberger's avatar
Armin Rauschenberger committed
815 816
      cv <- remMap::remMap.CV(X=X0,Y=y0s,lamL1.v=lamL1.v,lamL2.v=lamL2.v)
      #graphics::plot(x=lamL1.v,y=log(as.numeric(cv$ols.cv[,3])))
Armin Rauschenberger's avatar
Armin Rauschenberger committed
817 818
      index <- which(cv$ols.cv==min(cv$ols.cv),arr.ind=TRUE)[1,]
      #cat(index,"\n")
Armin Rauschenberger's avatar
Armin Rauschenberger committed
819 820 821
      object <- remMap::remMap(X.m=X0,Y.m=y0s,lamL1=lamL1.v[index[1]],lamL2=lamL2.v[index[2]])
      pred$map[foldid.ext==i,] <- matrix(data=mean,nrow=nrow(X1),ncol=ncol(y0),byrow=TRUE) + X1 %*% object$phi 
      # alternative: groupRemMap
Armin Rauschenberger's avatar
Armin Rauschenberger committed
822 823
      end <- Sys.time()
      time$map <- as.numeric(difftime(end,start,units="secs"))
Armin Rauschenberger's avatar
Armin Rauschenberger committed
824 825
    }
    
Armin Rauschenberger's avatar
Armin Rauschenberger committed
826 827 828 829 830 831 832 833
    if("sier" %in% compare){
      cat("sier"," ")
      start <- Sys.time()
      if(any(family!="gaussian")){
        stop("SiER requires \"gaussian\" family.",call.=FALSE)
      }
      invisible(utils::capture.output(object <- SiER::cv.SiER(X=X0,Y=y0,K.cv=5,upper.comp=10,thres=0.01)))
      # should be upper.comp=10 and thres=0.01
Armin Rauschenberger's avatar
Armin Rauschenberger committed
834
      pred$sier[foldid.ext==i,] <- SiER::pred.SiER(cv.fit=object,X.new=X1)
Armin Rauschenberger's avatar
Armin Rauschenberger committed
835 836
      end <- Sys.time()
      time$sier <- as.numeric(difftime(end,start,units="secs"))
Armin Rauschenberger's avatar
Armin Rauschenberger committed
837 838
    }
    
Armin Rauschenberger's avatar
Armin Rauschenberger committed
839 840 841
    if("gpm" %in% compare){
      cat("gpm"," ")
      start <- Sys.time()
Armin Rauschenberger's avatar
Armin Rauschenberger committed
842 843