R/autoadam.R

Defines functions auto.adam

Documented in auto.adam

#' @param parallel If TRUE, the estimation of ADAM models is done in parallel (used in \code{auto.adam} only).
#' If the number is provided (e.g. \code{parallel=41}), then the specified number of cores is set up.
#' WARNING! Packages \code{foreach} and either \code{doMC} (Linux and Mac only)
#' or \code{doParallel} are needed in order to run the function in parallel.
#' @param outliers Defines what to do with outliers: \code{"ignore"}, so just returning the model,
#' \code{"detect"} outliers based on specified \code{level} and include dummies for them in the model,
#' or detect and \code{"select"} those of them that reduce \code{ic} value.
#' @param level What confidence level to use for detection of outliers. The default is 99.9%. The statistics
#' values depends on the distribution used in the model.
#' @param fast If \code{TRUE}, then some of the orders of ARIMA are
#' skipped in the order selection. This is not advised for models with \code{lags} greater than 12.
#'
#' @examples
#' ourModel <- auto.adam(rnorm(100,100,10), model="ZZN", lags=c(1,4), orders=list(ar=c(2,2),ma=c(2,2),select=TRUE))
#'
#' @rdname adam
#' @export
auto.adam <- function(y, model="ZXZ", lags=c(frequency(y)), orders=list(ar=c(0),i=c(0),ma=c(0),select=FALSE),
                      formula=NULL, outliers=c("ignore","use","select"), level=0.99,
                      distribution=c("dnorm","dlaplace","ds",
                                     "dlnorm","dllaplace","dls","dinvgauss"),
                      h=0, holdout=FALSE,
                      persistence=NULL, phi=NULL, initial=c("optimal","backcasting"), arma=NULL,
                      occurrence=c("none","auto","fixed","general","odds-ratio","inverse-odds-ratio","direct"),
                      ic=c("AICc","AIC","BIC","BICc"), bounds=c("usual","admissible","none"),
                      xreg=NULL, xregDo=c("use","select","adapt"),
                      silent=TRUE, parallel=FALSE, fast=TRUE, ...){
    # Copyright (C) 2020 - Inf  Ivan Svetunkov

    # Start measuring the time of calculations
    startTime <- Sys.time();

    # paste0() is needed in order to get rid of potential issues with names
    responseName <- paste0(deparse(substitute(y)),collapse="");

    #### modelDo, ic ####
    if(any(unlist(strsplit(model,""))=="C")){
        modelDo <- "combine";
    }
    else{
        modelDo <- "select";
    }

    ic <- match.arg(ic,c("AICc","AIC","BIC","BICc"));
    ICFunction <- switch(ic,
                         "AIC"=AIC,
                         "AICc"=AICc,
                         "BIC"=BIC,
                         "BICc"=BICc);

    initial <- match.arg(initial);
    outliers <- match.arg(outliers);

    # Check the provided level value.
    if(length(level)>1){
        warning(paste0("Sorry, but we only support scalar for the level, ",
                       "when constructing in-sample prediction interval. ",
                       "Using the first provided value."),
                call.=FALSE);
        level <- level[1];
    }
    # Fix just in case a silly user used 95 etc instead of 0.95
    if(level>1){
        level[] <- level / 100;
    }

    # The function checks the provided parameters of adam and/or oes
    ##### data #####
    # If this is simulated, extract the actuals
    if(is.adam.sim(y) || is.smooth.sim(y)){
        y <- y$data;
    }
    # If this is Mdata, use all the available stuff
    else if(inherits(y,"Mdata")){
        h <- y$h;
        holdout <- TRUE;
        lags <- frequency(y$x);
        y <- ts(c(y$x,y$xx),start=start(y$x),frequency=frequency(y$x));
    }

    # If this is a vector, use length
    # yInSample is needed for checks only
    if(is.null(dim(y))){
        obsInSample <- length(y) - holdout*h;
        yInSample <- y[1:obsInSample];
    }
    else{
        obsInSample <- nrow(y) - holdout*h;
        if(!is.null(formula)){
            yInSample <- y[1:obsInSample,formula[[2]]];
        }
        else{
            yInSample <- y[1:obsInSample,1];
        }
    }

    # If this is non-positive data and positive defined distributions are used, fix this
    if(any(yInSample<=0) && any(c("dlnorm","dllaplace","dls","dinvgauss") %in% distribution) &&
       (!is.occurrence(occurrence) && occurrence[1]=="none")){
        distributionToDrop <- c("dlnorm","dllaplace","dls","dinvgauss")[c("dlnorm","dllaplace","dls","dinvgauss") %in% distribution];
        warning(paste0("The data is not strictly positive, so not all the distributions make sense. ",
                       "Dropping ",paste0(distributionToDrop,collapse=", "),"."),
                call.=FALSE);
        distribution <- distribution[!(distribution %in% distributionToDrop)];
    }
    nModels <- length(distribution);

    #### Create logical, determining, what we are dealing with ####
    # ETS
    etsModel <- all(model!="NNN");
    # These values are needed for number of degrees of freedom check
    Etype <- substr(model,1,1);
    Ttype <- substr(model,2,2);
    Stype <- substr(model,nchar(model),nchar(model));
    damped <- (nchar(model)==4);
    if(length(Etype)>1){
        Etype <- "Z";
    }
    if(length(Ttype)>1){
        Ttype <- "Z";
        damped <- TRUE;
    }
    if(length(Stype)>1){
        Stype <- "Z";
    }

    # ARIMA + ARIMA select
    if(is.list(orders)){
        arimaModel <- any(c(orders$ar,orders$i,orders$ma)>0);
    }
    else{
        arimaModel <- any(orders>0);
    }

    # xreg - either as a separate variable or as a matrix for y
    xregModel <- !is.null(xreg) || (!is.null(dim(y)) && ncol(y>1));
    xregNumber <- 0;
    if(xregModel){
        if(!is.null(xreg)){
            xregNumber[] <- ncol(xreg);
        }
        else if(!is.null(dim(y)) && ncol(y>1)){
            xregNumber[] <- ncol(y)-1;
        }
    }
    xregDo <- match.arg(xregDo);

    #### Checks of provided parameters for ARIMA selection ####
    if(arimaModel && is.list(orders)){
        arimaModelSelect <- orders$select;
        arMax <- orders$ar;
        iMax <- orders$i;
        maMax <- orders$ma;

        if(is.null(arimaModelSelect)){
            arimaModelSelect <- FALSE;
        }

        if(any(c(arMax,iMax,maMax)<0)){
            stop("Funny guy! How am I gonna construct a model with negative order?",call.=FALSE);
        }

        # If there are no lags for the basic components, correct this.
        if(sum(lags==1)==0){
            lags <- c(1,lags);
        }

        # If there are zero lags, drop them
        if(any(lags==0)){
            arMax <- arMax[lags!=0];
            iMax <- iMax[lags!=0];
            maMax <- maMax[lags!=0];
            lags <- lags[lags!=0];
        }

        # Define maxorder and make all the values look similar (for the polynomials)
        maxorder <- max(length(arMax),length(iMax),length(maMax),length(lags));
        if(length(arMax)!=maxorder){
            arMax <- c(arMax,rep(0,maxorder-length(arMax)));
        }
        if(length(iMax)!=maxorder){
            iMax <- c(iMax,rep(0,maxorder-length(iMax)));
        }
        if(length(maMax)!=maxorder){
            maMax <- c(maMax,rep(0,maxorder-length(maMax)));
        }

        # If zeroes are defined as orders for some lags, drop them.
        if(any((arMax + iMax + maMax)==0)){
            orders2leave <- (arMax + iMax + maMax)!=0;
            if(all(!orders2leave)){
                orders2leave <- lags==min(lags);
            }
            arMax <- arMax[orders2leave];
            iMax <- iMax[orders2leave];
            maMax <- maMax[orders2leave];
            lags <- lags[orders2leave];
        }

        # Get rid of duplicates in lags
        if(length(unique(lags))!=length(lags)){
            lagsNew <- unique(lags);
            arMaxNew <- iMaxNew <- maMaxNew <- lagsNew;
            for(i in 1:length(lagsNew)){
                arMaxNew[i] <- max(arMax[which(lags==lagsNew[i])],na.rm=TRUE);
                iMaxNew[i] <- max(iMax[which(lags==lagsNew[i])],na.rm=TRUE);
                maMaxNew[i] <- max(maMax[which(lags==lagsNew[i])],na.rm=TRUE);
            }
            arMax <- arMaxNew;
            iMax <- iMaxNew;
            maMax <- maMaxNew;
            lags <- lagsNew;
        }

        # Order things, so we would deal with the lowest level of seasonality first
        arMax <- arMax[order(lags,decreasing=FALSE)];
        iMax <- iMax[order(lags,decreasing=FALSE)];
        maMax <- maMax[order(lags,decreasing=FALSE)];
        lags <- sort(lags,decreasing=FALSE);
        initialArimaNumber <- max((arMax + iMax) %*% lags, maMax %*% lags);
    }
    else{
        arMax <- iMax <- maMax <- NULL;
        arimaModelSelect <- FALSE;
        initialArimaNumber <- 0;
    }

    #### Maximum number of parameters to estimate ####
    nParamMax <- (1 +
                      # ETS model
                      etsModel*((Etype!="N") + (Ttype!="N") + (Stype!="N")*length(lags) + damped +
                                    (initial=="optimal") * ((Etype!="N") + (Ttype!="N") + (Stype!="N")*sum(lags))) +
                      # ARIMA components: initials + parameters
                      arimaModel*((initial=="optimal")*initialArimaNumber + sum(arMax) + sum(maMax)) +
                      # Xreg initials and smoothing parameters
                      xregModel*(xregNumber*(1+(xregDo=="adapt"))));

    # Do something in order to make sure that the stuff works
    if((nParamMax > obsInSample) && arimaModelSelect){
        # If this is ARIMA, remove some orders
        if(arimaModel){
            nParamMaxNonARIMA <- nParamMax - ((initial=="optimal")*initialArimaNumber + sum(arMax) + sum(maMax));
            if(obsInSample > nParamMaxNonARIMA){
                # Drop out some ARIMA orders, start with seasonal
                # Reduce maximum order of AR
                while(nParamMax > obsInSample){
                    arimaTail <- max(tail(which(arMax!=0),1),tail(which(iMax!=0),1),tail(which(maMax!=0),1));
                    if(arMax[arimaTail]>0){
                        arMax[arimaTail] <- arMax[arimaTail]-1;
                        initialArimaNumber[] <- max((arMax + iMax) %*% lags, maMax %*% lags);
                        nParamMax[] <- (1 +
                                            # ETS model
                                            etsModel*((Etype!="N") + (Ttype!="N") + (Stype!="N")*length(lags) + damped +
                                                          (initial=="optimal") * ((Etype!="N") + (Ttype!="N") + (Stype!="N")*sum(lags))) +
                                            # ARIMA components: initials + parameters
                                            arimaModel*((initial=="optimal")*initialArimaNumber + sum(arMax) + sum(maMax)) +
                                            # Xreg initials and smoothing parameters
                                            xregModel*(xregNumber*(1+(xregDo=="adapt"))));
                    }

                    # Reduce maximum order of I
                    if(nParamMax > obsInSample && iMax[arimaTail]>0){
                        iMax[arimaTail] <- iMax[arimaTail]-1;
                        initialArimaNumber[] <- max((arMax + iMax) %*% lags, maMax %*% lags);
                        nParamMax[] <- (1 +
                                            # ETS model
                                            etsModel*((Etype!="N") + (Ttype!="N") + (Stype!="N")*length(lags) + damped +
                                                          (initial=="optimal") * ((Etype!="N") + (Ttype!="N") + (Stype!="N")*sum(lags))) +
                                            # ARIMA components: initials + parameters
                                            arimaModel*((initial=="optimal")*initialArimaNumber + sum(arMax) + sum(maMax)) +
                                            # Xreg initials and smoothing parameters
                                            xregModel*(xregNumber*(1+(xregDo=="adapt"))));
                    }

                    # Reduce maximum order of MA
                    if(nParamMax > obsInSample && maMax[arimaTail]>0){
                        maMax[arimaTail] <- maMax[arimaTail]-1;
                        initialArimaNumber[] <- max((arMax + iMax) %*% lags, maMax %*% lags);
                        nParamMax[] <- (1 +
                                            # ETS model
                                            etsModel*((Etype!="N") + (Ttype!="N") + (Stype!="N")*length(lags) + damped +
                                                          (initial=="optimal") * ((Etype!="N") + (Ttype!="N") + (Stype!="N")*sum(lags))) +
                                            # ARIMA components: initials + parameters
                                            arimaModel*((initial=="optimal")*initialArimaNumber + sum(arMax) + sum(maMax)) +
                                            # Xreg initials and smoothing parameters
                                            xregModel*(xregNumber*(1+(xregDo=="adapt"))));
                    }
                }
            }
        }
    }

    #### Parallel calculations ####
    # Check the parallel parameter and set the number of cores
    if(is.numeric(parallel)){
        nCores <- parallel;
        parallel <- TRUE
    }
    else{
        nCores <- min(parallel::detectCores() - 1, nModels);
    }

    # If this is parallel, then load the required packages
    if(parallel){
        if(!requireNamespace("foreach", quietly = TRUE)){
            stop("In order to run the function in parallel, 'foreach' package must be installed.", call. = FALSE);
        }
        if(!requireNamespace("parallel", quietly = TRUE)){
            stop("In order to run the function in parallel, 'parallel' package must be installed.", call. = FALSE);
        }

        # Check the system and choose the package to use
        if(Sys.info()['sysname']=="Windows"){
            if(requireNamespace("doParallel", quietly = TRUE)){
                cat(paste0("Setting up ", nCores, " clusters using 'doParallel'..."));
                cat("\n");
                cluster <- parallel::makeCluster(nCores);
                doParallel::registerDoParallel(cluster);
            }
            else{
                stop("Sorry, but in order to run the function in parallel, you need 'doParallel' package.",
                     call. = FALSE);
            }
        }
        else{
            if(requireNamespace("doMC", quietly = TRUE)){
                doMC::registerDoMC(nCores);
                cluster <- NULL;
            }
            else if(requireNamespace("doParallel", quietly = TRUE)){
                cat(paste0("Setting up ", nCores, " clusters using 'doParallel'..."));
                cat("\n");
                cluster <- parallel::makeCluster(nCores);
                doParallel::registerDoParallel(cluster);
            }
            else{
                stop(paste0("Sorry, but in order to run the function in parallel, you need either ",
                            "'doMC' (prefered) or 'doParallel' package."),
                     call. = FALSE);
            }
        }
    }

    if(!silent){
        if(!parallel){
            cat("Evaluating models with different distributions... ");
        }
        else{
            cat(paste0("Working... "));
        }
    }

    #### The function that does the loop and returns a list of ETS(X) models ####
    adamReturner <- function(y, model, lags, orders,
                             distribution, h, holdout,
                             persistence, phi, initial, arma,
                             occurrence, ic, bounds,
                             xreg, xregDo, parallel,
                             arimaModelSelect, arMax, iMax, maMax, ...){
        # If we select ARIMA, don't do it in the first step
        if(arimaModelSelect){
            ordersToUse <- c(0,0,0);
        }
        else{
            ordersToUse <- orders;
        }

        if(!parallel){
            # Prepare the list of models
            selectedModels <- vector("list",length(distribution));
            for(i in 1:length(distribution)){
                if(!silent){
                    cat(paste0(distribution[i],", "));
                }
                selectedModels[[i]] <- adam(y=y, model=model, lags=lags, orders=ordersToUse,
                                            distribution=distribution[i],
                                            h=h, holdout=holdout,
                                            persistence=persistence, phi=phi, initial=initial, arma=arma,
                                            occurrence=occurrence, ic=ic, bounds=bounds,
                                            xreg=xreg, xregDo=xregDo, silent=TRUE, ...);

                if(arimaModelSelect){
                    selectedModels[[i]] <- arimaSelector(y=y, model=model,
                                                         lags=lags, arMax=arMax, iMax=iMax, maMax=maMax,
                                                         distribution=selectedModels[[i]]$distribution, h=h, holdout=holdout,
                                                         persistence=persistence, phi=phi, initial=initial,
                                                         occurrence=occurrence, ic=ic, bounds=bounds, fast=fast,
                                                         silent=silent, xreg=xreg, xregDo=xregDo,
                                                         testModelETS=selectedModels[[i]], ...)
                }
            }
        }
        else{
            selectedModels <- foreach::`%dopar%`(foreach::foreach(i=1:length(distribution)),{
                testModel <- adam(y=y, model=model, lags=lags, orders=ordersToUse,
                                  distribution=distribution[i],
                                  h=h, holdout=holdout,
                                  persistence=persistence, phi=phi, initial=initial, arma=arma,
                                  occurrence=occurrence, ic=ic, bounds=bounds,
                                  xreg=xreg, xregDo=xregDo, silent=TRUE, ...)

                if(arimaModelSelect){
                    testModel <- arimaSelector(y=y, model=model,
                                               lags=lags, arMax=arMax, iMax=iMax, maMax=maMax,
                                               distribution=testModel$distribution, h=h, holdout=holdout,
                                               persistence=persistence, phi=phi, initial=initial,
                                               occurrence=occurrence, ic=ic,
                                               bounds=bounds, fast=fast,
                                               silent=TRUE, xreg=xreg, xregDo=xregDo,
                                               testModelETS=testModel, ...)
                }
                return(testModel);
            })
        }
        return(selectedModels);
    }

    #### ARIMA selection script ####
    if(arimaModelSelect){
        if(!is.null(arma)){
            warning("ARIMA order selection cannot be done with the provided arma parameters. Dropping them.",
                    call.=FALSE);
            arma <- NULL;
        }

        #### Function corrects IC taking number of parameters on previous step ####
        icCorrector <- function(llikelihood, ic, nParam, obsNonzero){
            if(ic=="AIC"){
                correction <- 2*nParam - 2*llikelihood;
            }
            else if(ic=="AICc"){
                if(nParam>=obsNonzero-1){
                    correction <- Inf;
                }
                else{
                    correction <- 2*nParam*obsNonzero/(obsNonzero-nParam-1) - 2*llikelihood;
                }
            }
            else if(ic=="BIC"){
                correction <- nParam*log(obsNonzero) - 2*llikelihood;
            }
            else if(ic=="BICc"){
                if(nParam>=obsNonzero-1){
                    correction <- Inf;
                }
                else{
                    correction <- (nParam*log(obsNonzero)*obsNonzero)/(obsNonzero-nParam-1) - 2*llikelihood;
                }
            }

            return(correction);
        }

        #### The function that selects ARIMA orders for the provided data ####
        arimaSelector <- function(y, model, lags, arMax, iMax, maMax,
                                  distribution, h, holdout,
                                  persistence, phi, initial,
                                  occurrence, ic, bounds, fast,
                                  silent, xreg, xregDo, testModelETS, ...){
            silentDebug <- FALSE;

            # Save the original values
            modelOriginal <- model;
            xregOriginal <- xreg;
            occurrenceOriginal <- occurrence;
            persistenceOriginal <- persistence;
            phiOriginal <- phi;

            # If the ETS model was done before this, then extract residuals
            if(is.adam(testModelETS)){
                dataAR <- dataI <- dataMA <- yInSample <- residuals(testModelETS);
                model <- "NNN";
                xreg <- NULL;
                occurrence <- "none"
                persistence <- NULL;
                phi <- NULL;

                # Don't count the scale term
                nParamOriginal <- nparam(testModelETS)-1;
            }
            else{
                # Fit just mean
                testModelETS <- adam(y, model="NNN",lags=1, distribution=distribution,
                                     h=h,holdout=holdout, occurrence=occurrence, bounds=bounds, silent=TRUE);
                dataAR <- dataI <- dataMA <- yInSample <- actuals(testModelETS);

                # Originally, we only have a constant
                nParamOriginal <- 1;
            }
            testModel <- testModelETS;
            bestIC <- bestICI <- ICFunction(testModel);
            obsNonzero <- nobs(testModelETS,all=FALSE);

            if(silentDebug){
                cat("Best IC: "); cat(bestIC); cat("\n");
            }
            if(!silent){
                cat(" Selecting ARIMA orders...    ");
            }

            # 1 stands for constant/no constant, another one stands for ARIMA(0,0,0)
            if(all(maMax==0)){
                nModelsARIMA <- prod(iMax + 1) * (1 + sum(arMax));
            }
            else{
                nModelsARIMA <- prod(iMax + 1) * (1 + sum(maMax*(1 + sum(arMax))));
            }
            ICValue <- 1E+100;
            m <- 0;

            lagsTest <- maTest <- arTest <- rep(0,length(lags));
            arBest <- maBest <- iBest <- rep(0,length(lags));
            arBestLocal <- maBestLocal <- arBest;

            iOrders <- matrix(0,prod(iMax+1),ncol=length(iMax));

            ##### Loop for differences #####
            # Prepare table with differences
            if(any(iMax!=0)){
                iOrders[,1] <- rep(c(0:iMax[1]),times=prod(iMax[-1]+1));
                if(length(iMax)>1){
                    for(seasLag in 2:length(iMax)){
                        iOrders[,seasLag] <- rep(c(0:iMax[seasLag]),each=prod(iMax[1:(seasLag-1)]+1))
                    }
                }
            }
            # Start the loop for differences
            for(d in 1:nrow(iOrders)){
                m <- m + 1;
                if(!silent){
                    cat(paste0(rep("\b",nchar(round(m/nModelsARIMA,2)*100)+1),collapse=""));
                    cat(paste0(round((m)/nModelsARIMA,2)*100,"%"));
                }
                nParamInitial <- 0;
                # If differences are zero, skip this step
                if(!all(iOrders[d,]==0)){
                    # Run the model for differences
                    testModel <- adam(y=yInSample, model=model, lags=lags,
                                      orders=list(ar=0,i=iOrders[d,],ma=0),
                                      distribution=distribution,
                                      h=h, holdout=FALSE,
                                      persistence=persistence, phi=phi, initial=initial,
                                      occurrence=occurrence, ic=ic, bounds=bounds,
                                      xreg=xreg, xregDo=xregDo, silent=TRUE, ...);
                    nParamInitial[] <- (initial=="optimal") * (iOrders[d,] %*% lags);
                }
                # Extract Information criteria
                ICValue <- ICFunction(testModel);
                if(silentDebug){
                    cat("I: "); cat(iOrders[d,]); cat(", "); cat(ICValue); cat("\n");
                }
                if(ICValue < bestICI){
                    bestICI <- ICValue;
                    dataMA <- dataI <- residuals(testModel);
                    if(ICValue < bestIC){
                        iBest <- iOrders[d,];
                        bestIC <- ICValue;
                        maBest <- arBest <- rep(0,length(arTest));
                    }
                }
                else{
                    if(fast){
                        m <- m + sum(maMax*(1 + sum(arMax)));
                        next;
                    }
                    else{
                        dataMA <- dataI <- residuals(testModel);
                    }
                }

                ##### Loop for MA #####
                if(any(maMax!=0)){
                    bestICMA <- bestICI;
                    maBestLocal <- maTest <- rep(0,length(maTest));
                    for(seasSelectMA in 1:length(lags)){
                        if(maMax[seasSelectMA]!=0){
                            for(maSelect in 1:maMax[seasSelectMA]){
                                m <- m + 1;
                                if(!silent){
                                    cat(paste0(rep("\b",nchar(round(m/nModelsARIMA,2)*100)+1),collapse=""));
                                    cat(paste0(round((m)/nModelsARIMA,2)*100,"%"));
                                }
                                maTest[seasSelectMA] <- maMax[seasSelectMA] - maSelect + 1;

                                # Run the model for MA
                                testModel <- adam(y=dataI, model="NNN", lags=lags,
                                                  orders=list(ar=0,i=0,ma=maTest),
                                                  distribution=distribution,
                                                  h=h, holdout=FALSE,
                                                  persistence=NULL, phi=NULL, initial=initial,
                                                  occurrence="none", ic=ic, bounds=bounds,
                                                  xreg=NULL, xregDo="use", silent=TRUE, ...);
                                if(initial=="optimal" && (maTest %*% lags > nParamInitial)){
                                    nParamInitial[] <-  (maTest %*% lags);
                                }
                                # Exclude the initials from the number of parameters
                                nParamMA <- sum(maTest);
                                ICValue <- icCorrector(logLik(testModel), ic,
                                                       nParamOriginal + nParamMA + nParamInitial,
                                                       obsNonzero);
                                if(silentDebug){
                                    cat("MA: "); cat(maTest); cat(", "); cat(ICValue); cat("\n");
                                }
                                if(ICValue < bestICMA){
                                    bestICMA <- ICValue;
                                    maBestLocal <- maTest;
                                    if(ICValue < bestIC){
                                        bestIC <- bestICMA;
                                        iBest <- iOrders[d,];
                                        maBest <- maTest;
                                        arBest <- rep(0,length(arTest));
                                    }
                                    dataMA <- residuals(testModel);
                                }
                                else{
                                    if(fast){
                                        m <- m + maTest[seasSelectMA] * (1 + sum(arMax)) - 1;
                                        maTest <- maBestLocal;
                                        break;
                                    }
                                    else{
                                        maTest <- maBestLocal;
                                        dataMA <- residuals(testModel);
                                    }
                                }

                                ##### Loop for AR #####
                                if(any(arMax!=0)){
                                    bestICAR <- bestICMA;
                                    arBestLocal <- arTest <- rep(0,length(arTest));
                                    for(seasSelectAR in 1:length(lags)){
                                        lagsTest[seasSelectAR] <- lags[seasSelectAR];
                                        if(arMax[seasSelectAR]!=0){
                                            for(arSelect in 1:arMax[seasSelectAR]){
                                                m <- m + 1;
                                                if(!silent){
                                                    cat(paste0(rep("\b",nchar(round(m/nModelsARIMA,2)*100)+1),collapse=""));
                                                    cat(paste0(round((m)/nModelsARIMA,2)*100,"%"));
                                                }
                                                arTest[seasSelectAR] <- arMax[seasSelectAR] - arSelect + 1;

                                                # Run the model for AR
                                                testModel <- adam(y=dataMA, model="NNN", lags=lags,
                                                                  orders=list(ar=arTest,i=0,ma=0),
                                                                  distribution=distribution,
                                                                  h=h, holdout=FALSE,
                                                                  persistence=NULL, phi=NULL, initial=initial,
                                                                  occurrence="none", ic=ic, bounds=bounds,
                                                                  xreg=NULL, xregDo="use", silent=TRUE, ...);
                                                if(initial=="optimal" && (arTest %*% lags > nParamInitial)){
                                                    nParamInitial[] <-  (arTest %*% lags);
                                                }
                                                # Exclude the initials (in order not to duplicate them)
                                                nParamAR <- sum(arTest);
                                                ICValue <- icCorrector(logLik(testModel), ic,
                                                                       nParamOriginal + nParamMA + nParamAR + nParamInitial,
                                                                       obsNonzero);
                                                if(silentDebug){
                                                    cat("AR: "); cat(arTest); cat(", "); cat(ICValue); cat("\n");
                                                }
                                                if(ICValue < bestICAR){
                                                    bestICAR <- ICValue;
                                                    arBestLocal <- arTest;
                                                    if(ICValue < bestIC){
                                                        bestIC <- ICValue;
                                                        iBest <- iOrders[d,];
                                                        arBest <- arTest;
                                                        maBest <- maTest;
                                                    }
                                                }
                                                else{
                                                    if(fast){
                                                        m <- m + arTest[seasSelectAR] - 1;
                                                        arTest <- arBestLocal;
                                                        break;
                                                    }
                                                    else{
                                                        arTest <- arBestLocal;
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
                else{
                    ##### Loop for AR #####
                    if(any(arMax!=0)){
                        bestICAR <- bestICMA;
                        arBestLocal <- arTest <- rep(0,length(arTest));
                        for(seasSelectAR in 1:length(lags)){
                            lagsTest[seasSelectAR] <- lags[seasSelectAR];
                            if(arMax[seasSelectAR]!=0){
                                for(arSelect in 1:arMax[seasSelectAR]){
                                    m <- m + 1;
                                    if(!silent){
                                        cat(paste0(rep("\b",nchar(round(m/nModelsARIMA,2)*100)+1),collapse=""));
                                        cat(paste0(round((m)/nModelsARIMA,2)*100,"%"));
                                    }
                                    arTest[seasSelectAR] <- arMax[seasSelectAR] - arSelect + 1;

                                    # Run the model for MA
                                    testModel <- adam(y=dataI, model="NNN", lags=lags,
                                                      orders=list(ar=arTest,i=0,ma=0),
                                                      distribution=distribution,
                                                      h=h, holdout=FALSE,
                                                      persistence=NULL, phi=NULL, initial=initial,
                                                      occurrence="none", ic=ic, bounds=bounds,
                                                      xreg=NULL, xregDo="use", silent=TRUE, ...);
                                    if(initial=="optimal" && (arTest %*% lags > nParamInitial)){
                                        nParamInitial[] <-  (arTest %*% lags);
                                    }
                                    # Exclude the initials (in order not to duplicate them)
                                    nParamAR <- sum(arTest);
                                    ICValue <- icCorrector(logLik(testModel), ic,
                                                           nParamOriginal + nParamAR + nParamInitial,
                                                           obsNonzero);
                                    if(silentDebug){
                                        cat("AR: "); cat(arTest); cat(", "); cat(ICValue); cat("\n");
                                    }
                                    if(ICValue < bestICAR){
                                        bestICAR <- ICValue;
                                        arBestLocal <- arTest;
                                        if(ICValue < bestIC){
                                            bestIC <- ICValue;
                                            iBest <- iOrders[d,];
                                            arBest <- arTest;
                                            maBest <- maTest;
                                        }
                                    }
                                    else{
                                        if(fast){
                                            m <- m + arTest[seasSelectAR] - 1;
                                            arTest <- arBestLocal;
                                            break;
                                        }
                                        else{
                                            arTest <- arBestLocal;
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }

            if(!silent && fast){
                cat(paste0(rep("\b",nchar(round(m/nModels,2)*100)+1),collapse=""));
                cat(paste0(" ",100,"%"));
            }

            #### Reestimate the best model in order to get rid of bias ####
            # Run the model for MA
            bestModel <- adam(y=y, model=modelOriginal, lags=lags,
                              orders=list(ar=(arBest),i=(iBest),ma=(maBest)),
                              distribution=distribution,
                              h=h, holdout=holdout,
                              persistence=persistenceOriginal, phi=phiOriginal, initial=initial,
                              occurrence=occurrenceOriginal, ic=ic, bounds=bounds,
                              xreg=xregOriginal, xregDo="use", silent=TRUE, ...);

            if(!silent){
                cat(". The best ARIMA is selected.\n");
            }
            return(bestModel);
        }
    }

    #### The function that extracts outliers and refits the model with the selection ####
    # Outliers types to include: additive outlier (AO) and level shift (LS).
    # Introduce leads and lags of outliers in case of "select"
    outlierDetector <- function(adamModel, outliers="use"){
        if(outliers=="ignore"){
            return(adamModel);
        }
        else{
            outliersModel <- outlierdummy(adamModel, level=level);
            if(length(outliersModel$id)>0){
                if(!silent){
                    cat("Dealing with outliers: ");
                }
                # Create a proper xreg matrix
                if(h>0){
                    outliersXreg <- rbind(outliersModel$outliers, matrix(0,nrow=h,length(outliersModel$id)));
                }
                else{
                    outliersXreg <- outliersModel$outliers;
                }
                # If select outliers, then introduce lags and leads
                if(outliers=="select"){
                    outliersXreg <- xregExpander(outliersXreg,c(-1:1),gaps="zero");
                }
                outliersDo <- outliers;
                # Form new xreg matrix (check y and xreg)
                if(xregModel){
                    if(!is.null(xreg)){
                        xregNames <- c(colnames(xreg),colnames(outliersXreg));
                        if(nrow(xreg)!=nrow(outliersXreg)){
                            obsXreg <- min(nrow(xreg),nrow(outliersXreg));
                            xregNew <- cbind(xreg[1:obsXreg,,drop=FALSE],
                                             outliersXreg[1:obsXreg,,drop=FALSE]);
                        }
                        else{
                            xregNew <- cbind(xreg,outliersXreg);
                        }
                        colnames(xregNew) <- xregNames;
                    }
                    # If y is a matrix, add columns
                    else if(!is.null(dim(y)) && ncol(y>1)){
                        y <- cbind(y,outliersXreg);
                        xregNew <- NULL;
                    }
                    # Update formula if it is provided
                    if(!is.null(formula)){
                        formula <- update(as.formula(formula),
                                          as.formula(paste0("~.+",paste0(colnames(outliersXreg),collapse="+"))));
                    }
                    outliersDo <- xregDo;
                    adamModel <- suppressWarnings(auto.adam(y, model, lags=lags, orders=orders,
                                                            distribution=distribution, h=h, holdout=holdout,
                                                            persistence=persistence, phi=phi, initial=initial, arma=arma,
                                                            occurrence=occurrence, ic=ic, bounds=bounds,
                                                            xreg=xregNew, xregDo=outliersDo,
                                                            silent=silent, parallel=parallel, fast=fast));
                }
                else{
                    adamModel <- suppressWarnings(auto.adam(y, model, lags=lags, orders=orders,
                                                            distribution=distribution, h=h, holdout=holdout,
                                                            persistence=persistence, phi=phi, initial=initial, arma=arma,
                                                            occurrence=occurrence, ic=ic, bounds=bounds,
                                                            xreg=outliersXreg, xregDo=outliersDo,
                                                            silent=silent, parallel=parallel, fast=fast));
                }
            }
            else{
                if(!silent){
                    cat("No outliers detected.\n");
                }
            }
            return(adamModel);
        }
    }

    #### A simple loop, no ARIMA orders selection ####
    if(!arimaModelSelect){
        selectedModels <- adamReturner(y, model, lags, orders,
                                       distribution, h, holdout,
                                       persistence, phi, initial, arma,
                                       occurrence, ic, bounds,
                                       xreg, xregDo, parallel,
                                       arimaModelSelect, arMax, iMax, maMax, ...);
    }
    else{
        #### If there is ETS(X), do ARIMA selection on residuals ####
        # Extract residuals from adams for each distribution, fit best ARIMA for each, refit the models.
        if(etsModel || xregModel){
            selectedModels <- adamReturner(y, model, lags, orders,
                                           distribution, h, holdout,
                                           persistence, phi, initial, arma,
                                           occurrence, ic, bounds,
                                           xreg, xregDo, parallel,
                                           arimaModelSelect, arMax, iMax, maMax, ...);
        }
        #### Otherwise, do the stuff directly ####
        # Do ARIMA selection for each distribution in parallel.
        else{
            if(!parallel){
                # Prepare the list of models
                selectedModels <- vector("list",length(distribution));
                for(i in 1:length(distribution)){
                    if(!silent){
                        cat(paste0(distribution[i],": "));
                    }
                    selectedModels[[i]] <- arimaSelector(y=y, model=model,
                                                         lags=lags, arMax=arMax, iMax=iMax, maMax=maMax,
                                                         distribution=distribution[i], h=h, holdout=holdout,
                                                         persistence=persistence, phi=phi, initial=initial,
                                                         occurrence=occurrence, ic=ic, bounds=bounds, fast=fast,
                                                         silent=silent, xreg=xreg, xregDo=xregDo, testModelETS=NULL, ...);
                }
            }
            else{
                selectedModels <- foreach::`%dopar%`(foreach::foreach(i=1:length(distribution)),{
                    testModel <- arimaSelector(y=y, model=model,
                                               lags=lags, arMax=arMax, iMax=iMax, maMax=maMax,
                                               distribution=distribution[i], h=h, holdout=holdout,
                                               persistence=persistence, phi=phi, initial=initial,
                                               occurrence=occurrence, ic=ic, bounds=bounds, fast=fast,
                                               silent=TRUE, xreg=xreg, xregDo=xregDo, testModelETS=NULL, ...);
                    return(testModel);
                })
            }
        }
    }

    if(modelDo=="select"){
        ICValues <- sapply(selectedModels, ICFunction);
    }
    else{
        ICValues <- vector("numeric",length(distribution));
        for(i in 1:length(distribution)){
            ICValues[i] <- selectedModels[[i]]$ICs[is.finite(selectedModels[[i]]$ICs)] %*%
                selectedModels[[i]]$ICw[is.finite(selectedModels[[i]]$ICs)];
        }
    }

    selectedModels[[which.min(ICValues)]]$timeElapsed <- Sys.time()-startTime;
    selectedModels[[which.min(ICValues)]]$formula <- as.formula(do.call("substitute",
                                                                        list(expr=selectedModels[[which.min(ICValues)]]$formula,
                                                                             env=list(y=as.name(responseName)))));
    # names(ICValues) <- sapply(selectedModels, modelType);
    # selectedModels[[which.min(ICValues)]]$ICValues <- ICValues;

    if(outliers!="ignore"){
        selectedModels[[which.min(ICValues)]] <- outlierDetector(selectedModels[[which.min(ICValues)]], outliers=outliers);
    }

    if(!silent){
        if(outliers=="ignore"){
            cat("Done!\n");
        }
        plot(selectedModels[[which.min(ICValues)]],7);
    }

    return(selectedModels[[which.min(ICValues)]]);
}
config-i1/mes documentation built on July 31, 2020, 8:16 p.m.