R/externVar.R

Defines functions externVar

Documented in externVar

#' Estimation of secondary regression models after the estimation of a primary latent class model
#' 
#' This function fits regression models to relate a latent class structure (stemmed 
#' from a latent class model estimated within \code{lcmm} package) with either an external
#'  outcome or external class predictors. 
#'  Two inference techniques are implemented to account for the classification error: 
#'  
#'  - a 2-stage estimation of the joint likelihood of the primary latent class model 
#'  and the secondary/ external regression;
#'  
#'  - a regression between the posterior latent class assignment and the external variable 
#'  which internally corrects for the assignment misclassification. 
#'  
#' It returns an object from one of the \code{lcmm} package classes.
#' 
#' A. DATA STRUCTURE
#' 
#' The \code{data} argument must follow specific structure for individual variables,
#' i.e. variables with a unique constant value for each subject. For an individual variable
#' given as external outcome, data value must be present only once per subject,
#' independently of any time variable used in the primary latent class.
#' For an individual variable given as external class predictor,
#' data values must be given for every row of every individual (as usual)
#' 
#' B. VARIANCE ESTIMATION
#' 
#' Not taking into account first stage variance with specifing \code{"none"} may lead to
#' underestimation of the final variance. When possible, Method \code{"Hessian"} 
#' which relies on the combination of Hessians from the primary and secondary
#' model is recommended. However, it may become numerically intensive in the event 
#' of very high number of parameters in the primary latent class model. As an 
#' alternative, especially in situations with a complex primary model but rather 
#' parcimonious secondary model, method \code{"paramBoot"} which implements a 
#' parametric bootstrap can be used.
#' 
#' @param model an object inheriting from class \code{hlme}, \code{lcmm}, 
#' \code{Jointlcmm}, \code{multlcmm} or \code{mpjlcmm} giving the primary latent
#'  class model.
#' @param fixed optional two sided linear formula object for specifying the
#' fixed-effects in the secondary model with an external outcome variable.
#' The response outcome is on the left of \code{~} and the covariates are separated
#' by \code{+} on the right of the \code{~}. By default, an intercept is included.
#' @param mixture optional one-sided formula object for the class-specific fixed effects
#' in the model for the external outcome. Among the list of covariates included in fixed,
#' the covariates with class-specific regression parameters are entered in
#' mixture separated by \code{+}. By default, an intercept is included.
#' If no intercept, \code{-1} should be the first term included.
#' @param random optional one-sided linear formula object for specifying the
#' random-effects on external outcome in the secondary model, if appropriate. 
#' By default, no random effect is included.
#' @param subject name of the covariate representing the grouping structure.
#' Even in the absence of a hierarchical structure.
#' @param classmb optional one-sided formula specifying the external predictors of 
#' latent class membership to be modelled in the secondary class-membership multinomial 
#' logistic model. Covariates are separated by \code{+} on the right of the \code{~}.
#' @param survival optional two-sided formula specifying the external survival part
#' of the model.
#' @param hazard optional family of hazard function assumed for the survival model
#' (Weibull, piecewise or splines)
#' @param hazardtype optional indicator for the type of baseline risk function
#' (Specific, PH or Common)
#' @param hazardnodes optional vector containing interior nodes if \code{splines} or
#' \code{piecewise} is specified for the baseline hazard function in \code{hazard}
#' @param TimeDepVar optional vector specifying the name of the time-depending covariate
#' in the survival model
#' @param logscale optional boolean indicating whether an exponential (logscale=TRUE) or
#' a square (logscale=FALSE -by default) transformation is used to
#' ensure positivity of parameters in the baseline risk functions
#' @param idiag if appropriate, optional logical for the structure of the variance-covariance
#' matrix of the random-effects in the secondary model. 
#' If \code{FALSE}, a non structured matrix of
#' variance-covariance is considered (by default). If \code{TRUE} a diagonal
#' matrix of variance-covariance is considered.
#' @param nwg if appropriate, optional logical indicating if the variance-covariance of the
#' random-effects in the secondary model is class-specific. If \code{FALSE} the
#' variance-covariance matrix is common over latent classes (by default). If \code{TRUE} a
#' class-specific proportional parameter multiplies the variance-covariance
#' matrix in each class (the proportional parameter in the last latent class
#' equals 1 to ensure identifiability).
#' @param randomY optional logical for including an outcome-specific random intercept.
#' If FALSE no outcome-specific random intercept is added (default). If TRUE independent
#' outcome-specific random intercept with parameterized variance are included
#' @param link optional family of parameterized link functions for the external outcome
#' if appropriate. Defaults to NULL, corresponding to continuous Gaussian distribution
#' (hlme function).
#' @param intnodes optional vector of interior nodes. This argument is only
#' required for a I-splines link function with nodes entered manually.
#' @param epsY optional definite positive real used to rescale the marker in (0,1)
#' when the beta link function is used. By default, epsY=0.5.
#' @param cor optional indicator for inclusion of an auto correlated Gaussian process
#' in the latent process linear (latent process) mixed model. Option "BM" indicates
#' a brownian motion with parameterized variance. Option "AR" specifies an
#' autoregressive process of order 1 with parameterized variance and correlation
#' intensity. Each option should be followed by the time variable in brackets as
#' code{cor=BM(time)}. By default, no autocorrelated Gaussian process is added.
#' @param nsim number of points to be used in the estimated link function. By default,
#' nsom=100.
#' @param range optional vector indicating the range of the outcomes (that is the
#' minimum and maximum). By default, the range is defined according to the minimum
#' and maximum observed values of the outcome. The option should be used
#' only for Beta and Splines transformations.
#' @param data Data frame containing the variables named in
#' \code{fixed}, \code{mixture}, \code{random}, \code{classmb} and \code{subject},
#' for both the current function arguments and the primary model arguments
#' Check \code{details} to get information on the data structure, especially with
#' external outcomes.
#' @param longitudinal only with \code{mpjlcmm} primary models and "twoStageJoint"
#' method: mandatory list containing the longitudinal submodels used in the primary
#' latent class model.
#' @param method character indicating the inference technique to be used:
#' \code{"twoStageJoint"} corresponds to 2-stage estimation. \code{"conditional"}
#' corresponds to the method based on the distribution of Y conditionally to the
#' true latent class membership.
#' @param varest optional character indicating the method to be used to compute the
#' variance of the regression estimates. \code{"none"} does not account for the
#' uncertainty in the primary latent class model, \code{"paramBoot"} computes the
#' total variance using a parametric bootstrap technique, \code{"Hessian"} computes
#' the total Hessian of the joint likelihood (implemented for \code{"twoStageJoint"}
#' method only). Default to \code{"Hessian"} for \code{"twoStageJoint"} method and
#' \code{"paramBoot"} for \code{"conditional"} method.
#' @param M option integer indicating the number of draws for the parametric boostrap
#' when \code{varest="paramBoot"}. Default to 200.
#' @param B optional vector of initial parameter values for the secondary model. 
#' If external outcome, the vector has the same structure as a latent class model
#' estimated in the other functions of \code{lcmm} package for the same type of 
#' outcome. If external class predictors (of size p), the vector is of length 
#' (ng-1)*(1+p). If \code{B=NULL} (by default), internal initial values are selected. 
#' @param convB optional threshold for the convergence criterion based on the
#' parameter stability. By default, convB=0.0001.
#' @param convL optional threshold for the convergence criterion based on the
#' log-likelihood stability. By default, convL=0.0001.
#' @param convG optional threshold for the convergence criterion based on the
#' derivatives. By default, convG=0.0001.
#' @param maxiter optional maximum number of iterations for the secondary model
#' estimation using Marquardt iterative algorithm. Defaults to 100
#' @param posfix optional vector specifying indices in parameter vector B the 
#' secondary model that should not be estimated. Default to NULL, all the 
#' parameters of the secondary regression are estimated. 
#' @param partialH optional logical for Piecewise and Splines baseline risk functions and
#' Splines link functions only. Indicates whether the parameters of the baseline risk or
#' link functions can be dropped from the Hessian matrix to define convergence criteria
#' (can solve non convergence due to estimates at the boundary of the parameter space - usually 0).
#' @param verbose logical indicating whether information about computation should be
#' reported. Default to FALSE.
#' @param nproc the number cores for parallel computation. Default to 1 (sequential mode).
#' @return an object of class \code{externVar} and  
#' \code{externSurv} for external survival outcomes,
#' \code{externX} for external class predictors, and
#' \code{hlme}, \code{lcmm}, or \code{multlcmm} for external longitudinal or cross-sectional outcomes.
#' @author Maris Dussartre, Cecile Proust-Lima and Viviane Philipps
#' 
#' 
#' @examples
#' 
#' \dontrun{
#' 
#' 
#' ###### Estimation of the primary latent class model                   ######
#' 
#' set.seed(1234)
#' PrimMod <- hlme(Ydep1~Time,random=~Time,subject='ID',ng=1,data=data_lcmm)
#' PrimMod2 <- hlme(Ydep1~Time,mixture=~Time,random=~Time,subject='ID',
#'                  ng=2,data=data_lcmm,B=random(PrimMod))
#' 
#' ###### Example 1: Relationship between a latent class structure and         #
#' #                   external class predictors                          ######
#'                   
#' # estimation of the secondary multinomial logistic model with total variance
#' # computed with the Hessian
#' 
#' XextHess <- externVar(PrimMod2,
#'                       classmb = ~X1 + X2 + X3 + X4, 
#'                       subject = "ID",
#'                       data = data_lcmm,
#'                       method = "twoStageJoint") 
#' summary(XextHess)
#' 
#' # estimation of a secondary multinomial logistic model with total variance
#' # computed with parametric Bootstrap (much longer). When using the bootstrap 
#' # estimator, we recommend running first the analysis with option varest = "none" 
#' # which is faster but which underestimates the variance. And then use these values
#' # as initial values when running the model with varest = "paramBoot" to obtain 
#' # a valid variance of the parameters. 
#' 
#' XextNone <- externVar(PrimMod2,
#'                       classmb = ~X1 + X2 + X3 + X4, 
#'                       subject = "ID",
#'                       data = data_lcmm,
#'                       varest = "none",
#'                       method = "twoStageJoint") 
#' 
#' XextBoot <- externVar(PrimMod2,
#'                       classmb = ~X1 + X2 + X3 + X4, 
#'                       subject = "ID",
#'                       data = data_lcmm,
#'                       varest = "paramBoot",
#'                       method = "twoStageJoint",
#'                       B = XextNone$best) 
#' summary(XextBoot)
#' 
#'  
#' ###### Example 2: Relationship between a latent class structure and         #
#' #                external outcome (repeatedly measured over time)     ######
#'                   
#' # estimation of the secondary linear mixed model with total variance
#' # computed with the Hessian
#' 
#' YextHess = externVar(PrimMod2,   #primary model
#'                      fixed = Ydep2 ~ Time*X1,  #secondary model
#'                      random = ~Time, #secondary model
#'                      mixture = ~Time,  #secondary model
#'                      subject="ID",
#'                      data=data_lcmm,
#'                      method = "twoStageJoint")
#'                      
#' 
#' # estimation of a secondary linear mixed model with total variance
#' # computed with parametric Bootstrap (much longer). When using the bootstrap 
#' # estimator, we recommend running first the analysis with option varest = "none" 
#' # which is faster but which underestimates the variance. And then use these values
#' # as initial values when running the model with varest = "paramBoot" to obtain 
#' # a valid variance of the parameters. 
#' 
#' YextNone = externVar(PrimMod2,   #primary model
#'                      fixed = Ydep2 ~ Time*X1,  #secondary model
#'                      random = ~Time, #secondary model
#'                      mixture = ~Time,  #secondary model
#'                      subject="ID",
#'                      data=data_lcmm,
#'                      varest = "none",
#'                      method = "twoStageJoint")
#' 
#' YextBoot = externVar(PrimMod2,   #primary model
#'                      fixed = Ydep2 ~ Time*X1,  #secondary model
#'                      random = ~Time, #secondary model
#'                      mixture = ~Time,  #secondary model
#'                      subject="ID",
#'                      data=data_lcmm,
#'                      method = "twoStageJoint",
#'                      B = YextNone$best,
#'                      varest= "paramBoot")
#' 
#' summary(YextBoot) 
#' 
#' 
#' ###### Example 3: Relationship between a latent class structure and         #
#' #                      external outcome (survival)                     ######
#' 
#' # estimation of the secondary survival model with total variance
#' # computed with the Hessian
#' 
#' YextHess = externVar(PrimMod2,   #primary model
#'                      survival = Surv(Tevent,Event)~ X1+mixture(X2), #secondary model
#'                      hazard="3-quant-splines", #secondary model
#'                      hazardtype="PH", #secondary model
#'                      subject="ID",
#'                      data=data_lcmm,
#'                      method = "twoStageJoint")
#' summary(YextHess)
#' 
#' 
#' # estimation of a secondary survival model with total variance
#' # computed with parametric Bootstrap (much longer). When using the bootstrap
#' # estimator, we recommend running first the analysis with option varest = "none"
#' # which is faster but which underestimates the variance. And then use these values
#' # as initial values when running the model with varest = "paramBoot" to obtain
#' # a valid variance of the parameters.
#' 
#' YextNone = externVar(PrimMod2,   #primary model
#'                      survival = Surv(Tevent,Event)~ X1+mixture(X2), #secondary model
#'                      hazard="3-quant-splines", #secondary model
#'                      hazardtype="PH", #secondary model
#'                      subject="ID",
#'                      data=data_lcmm,
#'                      varest = "none",
#'                      method = "twoStageJoint")
#' 
#' YextBoot = externVar(PrimMod2,   #primary model
#'                      survival = Surv(Tevent,Event)~ X1+mixture(X2), #secondary model
#'                      hazard="3-quant-splines", #secondary model
#'                      hazardtype="PH", #secondary model
#'                      subject="ID",
#'                      data=data_lcmm,
#'                      method = "twoStageJoint",
#'                      B = YextNone$best,
#'                      varest= "paramBoot")
#' 
#' summary(YextBoot)
#' 
#' }
#'
#'
#'
#' 
#' @export
#' 
#' 
#' 


externVar = function(model,
                     fixed,
                     mixture,
                     random,
                     subject,
                     classmb,
                     survival,
                     hazard = "Weibull",
                     hazardtype = "Specific",
                     hazardnodes = NULL,
                     TimeDepVar = NULL,
                     logscale = FALSE,
                     idiag = FALSE,
                     nwg = FALSE,
                     randomY = NULL,
                     link = NULL,
                     intnodes = NULL,
                     epsY = NULL,
                     cor = NULL,
                     nsim = NULL,
                     range = NULL,
                     data,
                     longitudinal,
                     method,
                     varest,
                     M = 200,
                     B,
                     convB = 0.0001,
                     convL = 0.0001,
                     convG = 0.0001,
                     maxiter = 100,
                     posfix,
                     partialH = FALSE,
                     verbose = FALSE,
                     nproc = 1){
  
    ptm <- proc.time()
    
    if(missing(model)) stop("model argument must be given")
    if(!inherits(model, c("hlme", "lcmm", "multlcmm", "Jointlcmm", "mpjlcmm"))) stop('primary model class must be either "hlme", "lcmm", "multlcmm", "Jointlcmm" or "mpjlcmm"')
    if(model$conv == 2) warning("primary model did not fully converge")
    if(sum(c(!missing(fixed), !missing(classmb), !missing(survival))) != 1) stop("One and only one in survival, fixed or classmb must be given")
    if(missing(method) | !method %in% c("twoStageJoint", "conditional")) stop('Method must be either "twoStageJoint" or "conditional"')
    if(model$ng == 1) stop("Primary model does not have latent class structure (ng=1)")
    if(method == "twoStageJoint" & missing(varest)) varest = "Hessian"
    if(method == "conditional" & missing(varest)) varest = "paramBoot"
    if(!varest %in% c("none", "paramBoot", "Hessian")) stop('Variance estimation method "varest" must be either "none", "paramBoot" or "Hessian"')
    if(!is.null(link) & missing(fixed)) stop("The argument link is not to be used with external class predictor")
    
    
    if(missing(posfix)) posfix = c()
    
    cl = match.call()
    
    ##Informations about primary model
    argumentsIn = as.list(model$call)
    funIn = as.character(argumentsIn[[1]])
    if(funIn == "jlcmm") funIn <- "Jointlcmm"
    if(funIn == "mlcmm") funIn <- "multlcmm"
    argumentsIn[[1]] = NULL
    ng = model$ng
    nIn = length(model$best)
    
    if(is.null(argumentsIn[["classmb"]])){
        oldclassmb = ~ 1
    } else {
        oldclassmb = formula(argumentsIn[["classmb"]])
    }
    if(!missing(classmb) & oldclassmb != ~1) stop("Primary model already has class predictor")
    
    ##number of MB parameters in primary model
    nInMB = ncol(model.matrix(oldclassmb, data))*(ng-1)
    
    ##Get subject
    if(missing(subject)){
        if (model$call$subject %in% colnames(data)){
            subject = model$call$subject
        } else {
            stop("The argument subject must be specified if different from the subject argument used in the primary model")
        }
    } 
    
    ##Get longitudinal
    if(funIn == "mpjlcmm"){
        if(missing(longitudinal)) stop("The argument longitudinal is mandatory with a mpjlcmm primary model")
        
        longCall = substitute(longitudinal)
        
        K = length(longitudinal)
        for(k in 1:K){
            cl_long = as.list(longitudinal[[k]]$call)
            if(inherits(longitudinal[[k]], "lcmm")) cl_long[["computeDiscrete"]] = FALSE
            longitudinal[[k]]$call = as.call(cl_long)
            
            assign(as.character(longCall[[k+1]]), longitudinal[[k]])
        }
    }
    
    ##nVCIn
    if(funIn == "mpjlcmm"){
        nVCIn = model$Nprm[3+2*model$K+(1:model$K)]
        iVCIn = c()
        prev = 0
        for(k in 1:model$K){
            iVCIn = c(iVCIn, sum(model$Nprm[c(1:3, 3:4*model$K-model$K+k-1)])+prev+1:nVCIn[k])
            prev = prev+model$npmK[k]
        }
    } else if(funIn == "Jointlcmm"){
        nVCIn = model$N[5]
        iVCIn = sum(model$N[1:4]) + 1:nVCIn
    } else if(funIn == "multlcmm"){
        nVCIn = model$N[4]
        iVCIn = sum(model$N[3]) + 1:nVCIn
    } else {
        nVCIn = model$N[3]
        iVCIn = sum(model$N[1:2]) + 1:nVCIn
    }
    
    ##pprob with new data
    argumentsInEdit = argumentsIn
    argumentsInEdit[["data"]] = data
    argumentsInEdit[["maxiter"]] = 0
    argumentsInEdit[["B"]] = model$best
    argumentsInEdit[["verbose"]] = FALSE
    modelEdit = do.call(funIn, argumentsInEdit)
    pprob = modelEdit$pprob
    
    
    arguments = list()
    
    ##Finding out the number of parameters is needed for all survival external outcome
    if(!missing(survival)){

        ##Informations about secondary outcome model
        ##number of survival parameters to estimate
        ##number of events
        surv <- cl$survival[[2]]
        if(length(surv)==3) #censure droite sans troncature gauche
        {
            idtrunc <- 0 
            nom.Tevent <- as.character(surv[2])
            nom.Event <- as.character(surv[3])
            nom.Tentry <- NULL #si pas de troncature, Tentry=0
            noms.surv <-  c(nom.Tevent,nom.Event) 
        }
        
        if(length(surv)==4) #censure droite et troncature
        {
            idtrunc <- 1 
            nom.Tentry <- as.character(surv[2])
            nom.Tevent <- as.character(surv[3])
            nom.Event <- as.character(surv[4])
            noms.surv <-  c(nom.Tentry,nom.Tevent,nom.Event)
        }  
        
        Tevent <- getElement(object=data,name=nom.Tevent)
        Event <- getElement(object=data,name=nom.Event)  
        nbevt <- length(attr(do.call("Surv",list(time=Tevent,event=Event,type="mstate")),"states")) 
        if(nbevt<1) nbevt <- 1
        
        ##get number of parameters for baseline functions
        hazard <- rep(hazard,length.out=nbevt)
        hazardtype <- rep(hazardtype,length.out=nbevt)
        if(any(hazard %in% c("splines","Splines")))
        {
            hazard[which(hazard %in% c("splines","Splines"))] <- "5-quant-splines" 
        }
        if(any(hazard %in% c("piecewise","Piecewise")))
        {
            hazard[which(hazard %in% c("piecewise","Piecewise"))] <- "5-quant-piecewise" 
        }
        
        hazWhat = hazard
        ##when not weibull, keep only the last word of hazard
        hazWhat[hazard != 'Weibull'] = sapply(strsplit(hazard[hazard != 'Weibull'], "-"), `[`, 3)
        hazN = rep(2, nbevt)
        hazN[hazard != 'Weibull'] = sapply(strsplit(hazard[hazard != 'Weibull'], "-"), `[`, 1)
        hazN = as.integer(hazN)
        hazN = hazN +
            (hazWhat == "Weibull")*0 +
            (hazWhat == "piecewise")*(-1) +
            (hazWhat == "splines")*(2)
        hazN = hazN + hazN *
            (hazardtype == "Specific")*(ng-1)
        
        ##we extract the number of base function parameter with constraints (ie, not PH parameters)
        nSurvConstraint = hazN
        
        hazN = hazN  +
            (hazardtype == "PH")*(ng-1)
        
        ##we now have all base functions parameters :
        nEstY = sum(hazN)
        
        ##get number of parameters for survival covariates
        form.surv <- cl$survival[3]
        
        noms.form.surv <- all.vars(attr(terms(formula(paste("~",form.surv))),"variables"))
        if(length(noms.form.surv)==0)
        {
            form.cause <- ~-1
            form.causek <- vector("list",nbevt)
            for(k in 1:nbevt) form.causek[[k]] <- ~-1
            form.mixture <- ~-1
            form.commun <- ~-1
            asurv <- terms(~-1)
        }
        else
        {
            ##creer la formula pour cause
            form1 <- gsub("mixture","",form.surv)
            form1 <- formula(paste("~",form1))
            asurv1 <- terms(form1,specials="cause")  
            ind.cause <- attr(asurv1,"specials")$cause
            if(length(ind.cause))
            {
                form.cause <- paste(labels(asurv1)[ind.cause],collapse="+")
                form.cause <- gsub("cause","",form.cause)
                form.cause <- formula(paste("~",form.cause))
            }
            else
            {
                form.cause <- ~-1 
            }
            
            ## formules pour causek
            form.causek <- vector("list",nbevt)
            for(k in 1:nbevt)
            {
                formk <- gsub("mixture","",form.surv)
                for(kk in 1:nbevt)
                {
                    if(kk != k) formk <- gsub(paste("cause",kk,sep=""),"",formk)
                }
                
                asurvk <- terms(formula(paste("~",formk)),specials=paste("cause",k,sep=""))
                ind.causek <- attr(asurvk,"specials")$cause
                
                if(length(ind.causek))
                {
                    formcausek <- paste(labels(asurvk)[ind.causek],collapse="+")
                    formcausek <- gsub(paste("cause",k,sep=""),"",formcausek)
                    formcausek <- formula(paste("~",formcausek))
                    form.causek[[k]] <- formcausek
                }
                else
                {
                    form.causek[[k]] <- ~-1
                }
            }
            
            
            
            ##creer la formule pour mixture
            form2 <- form.surv
            for( k in 1:nbevt)
            {
                form2 <- gsub(paste("cause",k,sep=""),"",form2)
            }
            form2 <- gsub("cause","",form2)
            form2 <- formula(paste("~",form2))         
            asurv2 <- terms(form2,specials="mixture") 
            ind.mixture <- attr(asurv2,"specials")$mixture
            if(length(ind.mixture))
            {
                form.mixture <- paste(labels(asurv2)[ind.mixture],collapse="+")
                form.mixture <- gsub("mixture","",form.mixture)
                form.mixture <- formula(paste("~",form.mixture))
            }
            else
            {
                form.mixture <- ~-1 
            }  
            
            ## creer la formule pour ni cause ni mixture
            asurv <- terms(formula(paste("~",form.surv)),specials=c("cause","mixture",paste("cause",1:nbevt,sep="")))
            ind.commun <- setdiff(1:length(labels(asurv)),unlist(attr(asurv,"specials")))
            if(length(ind.commun))
            {
                form.commun <- paste(labels(asurv)[ind.commun],collapse="+")
                form.commun <- gsub("mixture","",form.commun) #si X1*mixture(X2), alors X1:mixture(X2) dans form.commun
                form.commun <- gsub("cause","",form.commun)   # si X1:cause(X2)
                form.commun <- formula(paste("~",form.commun))  
                ##NB: si mixture(X1)*cause(X2), X1:X2 en commun
            }
            else
            {
                form.commun <- ~-1 
            }
        }
        
        ##I extract number of variable in each formula (through model.matrix) excluding intercept
        ncols = sapply(c(form.commun, form.cause, form.mixture, form.causek), function(x, data){
            mm = model.matrix(x, data)
            mm = mm[,-1]
            if(is.null(ncol(mm))) {return(1)}
            else {return(ncol(mm))}
        }, data = data)
        
        ##I also need how many parameters each kind of covariate makes
        nparam = c(1, nbevt, ng, nbevt*ng)
        
        ##we now have all the survival covariates parameters :
        nEstX = sum(ncols*nparam)
        
        nEst = nEstY + nEstX
    }
    
    ##A model structure is needed for all longitudinal external outcome
    if(!missing(fixed)){ 
        ##Manage inputs
        
        if(missing(mixture)) mixture = ~1
        if(missing(random)) random = ~-1
        
        if(!inherits(fixed,"formula")) stop("The argument fixed must be a formula")
        if(!inherits(mixture,"formula")) stop("The argument mixture must be a formula")
        if(!inherits(random,"formula")) stop("The argument random must be a formula")
        
        if(length(fixed[[2]]) != 1){
            argfunctionStrMod = "multlcmm"
        } else if(is.null(link)){
            argfunctionStrMod = "hlme"
        } else {
            argfunctionStrMod = "lcmm"
        }
        
        ##let's create structure for secondary model
        argumentsStrMod = list()
        argumentsStrMod[["fixed"]] = fixed
        argumentsStrMod[["random"]] = random
        argumentsStrMod[["subject"]] = subject
        argumentsStrMod[["ng"]] = 1
        argumentsStrMod[["idiag"]] = idiag
        argumentsStrMod[["randomY"]] = randomY
        if(argfunctionStrMod %in% c("lcmm", "multlcmm", "Jointlcmm")){
            argumentsStrMod[["link"]] = link
            argumentsStrMod[["intnodes"]] = intnodes
        }
        argumentsStrMod[["epsY"]] = epsY
        argumentsStrMod[["cor"]] = substitute(cor)
        argumentsStrMod[["nsim"]] = nsim
        argumentsStrMod[["range"]] = range
        argumentsStrMod[["data"]] = data
        argumentsStrMod[["maxiter"]] = 0
        argumentsStrMod[["verbose"]] = FALSE
        
        strMod = do.call(argfunctionStrMod, c(argumentsStrMod))
        
        argumentsStrMod[["mixture"]] = mixture
        argumentsStrMod[["classmb"]] = ~1
        argumentsStrMod[["ng"]] = ng
        argumentsStrMod[["nwg"]] = nwg
        argumentsStrMod[["B"]] = as.name("strMod")
        
        strMod = do.call(argfunctionStrMod, c(argumentsStrMod))
        
        strMod$best[1:ng+(ng-1)] = mean(strMod$best[1:ng+(ng-1)])# pourquoi?? tous les intercepts a la meme valeur. A revoir
        
    }
    
    ##Change general model arguments
    if(method == "twoStageJoint"){
        
        ##Common argument in twoStageJoint
        arguments[["data"]] = data
        arguments[["ng"]] = ng
        arguments[["subject"]] = subject
        ##technical options
        arguments[["maxiter"]] = maxiter
        arguments[["verbose"]] = verbose
        arguments[["nproc"]] = nproc
        arguments[["convB"]] = convB
        arguments[["convL"]] = convL
        arguments[["convG"]] = convG
        arguments[["partialH"]] = partialH
        ##primary survival
        arguments[["survival"]] =  argumentsIn[["survival"]]
        arguments[["hazard"]] =  argumentsIn[["hazard"]]
        arguments[["hazardtype"]] =  argumentsIn[["hazardtype"]]
        arguments[["hazardnodes"]] =  argumentsIn[["hazardnodes"]]
        arguments[["hazardrange"]] =  argumentsIn[["hazardrange"]]
        arguments[["TimeDepVar"]] =  argumentsIn[["TimeDepVar"]]
        arguments[["logscale"]] =  argumentsIn[["logscale"]]
        
        
        ##Primary Jointlcmm needs transformation into lcmm to be put into longitudinal.
        if(funIn == "Jointlcmm"){
            
            if(is.null(argumentsIn[["link"]])){
                argfunJoint = "hlme"
            } else {
                argfunJoint = "lcmm"
            }
            
            argumentsJoint = argumentsIn
            argumentsJoint[["survival"]] = NULL
            argumentsJoint[["hazard"]] = NULL
            argumentsJoint[["hazardtype"]] = NULL
            argumentsJoint[["hazardnodes"]] = NULL
            argumentsJoint[["hazardrange"]] = NULL
            argumentsJoint[["TimeDepVar"]] = NULL
            argumentsJoint[["logscale"]] = NULL
            
            argumentsJoint[["maxiter"]] = 0
            argumentsJoint[["mixture"]] = NULL
            argumentsJoint[["classmb"]] = NULL
            argumentsJoint[["ng"]] = 1
            argumentsJoint[["nwg"]] = FALSE
            argumentsJoint[["B"]] = NULL
            argumentsJoint[["verbose"]] = FALSE
            argumentsJoint[["data"]] = data
            
            modNoSurv = do.call(argfunJoint, argumentsJoint)
            
            argumentsJoint[["mixture"]] = argumentsIn[["mixture"]]
            argumentsJoint[["classmb"]]= ~1
            argumentsJoint[["ng"]] = argumentsIn[["ng"]]
            argumentsJoint[["nwg"]] = argumentsIn[["nwg"]]
            argumentsJoint[["B"]] = modNoSurv
            
            modNoSurv = do.call(argfunJoint, argumentsJoint)
        }
        
        
        ##Yextern survival
        if(!missing(survival)){
            ##manage inputs
            if(!is.null(argumentsIn[["survival"]])) stop('secondary survival model is not supported with "twoStageJoint" method if primary model already includes survival')
            
            funOut = "mpjlcmm"
            
            ##nOut : nuber of total final parameters
            nOut = nIn + nEst
            
            ##index
            iKeepOut = c(1:nInMB, nInMB+nEst+1:(nIn-nInMB))
            iKeepIn = 1:nIn
            iEst = nInMB+1:nEst
            
            ##input VC, only for keep betaa (iKeepIn)
            iVCKeep = iVCIn
            
            ##Id of varcov estimates
            iVCOut = c()
            
            ##list of arguments
            arguments[["survival"]] =  survival
            arguments[["hazard"]] =  hazard
            arguments[["hazardtype"]] =  hazardtype
            arguments[["hazardnodes"]] =  hazardnodes
            arguments[["TimeDepVar"]] =  TimeDepVar
            arguments[["logscale"]] =  logscale
            arguments[["posfix"]] = unique(c(iKeepOut, posfix))
            ##what is in longitudinal ?
            if(funIn == "mpjlcmm"){
                arguments[["longitudinal"]] = longitudinal
            } else {
                arguments[["longitudinal"]] = list(model)
            }
            ##initial values
            if(missing(B)){
                arguments[["B"]][iEst] = rep(0.1, nEst)
            } else {
                if(length(B) != length(iEst)) stop("B should be of length ", length(iEst))
                arguments[["B"]][iEst] = B
            }
        }
        
        ##Yextern longitudinal
        if(!missing(fixed)){
            
            funOut = "mpjlcmm"
            
            ##Let's change strMod's saved call
            strMod$call$data = substitute(data)
            
            ## Now join the primary and secondary model
            
            ##Informations about secondary outcome model
            ##number of classmb parameters to remove
            nMB = ng-1
            ##number of remaining parameters to estimate
            nStr = length(strMod$best)
            nEst = nStr - nMB
            ##nOut : nuber of total final parameters
            nOut = nIn + nEst
            
            ##index
            iKeepOut = 1:nIn
            iKeepIn = iKeepOut
            iEst = nIn+1:nEst
            
            ##input VC, only for keep betaa (iKeepIn)
            iVCKeep = iVCIn
            
            ##Id of varcov estimates
            if(inherits(strMod, "multlcmm")){
                nVCStr = strMod$N[4]
                iVCStr = sum(strMod$N[3]) + 1:nVCStr
            } else {
                nVCStr = strMod$N[3]
                iVCStr = sum(strMod$N[1:2]) + 1:nVCStr
            }
            iVCOut = nIn + iVCStr - nMB
            
            ##Liste des arguments
            ##on fixe nos parametres
            arguments[["posfix"]] = unique(c(iKeepOut, posfix))
            
            ##On donne les modeles
            if(funIn == "mpjlcmm"){
                arguments[["longitudinal"]] = c(longitudinal, list(strMod))
            } else if(funIn == "Jointlcmm"){
                arguments[["longitudinal"]] = list(modNoSurv, strMod)
            } else {
                arguments[["longitudinal"]] = list(model, strMod)
            } ## ici : B= prm modele entre puis prm modele ajoute
            
            ##initial values
            if(missing(B)){
                arguments[["B"]][iEst] = strMod$best[(nMB+1):nStr]
            } else {
                if(length(B) != length(iEst)) stop("B should be of length ", length(iEst))
                arguments[["B"]][iEst] = B
            }
        }
        
        ##X extern
        if(!missing(classmb)){
            
            if(!inherits(classmb,"formula")) stop("The argument classmb must be a formula")
            
            funOut = "mpjlcmm"
            
            ##nEst : number of MB parameters in output model
            nEst1G = ncol(model.matrix(classmb, data))
            nEst = nEst1G*(ng-1)
            
            ##nOut : nuber of total final parameters
            nOut = nIn - nInMB + nEst
            
            ##index
            iKeepOut = (nEst+1):nOut
            iKeepIn = (nInMB+1):nIn
            iEst = 1:nEst
            
            ##input VC, only for keep betas (iKeepIn)
            iVCKeep = iVCIn-nInMB
            
            ##Id of varcov estimates (none)
            iVCOut = c()
            
            ##On recree tous nos arguments
            if(funIn == "mpjlcmm"){
                arguments[["longitudinal"]] = longitudinal
            } else if(funIn == "Jointlcmm"){
                arguments[["longitudinal"]] = list(modNoSurv)
            } else {
                arguments[["longitudinal"]] = list(model)
            }
            arguments[["classmb"]] = classmb
            ##on ajoute des valeurs de base pour nos nouveaux estimateurs
            arguments[["B"]] = rep(0, nOut)
            ##initial values
            if(missing(B)){
                arguments[["B"]][1:ng-1] = model$best[1:ng-1]
            } else {
                if(length(B) != length(iEst)) stop("B should be of length ", length(iEst))
                arguments[["B"]][iEst] = B
            }
            
            ##on fixe nos parametres
            arguments[["posfix"]] = unique(c(posfix, iKeepOut)) 
        }
    }
    
    if(method == "conditional"){
        
        ##Yextern survival
        if(!missing(survival)){
            ## we need it all in a function in order to be able to use parametric bootstrap later on
            
            ##remaining parameters to estimate
            iEst = 1:nEst
            
            iKeepIn = 1:nIn
            iKeepOut = 1:nIn+nEst+2
            
            nOut = nEst+2
            
            ##Id of varcov estimates to keep in bootstrap
            iVCKeep = iVCIn
            iVCOut = c()
            nVCIn = 0
            
            ##We need what is inside of longitudinal to still exist in the worker
            argumentsIn[["longitudinal"]] = eval(argumentsIn[["longitudinal"]])
            
            conditionalS = function(model,
                                    data,
                                    survival,
                                    hazard,
                                    hazardtype,
                                    hazardnodes = NULL,
                                    TimeDepVar = NULL,
                                    logscale,
                                    subject,
                                    ng,
                                    B,
                                    link,
                                    iEst,
                                    maxiter,
                                    verbose,
                                    argumentsIn,
                                    funIn,
                                    nproc,
                                    convB,
                                    convL,
                                    convG){
                argumentsInEdit = argumentsIn
                argumentsInEdit[["B"]] = B[iKeepOut]
                argumentsInEdit[["maxiter"]] = 0
                argumentsInEdit[["verbose"]] = F
                argumentsInEdit[["nproc"]] = nproc
                argumentsInEdit[["data"]] = data
                model = do.call(funIn, argumentsInEdit)
                
                B = B[iEst]
                
                predCl = predictClass(model, data)
                
                ##First : let's compute P(C|\tilde C) (\tilde C : A)
                pAlY = sapply(1:ng, function(g){
                    return(as.numeric(predCl[,2] == g))
                })
                pClY = as.matrix(predCl[,3:(2+ng)])
                if(any(is.nan(pClY))) stop("NaN in posterior classification probability")
                
                pA = apply(pAlY, 2, mean)
                pClA = t(pAlY)%*%pClY/(model$ns*pA)
                
                if(det(pClA) == 0 | is.na(det(pClA))) stop("Computed error matrix is singular. One class might be empty")
                
                ##Then : let's add it to dataset for each individual
                indivProb = pAlY%*%pClA
                indivProb = cbind(predCl[,1], indivProb)
                colnames(indivProb)[1] = subject
                data = merge(data, indivProb, by = subject)
                
                ##We need dummy Y for the model to run
                data$dummyY = 1
                
                ##With some type of input model, prob does not have the same name
                if(!"prob1" %in% colnames(data)){
                    for(i in 1:ng){
                        data[[paste0("prob", i)]] = data[[paste0("probYT", i)]]
                    }
                }
                
                ##Finally : we need to build the model for ppriors !
                arguments = list()
                
                arguments[["data"]] = data
                arguments[["fixed"]] = dummyY~1 
                arguments[["mixture"]] =  ~-1
                arguments[["survival"]] =  survival
                arguments[["hazard"]] =  hazard
                arguments[["hazardtype"]] =  hazardtype
                arguments[["hazardnodes"]] =  hazardnodes
                arguments[["TimeDepVar"]] =  TimeDepVar
                arguments[["logscale"]] =  logscale
                arguments[["subject"]] = subject
                arguments[["classmb"]] = ~-1
                arguments[["ng"]] = ng
                arguments[["pprior"]] = c("prob1", "prob2")
                arguments[["posfix"]] = 1:2+length(iEst)
                ##technical options
                arguments[["maxiter"]] = maxiter
                arguments[["verbose"]] = verbose
                arguments[["nproc"]] = nproc
                arguments[["convB"]] = convB
                arguments[["convL"]] = convL
                arguments[["convG"]] = convG
                
                arguments[["B"]] = c(B[iEst], 1, 0.000001)
                
                res = do.call("Jointlcmm", arguments)
                res$call = match.call()
                return(res)
            }
            
            ##we need to build the model
            arguments[["data"]] = data
            arguments[["survival"]] =  survival
            arguments[["hazard"]] =  hazard
            arguments[["hazardtype"]] =  hazardtype
            arguments[["hazardnodes"]] =  hazardnodes
            arguments[["TimeDepVar"]] =  TimeDepVar
            arguments[["logscale"]] =  logscale
            arguments[["model"]] = model
            arguments[["subject"]] = subject
            arguments[["ng"]] = ng
            arguments[["link"]] = link
            arguments[["iEst"]] = iEst
            ##technical options
            arguments[["maxiter"]] = maxiter
            arguments[["verbose"]] = verbose
            arguments[["argumentsIn"]] = argumentsIn
            arguments[["funIn"]] = funIn
            arguments[["nproc"]] = nproc
            arguments[["convB"]] = convB
            arguments[["convL"]] = convL
            arguments[["convG"]] = convG
            
            ##on ajoute des valeurs de base pour nos nouveaux estimateurs
            arguments[["B"]] = rep(0.1, nIn+nOut)
            ##initial values
            if(!missing(B)){
                if(length(B) != length(iEst)) stop("B should be of length ", length(iEst))
                arguments[["B"]][iEst] = B
            }
            
            funOut = "conditionalS"
        }
        
        ##Yextern longitudinal
        if(!missing(fixed)){
            ## we need it all in a function in order to be able to use parametric bootstrap later on
            
            ##number of classmb parameters to remove
            nMB = ng-1
            ##number of remaining parameters to estimate
            nStr = length(strMod$best)
            nEst = nStr - nMB
            iEst = 1:nEst
            
            iKeepIn = 1:nIn 
            iKeepOut = 1:nIn+nEst 
            nOut = nEst
            
            ##Id of varcov estimates to keep in bootstrap
            iVCKeep = iVCIn
            ##Id of varcov estimates
            if(inherits(strMod, "multlcmm")){
                nVCStr = strMod$N[4]
                iVCStr = sum(strMod$N[3]) + 1:nVCStr
            } else {
                nVCStr = strMod$N[3]
                iVCStr = sum(strMod$N[1:2]) + 1:nVCStr
            }
            iVCOut = iVCStr - nMB
            
            nVCIn = 0
            
            ##We need what is inside of longitudinal to still exist in the worker
            argumentsIn[["longitudinal"]] = eval(argumentsIn[["longitudinal"]])
            
            conditional = function(model,
                                   data,
                                   fixed,
                                   random,
                                   idiag,
                                   nwg,
                                   randomY = NULL,
                                   link,
                                   intnodes = NULL,
                                   epsY = NULL,
                                   cor = NULL,
                                   nsim = NULL,
                                   range = NULL,
                                   subject,
                                   mixture,
                                   ng,
                                   B,
                                   iEst,
                                   maxiter,
                                   verbose,
                                   argumentsIn,
                                   funIn,
                                   nproc,
                                   convB,
                                   convL,
                                   convG){
                argumentsInEdit = argumentsIn
                argumentsInEdit[["B"]] = B[iKeepOut] ## B : prm model a estimer, prm model entree
                argumentsInEdit[["maxiter"]] = 0
                argumentsInEdit[["verbose"]] = F
                argumentsInEdit[["nproc"]] = nproc
                argumentsInEdit[["data"]] = data
                model = do.call(funIn, argumentsInEdit)
                
                B = B[iEst]
                ##predCl = predictClass(model, data) # pas utile # chgmt Viviane
                predCl <- model$pprob 

                ##First : let's compute P(C|\tilde C) (\tilde C : A)
                pAlY = sapply(1:ng, function(g){
                    return(as.numeric(predCl[,2] == g))
                })
                pClY = as.matrix(predCl[,3:(2+ng)])
                if(any(is.nan(pClY))) stop("NaN in posterior classification probability")
                
                pA = apply(pAlY, 2, mean)
                pClA = t(pAlY)%*%pClY/(model$ns*pA)
                
                if(det(pClA) == 0 | is.na(det(pClA))) stop("Computed error matrix is singular. One class might be empty")
                
                ##Then : let's add it to dataset for each individual
                indivProb = pAlY%*%pClA
                indivProb = cbind(predCl[,1], indivProb)
                colnames(indivProb)[1] = subject
                data = merge(data, indivProb, by = subject)
                
                ##Finally : we need to build the model for ppriors !
                arguments = list()
                
                if(length(fixed[[2]]) != 1){
                    funOut = "multlcmm"
                    arguments[["link"]] = link
                    arguments[["intnodes"]] = intnodes
                } else if(missing(link)){
                    funOut = "hlme"
                    arguments[["link"]] = NULL
                } else {
                    funOut = "lcmm"
                    arguments[["link"]] = link
                    arguments[["intnodes"]] = intnodes
                }
                
                ##With some type of input model, prob does not have the same name
                if(!"prob1" %in% colnames(data)){
                    for(i in 1:ng){
                        data[[paste0("prob", i)]] = data[[paste0("probYT", i)]]
                    }
                }
                
                arguments[["data"]] = data
                arguments[["fixed"]] = fixed
                arguments[["random"]] = random
                arguments[["idiag"]] = idiag
                arguments[["nwg"]] = nwg
                arguments[["randomY"]] = randomY
                arguments[["epsY"]] = epsY
                arguments[["cor"]] = substitute(cor)
                arguments[["nsim"]] = nsim
                arguments[["range"]] = range
                arguments[["subject"]] = subject
                arguments[["mixture"]] = mixture
                arguments[["classmb"]] = ~-1
                arguments[["ng"]] = ng
                arguments[["pprior"]] = c("prob1", "prob2")
                ##technical options
                arguments[["maxiter"]] = maxiter
                arguments[["verbose"]] = verbose
                arguments[["nproc"]] = nproc
                arguments[["convB"]] = convB
                arguments[["convL"]] = convL
                arguments[["convG"]] = convG
                
                arguments[["B"]] = B ## ici B = vi du modele secondaire
                
                res = do.call(funOut, arguments)
                res$call = match.call()
                return(res)
            }
            
            ##we need to build the model
            arguments[["data"]] = data
            arguments[["model"]] = model
            arguments[["fixed"]] = fixed
            arguments[["random"]] = random
            arguments[["idiag"]] = idiag
            arguments[["nwg"]] = nwg
            arguments[["randomY"]] = randomY
            arguments[["link"]] = link
            arguments[["intnodes"]] = intnodes
            arguments[["epsY"]] = epsY
            arguments[["cor"]] = substitute(cor)
            arguments[["nsim"]] = nsim
            arguments[["range"]] = range
            arguments[["subject"]] = subject
            arguments[["mixture"]] = mixture
            arguments[["ng"]] = ng
            arguments[["iEst"]] = iEst
            ##technical options
            arguments[["maxiter"]] = maxiter
            arguments[["verbose"]] = verbose
            arguments[["argumentsIn"]] = argumentsIn
            arguments[["funIn"]] = funIn
            arguments[["nproc"]] = nproc
            arguments[["convB"]] = convB
            arguments[["convL"]] = convL
            arguments[["convG"]] = convG
            
            
            ##on ajoute des valeurs de base pour nos nouveaux estimateurs
            arguments[["B"]] = rep(0, nIn+nOut) ## ici B = valeurs pour les 2 modeles
            ##initial values
            if(missing(B)){
                arguments[["B"]][iEst] = strMod$best[(nMB+1):nStr]
            } else {
                if(length(B) != length(iEst)) stop("B should be of length ", length(iEst))
                arguments[["B"]][iEst] = B
            }
            
            funOut = "conditional"
        }
        
        ##Xextern
        if(!missing(classmb)){
            ##we need it all in a function in order to be able to use parametric bootstrap later
            
            ##nEst : number of MB parameters in output model
            nEst1G = ncol(model.matrix(classmb, data))
            nEst = nEst1G*(ng-1)
            iEst = 1:nEst
            
            iKeepIn = 1:nIn
            iKeepOut = 1:nIn+nEst
            
            nOut = nEst
            
            ##Id of varcov estimates to keep in bootstrap
            iVCKeep = iVCIn
            ##Id of varcov estimates (none)
            iVCOut = c()
            nVCIn = 0

            ##We need what is inside of longitudinal to still exist in the worker
            argumentsIn[["longitudinal"]] = eval(argumentsIn[["longitudinal"]])
            
            conditionalX = function(classmb,
                                    data,
                                    ng,
                                    B,
                                    iKeepIn,
                                    iEst,
                                    argumentsIn,
                                    funIn,
                                    nproc,
                                    maxiter,
                                    verbose){
                argumentsInEdit = argumentsIn
                argumentsInEdit[["B"]] = B[iKeepOut]
                argumentsInEdit[["maxiter"]] = 0
                argumentsInEdit[["verbose"]] = F
                argumentsInEdit[["nproc"]] = nproc
                argumentsInEdit[["data"]] = data
                model = do.call(funIn, argumentsInEdit)
                
                B = B[iEst]
                
                predCl = predictClass(model, data)
                
                ##First : let's compute P(\tilde C|C) (\tilde C : A)
                pAlY = sapply(1:ng, function(g){
                    return(as.numeric(predCl[,2] == g))
                })
                pClY = as.matrix(predCl[,3:(2+ng)])
                if(any(is.nan(pClY))) stop("NaN in posterior classification probability")
                
                betas = c(model$best[1:(ng-1)], 0)
                pC = sapply(betas, function(b, betas){
                    exp(b)/sum(exp(betas))
                }, betas=betas)
                
                pAlC = t(pClY)%*%pAlY/(model$ns*pC)
                
                if(det(pAlC) == 0 | is.na(det(pAlC))) stop("Computed error matrix is singular. One class might be empty")
                
                
                ##Then : let's add the classification to the dataset for each individual
                indivProb = pAlY%*%t(pAlC)
                indivProb = cbind(predCl[,1], indivProb)
                colnames(indivProb) = c(subject, paste0("class", 1:ng))
                data = merge(data, indivProb, by = subject)
                
                for(id in unique(data[[subject]])){
                    if(sum(data[[subject]] == id) > 1) data = data[-which(data[[subject]] == id)[-1],]
                }
                
                ##negative log likelihood function
                nLL = function(beta, y, X) {
                    beta = matrix(ncol = ncol(y)-1, byrow = T, beta)
                    denom <- apply(exp(X%*%beta),1,sum) + 1
                    num_mat <- y*cbind(exp(X%*%beta), 1)
                    num <- apply(num_mat,1,sum)
                    vrais <- sum(log(num/denom))
                    return(-vrais)
                }
                
                ##frame
                argmf = list(
                    formula = classmb,
                    data = data
                )
                mf = do.call(model.frame, argmf)
                ns = nrow(mf)
                
                y = data[, paste0("class", 1:ng)]
                y = as.matrix(sapply(y, as.numeric))
                nBy = ncol(y)-1
                
                X = model.matrix(classmb, data)
                nEst = ncol(X)*nBy
                
                opt = mla(b=B, fn=nLL, y=y, X=X, print.info = verbose, nproc = nproc, maxiter = maxiter)
                
                namesX = c("intercept", colnames(X)[colnames(X) != "(Intercept)"])
                names(opt$b) = c(sapply(namesX, FUN = function(i){
                    return(paste0(i, " ", colnames(y)[-ng]))
                }))
                Names = list(Xnsnames = namesX,
                             ID = subject)
                
                N = c(nEst)
                
                res = list(best = opt$b,
                           V = opt$v,
                           conv = opt$istop,
                           loglik = -opt$fn.value,
                           ns = length(unique(data[[subject]])),
                           ng = ng,
                           idprob = rep(1, ncol(X)),
                           nv2 = ncol(X),
                           gconv = c(opt$ca, opt$cb, opt$rdm),
                           pprob = NULL,
                           Names = Names,
                           N = N,
                           call = match.call())
                
                return(res)
            }
            
            ##Finally : we need to build the model arguments
            arguments[["classmb"]] = classmb
            arguments[["data"]] = data
            arguments[["ng"]] = ng
            arguments[["iKeepIn"]] = iKeepIn
            arguments[["iEst"]] = iEst
            arguments[["argumentsIn"]] = argumentsIn
            arguments[["funIn"]] = funIn
            arguments[["nproc"]] = nproc
            arguments[["maxiter"]] = maxiter
            arguments[["verbose"]] = verbose
            
            ##on ajoute des valeurs de base pour nos nouveaux estimateurs
            arguments[["B"]] = rep(0, nIn+nOut)
            ##initial values
            if(!missing(B)){
                if(length(B) != length(iEst)) stop("B should be of length ", length(iEst))
                arguments[["B"]][iEst] = B
            }
            
            funOut = "conditionalX"
            
            
            
        }
    }

    if(varest != "paramBoot"){
        arguments[["B"]][iKeepOut] = model$best[iKeepIn]
        if(verbose){cat("Model estimation...\n\n")}
        ##Model Estimation
        modOut = do.call(funOut, c(arguments))
    }
    
    if(varest == "Hessian"){
        if(method != "twoStageJoint") stop("Hessian variance estimation method only avaliable for 'twoStageJoint' method")
        
        if(verbose){cat("Variance estimation...\n\n")}
        nb11 = length(model$best)
        V11 = matrix(0, nb11, nb11)
        V11[upper.tri(V11, diag=T)] = model$V
        V11[lower.tri(V11, diag=F)] = t(V11)[lower.tri(V11, diag=F)]
        n1 = model$ns
        V11 = V11[iKeepIn, iKeepIn]
        
        nb22 = length(modOut$best)
        V22 = matrix(0, nb22, nb22)
        V22[upper.tri(V22, diag=T)] = modOut$V
        V22[lower.tri(V22, diag=F)] = t(V22)[lower.tri(V22, diag=F)]
        saveV22 = V22
        V22 = V22[iEst, iEst]
        n2 = modOut$ns
        
        modOut
        if(nproc == 1){
            I12 = -hessienne(modOut)
        } else {
            I12 = -hessienne(modOut, method = "deriva", nproc = nproc)
        }
        I12 = I12[iEst, iKeepOut]
        
        V = V22*n2 + (V22*n2) %*% (I12/n2) %*% ((n2/n1)*(V11*n1)) %*% t(I12/n2) %*% (V22*n2)
        V = V/n2
        saveV22[iEst, iEst] = V
        V = saveV22
        
        modOut$V = V[upper.tri(V, diag = TRUE)]
    }
    
    ##Get Bootstrap Models
    if(varest == "paramBoot"){
        if(verbose){cat("Bootstrap estimation...\n\n")}
        est = estimates(model)
        
        Vin = matrix(0, length(est), length(est))
        Vin[upper.tri(Vin, diag = T)] = model$V
        Vin[lower.tri(Vin, diag = F)] = t(Vin)[lower.tri(t(Vin), diag=F)]
        
        est = est[iKeepIn]
        Vin = Vin[iKeepIn,iKeepIn]
        
        coefss = rmvnorm(M, est, Vin)
        coefss = as.data.frame(coefss)
        colnames(coefss) = names(model$best)[iKeepIn]
        ##we just need to build back varcov into the coefs instead of cholesky matrix
        coefss = apply(coefss, 1, function(coefs, model, data, iVCKeep){
            if(funIn == "mpjlcmm"){
                varcovMods = longitudinal
            } else {
                varcovMods = list(model)
            }
            
            chols = coefs[iVCKeep]
            model$cholesky = chols
            
            varcov = c()
            countChol = 0
            for(varcovMod in varcovMods){
                ncolRandMod = ncol(model.matrix(formula(varcovMod$call$random), data))
                
                if(varcovMod$idiag){
                    nChol = ncolRandMod-as.integer(funIn == "multlcmm")
                    
                    vc = chols[1:nChol+countChol]
                    
                    varcov = c(varcov, (vc^2))
                } else {
                    nChol = ncolRandMod*(ncolRandMod+1)/2-as.integer(funIn == "multlcmm")
                    
                    ##cholMatrix
                    cholMatrix = matrix(0, ncolRandMod, ncolRandMod)
                    cholsToMatrix = chols[1:nChol+countChol]
                    if(funIn == "multlcmm") cholsToMatrix = c(1, cholsToMatrix)
                    cholMatrix[upper.tri(cholMatrix, diag = T)] = cholsToMatrix
                    
                    vc = t(cholMatrix)%*%cholMatrix
                    varcov = c(varcov, vc[upper.tri(vc, diag = T)])
                    if(funIn == "multlcmm") varcov = varcov[-1]
                }
                
                countChol = countChol + nChol
            }
            coefs[iVCKeep] = varcov
            
            return(coefs)
        }, model = model, data = data, iVCKeep = iVCKeep)
        
        if(nproc > 1)
        {
            clust <- parallel::makeCluster(nproc)
            
            ##load all loaded packages
            packages = loadedNamespaces()
            for(pack in packages){
                clusterExport(clust, "pack", environment())
                clusterEvalQ(clust, require(pack, character.only = T))
            }
            
            survivalMissing = missing(survival)
            fixedMissing = missing(fixed)
            modOuts <- parApply(clust, coefss, 2, function(coefs, arguments, iKeepOut, funOut, iEst, survivalMissing, fixedMissing, logscale){
                arguments[["B"]][iKeepOut] = coefs
                arguments[["nproc"]] = 1
                
                ##Model Estimation
                modOut = do.call(funOut, c(arguments))


######## chgmt Viviane ########
        ## if(!fixedMissing){
        ##     ##cholesky not varcov as output in best
        ##     modOut$best = estimates(modOut)
            
        ##     ## abs value of link prm :
        ##     n = length(modOut$best)
        ##     if(inherits(modOut, "lcmm")){ #For lcmm
        ##         nLink <- n - sum(modOut$N[c(1:4,6)])
        ##     if(modOut$linktype == 2){ #with spline link
        ##       ##nSpl = n-sum(modOut$N[1:4]) #count number of link function parameters (the only one not in N)
        ##       ##modOut$best[n-2:nSpl+1+1] = abs(modOut$best[n-2:nSpl+1+1]) #all but the first
        ##       modOut$best[sum(modOut$N[1:4])+2:nLink] = abs(modOut$best[sum(modOut$N[1:4])+2:nLink]) #all but the first
        ##     } else { #rest of lcmm
        ##       modOut$best[sum(modOut$N[1:4])+nLink] = abs(modOut$best[sum(modOut$N[1:4])+nLink]) #only the last one
        ##     }
        ##   } else if (inherits(modOut, "hlme")){ #hlme
        ##     modOut$best[n] = abs(modOut$best[n]) #only the last one
        ##   } else if(inherits(modOut, "multlcmm")){
        ##     nPreLink = sum(modOut$N[3:8]) #number of parameters before the one for the link function
        ##     numSPL = 0
        ##     for (ny in 1:modOut$N[8]){
        ##       if (modOut$linktype[ny]==0) nLink = 2
        ##       if (modOut$linktype[ny]==1) nLink = 4
        ##       if (modOut$linktype[ny]==2){
        ##         numSPL <- numSPL+1
        ##         nLink = modOut$nbnodes[numSPL]+2
        ##         modOut$best[nPreLink+2:nLink] = abs(modOut$best[nPreLink+2:nLink])
        ##       } else {
        ##         modOut$best[nPreLink+nLink] = abs(modOut$best[nPreLink+nLink])
        ##       }
        ##       nPreLink = nPreLink+nLink
        ##     }
        ##   } else if(inherits(modOut, "mpjlcmm")){
        ##     #Residual Error : need to be the same sign across bootstrap iterations
        ##     nPre = sum(modOut$N[1:(2+modOut$nbevt)])
        ##     sumny = 0
        ##     for(k in 1:modOut$K){
        ##       if(modOut$contrainte[k] == 2){ ## attention : length(nrisq) = nbevt!!
        ##         nPre = nPre + sum(modOut$Nprm[2+modOut$nbevt+1:7*modOut$K-modOut$K+k]) #add number of parameters before the one for the link function
        ##       } else {
        ##         nPre = nPre + sum(modOut$Nprm[2+modOut$nbevt+1:8*modOut$K-modOut$K+k]) #add the number of parameters for this K
        ##       }
        ##       for(y in 1:modOut$ny[k]){
        ##         sumny = sumny+1
                
        ##         if(modOut$contrainte[k] == 1 & modOut$linktype[sumny] == 2){ #for lcmm with spl link
        ##           nSpl = sum(modOut$Nprm[2+modOut$nbevt+7*modOut$K+sumny]) #count number of link function parameters (the only one not in N)
        ##           modOut$best[nPre-2:nSpl+1+1] = abs(modOut$best[nPre-2:nSpl+1+1]) #all but the first
        ##         } else if (modOut$contrainte[k] == 0 | modOut$contrainte[k] == 1){ #hlme ou le reste de lcmm
        ##           modOut$best[nPre] = abs(modOut$best[nPre]) #only the last one
        ##         } else if (modOut$contrainte[k] == 2) {
        ##           numSPL = 0
        ##           if (modOut$linktype[sumny]==0) nLink = 2
        ##           if (modOut$linktype[sumny]==1) nLink = 4
        ##           if (modOut$linktype[sumny]==2){
        ##             numSPL <- numSPL+1
        ##             nLink = modOut$call$longitudinal[[k]]$nbnodes[numSPL]+2
        ##             modOut$best[nPre+2:nLink] = abs(modOut$best[nPre+2:nLink])
        ##           } else {
        ##             modOut$best[nPre+nLink] = abs(modOut$best[nPre+nLink])
        ##           }
        ##           nPre = nPre+nLink
        ##         }
        ##       }
        ##     }
        ##   }
        ## }
        
        ## ##nw & cor & alea
        ## idnw <- NULL
        ## idalea <- NULL
        ## idcor <- NULL
        ## if(inherits(modOut, "multlcmm")){#with multlcmm
        ##   if(modOut$N[5] != 0) idnw = sum(modOut$N[3:4])+1:modOut$N[5]
        ##   if(modOut$N[6] != 0) idalea = sum(modOut$N[3:5])+1:modOut$N[6]
        ##   if(modOut$N[7] != 0) idcor = sum(modOut$N[3:6])+modOut$N[7]
        ## } else if(inherits(modOut, "mpjlcmm")){#with mpjlcmm
        ##   nPre = sum(modOut$N[1:3])
        ##   idnw = c()
        ##   idcor = c()
        ##   idalea = c()
        ##   for(k in 1:modOut$K){
        ##     nprenw = nPre+sum(modOut$Nprm[c(3+1:3*modOut$K-modOut$K+k)])
        ##     if(modOut$Nprm[3+4*modOut$K-modOut$K+k] != 0){
        ##       idnw = c(idnw, nprenw+1:modOut$Nprm[3+4*modOut$K-modOut$K+k])
        ##     }
            
        ##     nprecor = nPre+sum(modOut$Nprm[c(3+1:4*modOut$K-modOut$K+k)])
        ##     if(modOut$Nprm[3+5*modOut$K-modOut$K+k] != 0){
        ##       idcor = c(idcor, nprecor+modOut$Nprm[3+5*modOut$K-modOut$K+k])
        ##     }
            
        ##     nprealea = nPre+sum(modOut$Nprm[c(3+1:6*modOut$K-modOut$K+k)])
        ##     if(modOut$Nprm[3+7*modOut$K-modOut$K+k] != 0){
        ##       idalea = c(idalea, nprealea+1:modOut$Nprm[3+7*modOut$K-modOut$K+k])
        ##     }
            
        ##     nPre = nPre+sum(modOut$Nprm[c(3+1:8*modOut$K-modOut$K+k)])
        ##   }
        ## } else {#with lcmm & hlme
        ##   if(modOut$N[4] != 0) idnw = sum(modOut$N[1:3])+1:modOut$N[4]
        ##   if(modOut$N[5] != 0) idcor = sum(modOut$N[1:4])+modOut$N[5]
        ## }
        ## modOut$best[idnw] = abs(modOut$best[idnw])
        ## modOut$best[idcor] = abs(modOut$best[idcor])
        ## modOut$best[idalea] = abs(modOut$best[idalea])
        
        ## #Survival Base Function : need to be the same sign across bootstrap iterations
        ## if(!survivalMissing & !logscale){
        ##   iSurvConstraint = 1:nSurvConstraint+modOut$N[1]
        ##   modOut$best[iSurvConstraint] = abs(modOut$best[iSurvConstraint])
        ## }
######## fin chgmt Viviane ########

        if(inherits(modOut, c("hlme", "lcmm", "multlcmm", "Jointlcmm", "mpjlcmm"))) modOut <- absprm(modOut) ## remplace ce que j'ai mis en commentaire
        ## si model multinomial, pas besoin de valeur absolue dans les prm
        
        return(modOut)
      }, arguments = arguments, iKeepOut = iKeepOut, funOut = funOut, iEst = iEst, survivalMissing = survivalMissing, fixedMissing = fixedMissing, logscale = logscale)
      parallel::stopCluster(clust)
      
      ##format output
      bests = as.data.frame(matrix(NA, nrow = nEst, ncol = M))
      Vs = list()
      conv = c()
      
      modOut = modOuts[[M]]
      for (i in 1:M){
        ##output V and betas
        bests[,i] = modOut$best[iEst]
        
        V = matrix(NA, nOut, nOut)
        V[upper.tri(V, diag = T)] = modOut$V
        V[lower.tri(V, diag = F)] = t(V)[lower.tri(t(V), diag = F)]
        Vs = c(Vs, list(V[iEst, iEst]))
        
        conv = c(conv, modOut$conv)
      }
    } else {
      ##estimate final models, extract usefull information
      bests = as.data.frame(matrix(NA, nrow = nEst, ncol = M))
      Vs = list()
      conv = c()
      for(i in 1:M){
        if(verbose) cat("==================== Bootstrap Iteration", i, "====================\n")
        
        arguments[["B"]][iKeepOut] = coefss[,i]
        
        ##Model Estimation
        if(verbose){
          modOut = do.call(funOut, c(arguments))
        } else {
          captured_log = capture.output({modOut = do.call(funOut, c(arguments))})
        }

######## chgmt Viviane ########
        ## if(!missing(fixed)){
        ##   #cholesky not varcov as output in best
        ##   modOut$best = estimates(modOut)

        ##   n = length(modOut$best)
        ##   if(inherits(modOut, "lcmm")){ #For lcmm
        ##     if(modOut$linktype == 2){ #with spline link
        ##       nSpl = n-sum(modOut$N[1:4]) #count number of link function parameters (the only one not in N)
        ##       modOut$best[n-2:nSpl+1+1] = abs(modOut$best[n-2:nSpl+1+1]) #all but the first
        ##     } else { #rest of lcmm
        ##       modOut$best[n] = abs(modOut$best[n]) #only the last one
        ##     }
        ##   } else if (inherits(modOut, "hlme")){ #hlme
        ##     modOut$best[n] = abs(modOut$best[n]) #only the last one
        ##   } else if(inherits(modOut, "multlcmm")){
        ##     nPreLink = modOut$N[1:5] #number of parameters before the one for the link function
        ##     numSPL = 0
        ##     for (ny in 1:modOut$N[8]){
        ##       if (modOut$linktype[ny]==0) nLink = 2
        ##       if (modOut$linktype[ny]==1) nLink = 4
        ##       if (modOut$linktype[ny]==2){
        ##         numSPL <- numSPL+1
        ##         nLink = modOut$nbnodes[numSPL]+2
        ##         modOut$best[nPreLink+2:nLink] = abs(modOut$best[nPreLink+2:nLink])
        ##       } else {
        ##         modOut$best[nPreLink+nLink] = abs(modOut$best[nPreLink+nLink])
        ##       }
        ##       nPreLink = nPreLink+nLink
        ##     }
        ##   } else if(inherits(modOut, "mpjlcmm")){
        ##     #Residual Error : need to be the same sign across bootstrap iterations
        ##     nPre = sum(modOut$N[1:3])
        ##     sumny = 0
        ##     for(k in 1:modOut$K){
        ##       if(modOut$contrainte[k] == 2){
        ##         nPre = nPre + sum(modOut$Nprm[3+1:7*modOut$K-modOut$K+k]) #add number of parameters before the one for the link function
        ##       } else {
        ##         nPre = nPre + sum(modOut$Nprm[3+1:8*modOut$K-modOut$K+k]) #add the number of parameters for this K
        ##       }
        ##       for(y in 1:modOut$ny[k]){
        ##         sumny = sumny+1
                
        ##         if(modOut$contrainte[k] == 1 & modOut$linktype[sumny] == 2){ #for lcmm with spl link
        ##           nSpl = sum(modOut$Nprm[3+7*modOut$K+1*modOut$K-modOut$K+k]) #count number of link function parameters (the only one not in N)
        ##           modOut$best[nPre-2:nSpl+1+1] = abs(modOut$best[nPre-2:nSpl+1+1]) #all but the first
        ##         } else if (modOut$contrainte[k] == 0 | modOut$contrainte[k] == 1){ #hlme ou le reste de lcmm
        ##           modOut$best[nPre] = abs(modOut$best[nPre]) #only the last one
        ##         } else if (modOut$contrainte[k] == 2) {
        ##           numSPL = 0
        ##           if (modOut$linktype[sumny]==0) nLink = 2
        ##           if (modOut$linktype[sumny]==1) nLink = 4
        ##           if (modOut$linktype[sumny]==2){
        ##             numSPL <- numSPL+1
        ##             nLink = modOut$call$longitudinal[[k]]$nbnodes[numSPL]+2
        ##             modOut$best[nPre+2:nLink] = abs(modOut$best[nPre+2:nLink])
        ##           } else {
        ##             modOut$best[nPre+nLink] = abs(modOut$best[nPre+nLink])
        ##           }
        ##           nPre = nPre+nLink
        ##         }
        ##       }
        ##     }
        ##   }
        ## }
        
        ## ##nw & cor & alea
        ## idnw <- NULL
        ## idalea <- NULL
        ## idcor <- NULL
        ## if(inherits(modOut, "multlcmm")){#with multlcmm
        ##   if(modOut$N[5] != 0) idnw = sum(modOut$N[3:4])+1:modOut$N[5]
        ##   if(modOut$N[6] != 0) idalea = sum(modOut$N[3:5])+1:modOut$N[6]
        ##   if(modOut$N[7] != 0) idcor = sum(modOut$N[3:6])+modOut$N[7]
        ## } else if(inherits(modOut, "mpjlcmm")){#with mpjlcmm
        ##   nPre = sum(modOut$N[1:3])
        ##   idnw = c()
        ##   idcor = c()
        ##   idalea = c()
        ##   for(k in 1:modOut$K){
        ##     nprenw = nPre+sum(modOut$Nprm[c(3+1:3*modOut$K-modOut$K+k)])
        ##     if(modOut$Nprm[3+4*modOut$K-modOut$K+k] != 0){
        ##       idnw = c(idnw, nprenw+1:modOut$Nprm[3+4*modOut$K-modOut$K+k])
        ##     }
            
        ##     nprecor = nPre+sum(modOut$Nprm[c(3+1:4*modOut$K-modOut$K+k)])
        ##     if(modOut$Nprm[3+5*modOut$K-modOut$K+k] != 0){
        ##       idcor = c(idcor, nprecor+modOut$Nprm[3+5*modOut$K-modOut$K+k])
        ##     }
            
        ##     nprealea = nPre+sum(modOut$Nprm[c(3+1:6*modOut$K-modOut$K+k)])
        ##     if(modOut$Nprm[3+7*modOut$K-modOut$K+k] != 0){
        ##       idalea = c(idalea, nprealea+1:modOut$Nprm[3+7*modOut$K-modOut$K+k])
        ##     }
            
        ##     nPre = nPre+sum(modOut$Nprm[c(3+1:8*modOut$K-modOut$K+k)])
        ##   }
        ## } else {#with lcmm
        ##     if(inherits(modOut, "lcmm")){
        ##         if(modOut$N[4] != 0) idnw = sum(modOut$N[1:3])+1:modOut$N[4]
        ##         if(modOut$N[6] != 0) idcor = sum(modOut$N[1:4])+ntr+modOut$N[5]
        ##     }
        ##     else { #hlme
        ##         if(inherits(modOut, "hlme")){
        ##             if(modOut$N[4] != 0) idnw = sum(modOut$N[1:3])+1:modOut$N[4]
        ##             if(modOut$N[5] != 0) idcor = sum(modOut$N[1:4])+modOut$N[5]
        ##         }       
        ##     }
        ## }
        ## modOut$best[idnw] = abs(modOut$best[idnw])
        ## modOut$best[idcor] = abs(modOut$best[idcor])
        ## modOut$best[idalea] = abs(modOut$best[idalea])
        
        ## #Survival Base Function constraint : need to be the same sign across bootstrap iterations
        ## if(!missing(survival) & !logscale){
        ##   iSurvConstraint = 1:nSurvConstraint+modOut$N[1]
        ##   modOut$best[iSurvConstraint] = abs(modOut$best[iSurvConstraint])
        ## }
######## chgmt Viviane ########

        if(inherits(modOut, c("hlme", "lcmm", "multlcmm", "Jointlcmm", "mpjlcmm"))) modOut <- absprm(modOut) ## remplace ce que j'ai mis en commentaire
        ## si model multinomial, pas besoin de valeur absolue dans les prm
        
        ##output V and betas
        bests[,i] = modOut$best[iEst]
        
        V = matrix(NA, nOut, nOut)
        V[upper.tri(V, diag = T)] = modOut$V
        V[lower.tri(V, diag = F)] = t(V)[lower.tri(t(V), diag = F)]
        Vs = c(Vs, list(V[iEst, iEst]))
        
        conv = c(conv, modOut$conv)
      }
    }
    
    Mconv = sum(conv %in% c(1,3))
    if(Mconv <= 1){
      stop("No parametric boostrap iteration could converge.")
    }
    
    ##compute variance
    bests = bests[,conv %in% c(1,3)]
    mb = apply(bests, 1, mean, na.rm = T)
    
    Vs = Vs[conv %in% c(1,3)]
    V2 = Reduce("+", Vs)/Mconv
    V1 = (Mconv+1)/((Mconv-1)*Mconv) * Reduce("+", lapply(bests, function(best){
      (best-mb)%*%t(best-mb)
    }))
    covar = V2 + V1
    V = matrix(0, nOut, nOut)
    V[iEst, iEst] = covar
    
    ##Create output model, based on last estimated bootstrap model structure
    modOut$best[iKeepOut] = model$best[iKeepIn]
    modOut$best[iEst] = mb
    modOut$V = V[upper.tri(V, diag = T)]
    modOut$Mconv = Mconv
    
    ##replace chol for varcov in best
    if(!missing(fixed)){
      modOut$cholesky[-(1:sum(nVCIn))*(nVCIn != 0)] = modOut$best[iVCOut]
      if(idiag & inherits(modOut, "mpjlcmm")){ #idiag but not multlcmm because diff $chol structure
        if(sum(nVCIn) == 0){
          modOut$best[iVCOut] = modOut$cholesky^2
        } else {
          modOut$best[iVCOut] = modOut$cholesky[-(1:sum(nVCIn))]^2
        }
      } else if(inherits(modOut, "mpjlcmm")) { #only mpj (twoStage), because $contrainte is used to get multlcmm
        isMult = as.integer(modOut$contrainte[modOut$K] == 2)
        NVC = sqrt(2*(length(modOut$cholesky)+isMult-sum(nVCIn))+1/4)-1/2
        cholMatrix = matrix(0, NVC, NVC)
        chols = modOut$best[iVCOut]
        if(modOut$contrainte[modOut$K] == 2) chols = c(1, chols) #mpjlcmm & mult : no 1 in $chol
        cholMatrix[upper.tri(cholMatrix, diag = T)] = chols
        vc = t(cholMatrix)%*%cholMatrix
        vc = vc[upper.tri(vc, diag = T)]
        if(modOut$contrainte[modOut$K] == 2) vc = vc[-1]
        modOut$best[iVCOut] = vc
      } else { #for conditional, but mult can be idiag. Because $chol structure is full for mult even with idiag
        NVC = sqrt(2*(length(modOut$cholesky)-nVCIn)+1/4)-1/2
        cholMatrix = matrix(0, NVC, NVC)
        chols = modOut$best[iVCOut]
        if(inherits(modOut, "multlcmm")) chols = c(1, chols)
        if(idiag){
          chols_idiag = c()
          for(i in 1:NVC){
            chols_idiag = c(chols_idiag, rep(0, i-1), chols[i])
          }
          chols = chols_idiag
        }
        cholMatrix[upper.tri(cholMatrix, diag = T)] = chols
        vc = t(cholMatrix)%*%cholMatrix
        if(idiag){
          vc = diag(vc)
        } else {
          vc = vc[upper.tri(vc, diag = T)]
        }
        if(inherits(modOut, "multlcmm")) vc = vc[-1]
        modOut$best[iVCOut] = vc
      }
    }
    
    ##Computations on final model
    z = modOut$call
    z$posfix = NULL
    z$B = modOut$best
    z$maxiter = 0
    z$verbose = FALSE
    modelEdit = eval(z)
    if(method == "twoStageJoint") modOut$pprob = modelEdit$pprob
    if(!missing(survival)) modOut$predSurv = modelEdit$predSurv
    modOut$loglik = modelEdit$loglik
    if(method == "conditional") modOut$best = modOut$best[iEst]
  }
  
  modOut$call$data = substitute(data)
  
  
  ##On remet longicall comme considere dans environnement present, pas dans celui de mpjlcmm
  if(inherits(modOut, "mpjlcmm")){
    longicall = vector("list", modOut$K)
    for(k in 1:modOut$K){
      longicall[[k]] = eval(modOut$call$longitudinal)[[k]]$call
    }
    modOut$longicall = longicall
  }
  
  cost = proc.time()-ptm
  
  ##Object Class Creation and transformation from mpjlcmm
  if(!missing(survival)){
    if(method == "twoStageJoint"){
      best = modOut$best[iEst]
      V = matrix(NA, nOut, nOut)
      V[upper.tri(V, diag = T)] = modOut$V
      V = V[iEst, iEst]
      V = V[upper.tri(V, diag = T)]
      
      ##select output
      N = modOut$N[c(2:3, 11+modOut$K+modOut$nbevt)]
      Nprm = modOut$Nprm[2:(2+modOut$nbevt)]
      Names = modOut$Names[c("Xnsnames", "ID", "Tnames", "TimeDepVar.name")]
      Names[["Xnsnames"]] = Names[["Xnsnames"]][as.logical(modOut$idspecif)]
      levels = modOut$levels[c("levelsdata", "levelssurv")]
      
      modOut = list(nbevt = modOut$nbevt, ng = modOut$ng, ns = modOut$ns, idcom = modOut$idcom,
                    idspecif = modOut$idspecif, idtdv = modOut$idtdv, loglik = modOut$loglik,
                    best = best, V = V, gconv = modOut$gconv, conv = modOut$conv, call = cl,
                    niter = modOut$niter, N = N, Nprm = Nprm, pprob = pprob, Names = Names,
                    logspecif = modOut$logspecif, predSurv = modOut$predSurv, typrisq = modOut$typrisq,
                    hazardtype = modOut$hazardtype, hazardnodes = modOut$hazardnodes, nz = modOut$nz,
                    scoretest = modOut$scoretest, na.action = modOut$na.action, levels = levels, 
                    AIC = 2*(length(best)-length(posfix)-modOut$loglik),
                    BIC = (length(best)-length(posfix))*log(modOut$ns)-2*modOut$loglik,
                    varest = varest, runtime = cost[3])
      if(varest == "paramBoot"){
        modOut$Mconv = round(Mconv/M*100, 1)
        modOut$conv = as.integer(modOut$Mconv > 90)
      }
    }
    if(method == "conditional"){
      data$dummyY = 1
      subLoglik = hlme(dummyY~1,
                       data = data,
                       subject = subject,
                       maxiter = 0,
                       B = c(1, 0.000001))$loglik
      loglik = modOut$loglik - subLoglik
      
      best = modOut$best[iEst]
      V = matrix(NA, nOut, nOut)
      V[upper.tri(V, diag = T)] = modOut$V
      V = V[iEst, iEst]
      V = V[upper.tri(V, diag = T)]
      
      N = modOut$N[c(2:3, 10)]
      Nprm = modOut$N[2:3]
      Names = modOut$Names[c("Xnames2", "ID", "Tnames", "TimeDepVar.name")]
      names(Names)[names(Names) == "Xnames2"] = "Xnsnames"
      levels = modOut$levels[c("levelsdata", "levelssurv")]
      
      
      modOut = list(nbevt = 1, ng = modOut$ng, ns = modOut$ns, idcom = modOut$idcom,
                    idspecif = modOut$idspecif, idtdv = modOut$idtdv, loglik = loglik,
                    best = best, V = V, gconv = modOut$gconv, conv = modOut$conv, call = cl,
                    niter = modOut$niter, N = N, Nprm = Nprm, pprob = pprob, Names = Names,
                    logspecif = modOut$logspecif, predSurv = modOut$predSurv, typrisq = modOut$hazard[[1]],
                    hazardtype = modOut$hazard[[2]], hazardnodes = modOut$hazard[[3]], nz = modOut$hazard[[4]],
                    scoretest = modOut$scoretest, na.action = modOut$na.action, levels = levels,
                    AIC = 2*(length(best)-length(posfix)-loglik),
                    BIC = (length(best)-length(posfix))*log(modOut$ns)-2*loglik,
                    varest = varest, runtime = cost[3])
      if(varest == "paramBoot"){
        modOut$Mconv = round(Mconv/M*100, 1)
        modOut$conv = as.integer(modOut$Mconv > 90)
      }
    }

    class(modOut) = c("externSurv", "externVar")
    
  }
  if(!missing(fixed)){
    if(method == "twoStageJoint"){
      
      ##Get info
      gconv = modOut$gconv
      conv = modOut$conv
      niter = modOut$niter
      
      ##With exclusively longitudinal external outcome
      modUpdate = update(modOut)
      modOut = modUpdate[[length(modUpdate)]]
      
      modOut$gconv = gconv
      modOut$conv = conv
      modOut$niter = niter
      modOut$pprob = pprob
      if(inherits(modOut, "multlcmm")) modOut$N[3] = modOut$N[3]-modOut$N[1]
      modOut$N[1] = 0
      modOut$idprob0 = rep(0, length(modOut$idprob0))
      modOut$call = cl
      modOut$varest = varest
      modOut$runtime = cost[3]
      
      V = matrix(NA, length(modOut$best), length(modOut$best))
      V[upper.tri(V, diag = T)] = modOut$V
      V[lower.tri(V, diag = F)] = t(V)[lower.tri(t(V), diag = F)]
      V = V[-c(1:(modOut$ng-1)), -c(1:(modOut$ng-1))]
      V = V[upper.tri(V, diag = T)]
      
      modOut$V = V
      modOut$best = modOut$best[-c(1:nMB)]
      if(varest == "paramBoot"){
        modOut$Mconv = round(Mconv/M*100, 1)
        modOut$conv = as.integer(modOut$Mconv > 90)
      }
      
      class(modOut) = c(class(modOut), "externVar")
    }
    if(method == "conditional"){
      ##Get info
      modOut$pprob = pprob
      if(inherits(modOut, "multlcmm")) modOut$N[3] = modOut$N[3]-modOut$N[1]
      modOut$call = cl
      modOut$varest = varest
      modOut$runtime = cost[3]
      if(varest == "paramBoot"){
        modOut$Mconv = round(Mconv/M*100, 1)
        modOut$conv = as.integer(modOut$Mconv > 90)
      }
      
      class(modOut) = c(class(modOut), "externVar")
    }
  }
  if(!missing(classmb)){
    V = matrix(NA, nOut, nOut)
    V[upper.tri(V, diag = T)] = modOut$V
    V[lower.tri(V, diag = F)] = t(V)[lower.tri(t(V), diag = F)]
    V = V[iEst, iEst]
    V = V[upper.tri(V, diag = T)]
    
    best = modOut$best[iEst]
    
    ##Select output
    N = modOut$N[1]
    Names = modOut$Names[c("Xnsnames", "ID")]
    levels = modOut$levels[c("levelsdata", "levelsclassmb")]
    
    modOut = list(ng = modOut$ng, ns = modOut$ns, idprob = modOut$idprob, nv2 = modOut$nv2,
                  loglik = modOut$loglik, best = best, V = V, gconv = modOut$gconv,
                  conv = modOut$conv, call = cl, niter = modOut$niter, N = N,
                  pprob = modOut$pprob, Names = Names, na.action = modOut$na.action,
                  AIC = 2*(length(best)-length(posfix)-modOut$loglik),
                  BIC = (length(best)-length(posfix))*log(modOut$ns)-2*modOut$loglik,
                  varest = varest, method = method, runtime = cost[3])
    if(varest == "paramBoot"){
      modOut$Mconv = round(Mconv/M*100, 1)
      modOut$conv = as.integer(modOut$Mconv > 90)
    }
    
    class(modOut) = c("externX", "externVar")
  }
  
  if(verbose){cat(paste("The externVar program took", round(cost[3],2), "seconds\n"))}
  
  return(modOut)
  
}
CecileProust-Lima/lcmm documentation built on March 3, 2024, 5:23 p.m.