Nothing
utils::globalVariables(c("adamFitted","algorithm","arEstimate","arOrders","arRequired","arimaModel",
"arimaPolynomials","armaParameters","componentsNamesARIMA","componentsNamesETS",
"componentsNumberARIMA","componentsNumberETS","componentsNumberETSNonSeasonal",
"componentsNumberETSSeasonal","digits","etsModel","ftol_abs","ftol_rel",
"horizon","iOrders","iRequired","initialArima","initialArimaEstimate",
"initialArimaNumber","initialLevel","initialLevelEstimate","initialSeasonal",
"initialSeasonalEstimate","initialTrend","initialTrendEstimate","lagsModelARIMA",
"lagsModelAll","lagsModelSeasonal","indexLookupTable","profilesRecentTable",
"other","otherParameterEstimate","lambda","lossFunction",
"maEstimate","maOrders","maRequired","matVt","matWt","maxtime","modelIsTrendy",
"nParamEstimated","persistenceLevel","persistenceLevelEstimate",
"persistenceSeasonal","persistenceSeasonalEstimate","persistenceTrend",
"persistenceTrendEstimate","vecG","xtol_abs","xtol_rel","stepSize","yClasses",
"yForecastIndex","yInSampleIndex","yIndexAll","yNAValues","yStart","responseName",
"xregParametersMissing","xregParametersIncluded","xregParametersEstimated",
"xregParametersPersistence","xregModelInitials","constantName","yDenominator",
"damped","dataStart","initialEstimate","initialSeasonEstimate","maxeval","icFunction",
"modelIsMultiplicative","modelIsSeasonal","nComponentsAll","nComponentsNonSeasonal",
"nIterations"));
#' ADAM is Augmented Dynamic Adaptive Model
#'
#' Function constructs an advanced Single Source of Error model, based on ETS
#' taxonomy and ARIMA elements
#'
#' Function estimates ADAM in a form of the Single Source of Error state space
#' model of the following type:
#'
#' \deqn{y_{t} = o_t (w(v_{t-l}) + h(x_t, a_{t-1}) + r(v_{t-l}) \epsilon_{t})}
#'
#' \deqn{v_{t} = f(v_{t-l}, a_{t-1}) + g(v_{t-l}, a_{t-1}, x_{t}) \epsilon_{t}}
#'
#' Where \eqn{o_{t}} is the Bernoulli distributed random variable (in case of
#' normal data it equals to 1 for all observations), \eqn{v_{t}} is the state
#' vector and \eqn{l} is the vector of lags, \eqn{x_t} is the vector of
#' exogenous variables. w(.) is the measurement function, r(.) is the error
#' function, f(.) is the transition function, g(.) is the persistence
#' function and \eqn{a_t} is the vector of parameters for exogenous variables.
#' Finally, \eqn{\epsilon_{t}} is the error term.
#'
#' The implemented model allows introducing several seasonal states and supports
#' intermittent data via the \code{occurrence} variable.
#'
#' The error term \eqn{\epsilon_t} can follow different distributions, which
#' are regulated via the \code{distribution} parameter. This includes:
#' \enumerate{
#' \item \code{default} - Normal distribution is used for the Additive error models,
#' Gamma is used for the Multiplicative error models.
#' \item dnorm - \link[stats]{Normal} distribution,
#' \item \link[greybox]{dlaplace} - Laplace distribution,
#' \item \link[greybox]{ds} - S distribution,
#' \item \link[greybox]{dgnorm} - Generalised Normal distribution,
# \item \link[stats]{dlogis} - Logistic Distribution,
# \item \link[stats]{dt} - T distribution,
# \item \link[greybox]{dalaplace} - Asymmetric Laplace distribution,
#' \item \link[stats]{dlnorm} - Log-Normal distribution,
# \item dllaplace - Log-Laplace distribution,
# \item dls - Log-S distribution,
# \item dlgnorm - Log-Generalised Normal distribution,
# \item \link[greybox]{dbcnorm} - Box-Cox normal distribution,
#' \item \link[stats]{dgamma} - Gamma distribution,
#' \item \link[statmod]{dinvgauss} - Inverse Gaussian distribution,
#' }
#'
#' For some more information about the model and its implementation, see the
#' vignette: \code{vignette("adam","smooth")}. The more detailed explanation
#' of ADAM is provided by Svetunkov (2021).
#'
#' The function \code{auto.adam()} tries out models with the specified
#' distributions and returns the one with the most suitable one based on selected
#' information criterion.
#'
#' \link[greybox]{sm}.adam method estimates the scale model for the already
#' estimated adam. In order for ADAM to take the SM model into account, the
#' latter needs to be recorded in the former, amending the likelihood and the number
#' of degrees of freedom. This can be done using \link[greybox]{implant} method.
#'
#' @template ssAuthor
#' @template ssKeywords
#'
#' @template smoothRef
#' @template ssADAMRef
#' @template ssGeneralRef
#' @template ssIntermittentRef
#' @template ssETSRef
#' @template ssIntervalsRef
#'
#' @param data Vector, containing data needed to be forecasted. If a matrix (or
#' data.frame / data.table) is provided, then the first column is used as a
#' response variable, while the rest of the matrix is used as a set of explanatory
#' variables. \code{formula} can be used in the latter case in order to define what
#' relation to have.
#' @param model The type of ETS model. The first letter stands for the type of
#' the error term ("A" or "M"), the second (and sometimes the third as well) is for
#' the trend ("N", "A", "Ad", "M" or "Md"), and the last one is for the type of
#' seasonality ("N", "A" or "M"). In case of several lags, the seasonal components
#' are assumed to be the same. The model is then printed out as
#' ETS(M,Ad,M)[m1,m2,...], where m1, m2, ... are the lags specified by the
#' \code{lags} parameter.
#' There are several options for the \code{model} besides the conventional ones,
#' which rely on information criteria:
#' \enumerate{
#' \item \code{model="ZZZ"} means that the model will be selected based on the
#' chosen information criteria type. The Branch and Bound is used in the process.
#' \item \code{model="XXX"} means that only additive components are tested, using
#' Branch and Bound.
#' \item \code{model="YYY"} implies selecting between multiplicative components.
#' \item \code{model="CCC"} triggers the combination of forecasts of models using
#' information criteria weights (Kolassa, 2011).
#' \item combinations between these four and the classical components are also
#' accepted. For example, \code{model="CAY"} will combine models with additive
#' trend and either none or multiplicative seasonality.
#' \item \code{model="PPP"} will produce the selection between pure additive and
#' pure multiplicative models. "P" stands for "Pure". This cannot be mixed with
#' other types of components.
#' \item \code{model="FFF"} will select between all the 30 types of models. "F"
#' stands for "Full". This cannot be mixed with other types of components.
#' \item The parameter \code{model} can also be a vector of names of models for a
#' finer tuning (pool of models). For example, \code{model=c("ANN","AAA")} will
#' estimate only two models and select the best of them.
#' }
#'
#' Also, \code{model} can accept a previously estimated adam and use all
#' its parameters.
#'
#' Keep in mind that model selection with "Z" components uses Branch and Bound
#' algorithm and may skip some models that could have slightly smaller
#' information criteria. If you want to do a exhaustive search, you would need
#' to list all the models to check as a vector.
#'
#' The default value is set to \code{"ZXZ"}, because the multiplicative trend is explosive
#' and dangerous. It should be used only for each separate time series, not for the
#' automated predictions for big datasets.
#'
#' @param lags Defines lags for the corresponding components. All components
#' count, starting from level, so ETS(M,M,M) model for monthly data will have
#' \code{lags=c(1,1,12)}. However, the function will also accept \code{lags=c(12)},
#' assuming that the lags 1 were dropped. In case of ARIMA, lags specify what should be
#' the seasonal component lag. e.g. \code{lags=c(1,12)} will lead to the
#' seasonal ARIMA with m=12. This can accept several lags, supporting multiple seasonal ETS
#' and ARIMA models.
#' @param orders The order of ARIMA to be included in the model. This should be passed
#' either as a vector (in which case the non-seasonal ARIMA is assumed) or as a list of
#' a type \code{orders=list(ar=c(p,P),i=c(d,D),ma=c(q,Q))}, in which case the \code{lags}
#' variable is used in order to determine the seasonality m. See \link[smooth]{msarima}
#' for details.
#' In addition, \code{orders} accepts one more parameter: \code{orders=list(select=FALSE)}.
#' If \code{TRUE}, then the function will select the most appropriate order using a
#' mechanism similar to \code{auto.msarima()}, but implemented in \code{auto.adam()}.
#' The values \code{list(ar=...,i=...,ma=...)} specify the maximum orders to check in
#' this case.
#' @param formula Formula to use in case of explanatory variables. If \code{NULL},
#' then all the variables are used as is. Can also include \code{trend}, which would add
#' the global trend. Only needed if \code{data} is a matrix or if \code{trend} is provided.
#' @param constant Logical, determining, whether the constant is needed in the model or not.
#' This is mainly needed for ARIMA part of the model, but can be used for ETS as well. In
#' case of pure regression, this is completely ignored (use \code{formula} instead).
#' @param distribution what density function to assume for the error term. The full
#' name of the distribution should be provided, starting with the letter "d" -
#' "density". The names align with the names of distribution functions in R.
#' For example, see \link[stats]{dnorm}. For detailed explanation of available
#' distributions, see vignette in greybox package: \code{vignette("greybox","alm")}.
#' @param loss The type of Loss Function used in optimization. \code{loss} can
#' be:
#' \itemize{
#' \item \code{likelihood} - the model is estimated via the maximisation of the
#' likelihood of the function specified in \code{distribution};
#' \item \code{MSE} (Mean Squared Error),
#' \item \code{MAE} (Mean Absolute Error),
#' \item \code{HAM} (Half Absolute Moment),
#' \item \code{LASSO} - use LASSO to shrink the parameters of the model;
#' \item \code{RIDGE} - use RIDGE to shrink the parameters of the model;
#' \item \code{TMSE} - Trace Mean Squared Error,
#' \item \code{GTMSE} - Geometric Trace Mean Squared Error,
#' \item \code{MSEh} - optimisation using only h-steps ahead error,
#' \item \code{MSCE} - Mean Squared Cumulative Error.
#' }
#' In case of LASSO / RIDGE, the variables are not normalised prior to the estimation,
#' but the parameters are divided by the mean values of explanatory variables.
#'
#' Note that model selection and combination works properly only for the default
#' \code{loss="likelihood"}.
#'
#' Furthermore, just for fun the absolute and half analogues of multistep estimators
#' are available: \code{MAEh}, \code{TMAE}, \code{GTMAE}, \code{MACE},
#' \code{HAMh}, \code{THAM}, \code{GTHAM}, \code{CHAM}.
#'
#' Last but not least, user can provide their own function here as well, making sure
#' that it accepts parameters \code{actual}, \code{fitted} and \code{B}. Here is an
#' example:
#'
#' \code{lossFunction <- function(actual, fitted, B) return(mean(abs(actual-fitted)))}
#'
#' \code{loss=lossFunction}
#' @param h The forecast horizon. Mainly needed for the multistep loss functions.
#' @param holdout Logical. If \code{TRUE}, then the holdout of the size \code{h}
#' is taken from the data (can be used for the model testing purposes).
#' @param persistence Persistence vector \eqn{g}, containing smoothing
#' parameters. If \code{NULL}, then estimated. Can be also passed as a names list of
#' the type: \code{persistence=list(level=0.1, trend=0.05, seasonal=c(0.1,0.2),
#' xreg=c(0.1,0.2))}. Dropping some elements from the named list will make the function
#' estimate them. e.g. if you don't specify seasonal in the persistence for the ETS(M,N,M)
#' model, it will be estimated.
#' @param phi Value of damping parameter. If \code{NULL} then it is estimated.
#' Only applicable for damped-trend models.
#' @param initial Can be either character or a list, or a vector of initial states.
#' If it is character, then it can be \code{"optimal"}, meaning that all initial
#' states are optimised, or \code{"backcasting"}, meaning that the initials of
#' dynamic part of the model are produced using backcasting procedure (advised
#' for data with high frequency). In the latter case, the parameters of the
#' explanatory variables are optimised. This is recommended for ETSX and ARIMAX
#' models. Alternatively, you can set \code{initial="complete"} backcasting,
#' which means that all states (including explanatory variables) are initialised
#' via backcasting.
#'
#' If a use provides a list of values, it is recommended to use the named one and
#' to provide the initial components that are available. For example:
#' \code{initial=list(level=1000,trend=10,seasonal=list(c(1,2),c(1,2,3,4)),
#' arima=1,xreg=100)}. If some of the components are needed by the model, but are
#' not provided in the list, they will be estimated. If the vector is provided,
#' then it is expected that the components will be provided inthe same order as above,
#' one after another without any gaps.
#' @param arma Either the named list or a vector with AR / MA parameters ordered lag-wise.
#' The number of elements should correspond to the specified orders e.g.
#' \code{orders=list(ar=c(1,1),ma=c(1,1)), lags=c(1,4), arma=list(ar=c(0.9,0.8),ma=c(-0.3,0.3))}
#' @param occurrence The type of model used in probability estimation. Can be
#' \code{"none"} - none,
#' \code{"fixed"} - constant probability,
#' \code{"general"} - the general Beta model with two parameters,
#' \code{"odds-ratio"} - the Odds-ratio model with b=1 in Beta distribution,
#' \code{"inverse-odds-ratio"} - the model with a=1 in Beta distribution,
#' \code{"direct"} - the TSB-like (Teunter et al., 2011) probability update
#' mechanism a+b=1,
#' \code{"auto"} - the automatically selected type of occurrence model.
#'
#' The type of model used in the occurrence is equal to the one provided in the
#' \code{model} parameter.
#'
#' Also, a model produced using \link[smooth]{oes} or \link[greybox]{alm} function
#' can be used here.
#' @param ic The information criterion to use in the model selection / combination
#' procedure.
#' @param bounds The type of bounds for the persistence to use in the model
#' estimation. Can be either \code{admissible} - guaranteeing the stability of the
#' model, \code{usual} - restricting the values with (0, 1) or \code{none} - no
#' restrictions (potentially dangerous).
#' @param regressors The variable defines what to do with the provided explanatory
#' variables:
#' \code{"use"} means that all of the data should be used, while
#' \code{"select"} means that a selection using \code{ic} should be done,
#' \code{"adapt"} will trigger the mechanism of time varying parameters for the
#' explanatory variables.
#' @param silent Specifies, whether to provide the progress of the function or not.
#' If \code{TRUE}, then the function will print what it does and how much it has
#' already done.
#' @param ... Other non-documented parameters. For example, \code{FI=TRUE} will
#' make the function also produce Fisher Information matrix, which then can be
#' used to calculated variances of smoothing parameters and initial states of
#' the model. This is calculated based on the hessian of log-likelihood function and
#' accepts \code{stepSize} parameter, determining how it is calculated. The default value
#' is \code{stepSize=.Machine$double.eps^(1/4)}. This is used in the \link[stats]{vcov} method.
#' Number of iterations inside the backcasting loop to do is regulated with \code{nIterations}
#' parameter. By default it is set to 2. Furthermore, starting values of parameters can be
#' passed via \code{B}, while the upper and lower bounds should be passed in \code{ub}
#' and \code{lb} respectively. In this case they will be used for optimisation. These
#' values should have the length equal to the number of parameters to estimate in
#' the following order:
#' \enumerate{
#' \item All smoothing parameters (for the states and then for the explanatory variables);
#' \item Damping parameter (if needed);
#' \item ARMA parameters;
#' \item All the initial values (for the states and then for the explanatory variables).
#' }
#' You can also pass parameters to the optimiser in order to fine tune its work:
#' \itemize{
#' \item \code{maxeval} - maximum number of evaluations to carry out. The default is 40 per
#' estimated parameter for ETS and / or ARIMA and at least 1000 if explanatory variables
#' are introduced in the model (100 per parameter for explanatory variables, but not less
#' than 1000);
#' \item \code{maxtime} - stop, when the optimisation time (in seconds) exceeds this;
#' \item \code{xtol_rel} - the relative precision of the optimiser (the default is 1E-6);
#' \item \code{xtol_abs} - the absolute precision of the optimiser (the default is 1E-8);
#' \item \code{ftol_rel} - the stopping criterion in case of the relative change in the loss
#' function (the default is 1E-8);
#' \item \code{ftol_abs} - the stopping criterion in case of the absolute change in the loss
#' function (the default is 0 - not used);
#' \item \code{algorithm} - the algorithm to use in optimisation
#' (by default, \code{"NLOPT_LN_NELDERMEAD"} is used);
#' \item \code{print_level} - the level of output for the optimiser (0 by default).
#' If equal to 41, then the detailed results of the optimisation are returned.
#' }
#' You can read more about these parameters by running the function
#' \link[nloptr]{nloptr.print.options}.
#' Finally, the parameter \code{lambda} for LASSO / RIDGE, \code{alpha} for the Asymmetric
#' Laplace, \code{shape} for the Generalised Normal and \code{nu} for Student's distributions
#' can be provided here as well.
#'
#' @return Object of class "adam" is returned. It contains the list of the
#' following values:
#' \itemize{
#' \item \code{model} - the name of the constructed model,
#' \item \code{timeElapsed} - the time elapsed for the estimation of the model,
#' \item \code{data} - the in-sample part of the data used for the training of the model. Includes
#' the actual values in the first column,
#' \item \code{holdout} - the holdout part of the data, excluded for purposes of model evaluation,
#' \item \code{fitted} - the vector of fitted values,
#' \item \code{residuals} - the vector of residuals,
#' \item \code{forecast} - the point forecast for h steps ahead (by default NA is returned). NOTE
#' that these do not always correspond to the conditional expectations for ETS models. See ADAM
#' textbook, Section 6.4. for details (\url{https://openforecast.org/adam/ETSTaxonomyMaths.html}),
#' \item \code{states} - the matrix of states with observations in rows and states in columns,
#' \item \code{persisten} - the vector of smoothing parameters,
#' \item \code{phi} - the value of damping parameter,
#' \item \code{transition} - the transition matrix,
#' \item \code{measurement} - the measurement matrix with observations in rows and state elements
#' in columns,
#' \item \code{initial} - the named list of initial values, including level, trend, seasonal, ARIMA
#' and xreg components,
#' \item \code{initialEstimated} - the named vector, defining which of the initials were estimated in
#' the model,
#' \item \code{initialType} - the type of initialisation used ("optimal" / "complete" / "provided"),
#' \item \code{orders} - the orders of ARIMA used in the estimation,
#' \item \code{constant} - the value of the constant (if it was included),
#' \item \code{arma} - the list of AR / MA parameters used in the model,
#' \item \code{nParam} - the matrix of the estimated / provided parameters,
#' \item \code{occurrence} - the oes model used for the occurrence part of the model,
#' \item \code{formula} - the formula used for the explanatory variables expansion,
#' \item \code{loss} - the type of loss function used in the estimation,
#' \item \code{lossValue} - the value of that loss function,
#' \item \code{logLik} - the value of the log-likelihood,
#' \item \code{distribution} - the distribution function used in the calculation of the likelihood,
#' \item \code{scale} - the value of the scale parameter,
#' \item \code{lambda} - the value of the parameter used in LASSO / dalaplace / dt,
#' \item \code{B} - the vector of all estimated parameters,
#' \item \code{lags} - the vector of lags used in the model construction,
#' \item \code{lagsAll} - the vector of the internal lags used in the model,
#' \item \code{profile} - the matrix with the profile used in the construction of the model,
#' \item \code{profileInitial} - the matrix with the initial profile (for the before the sample values),
#' \item \code{call} - the call used in the evaluation,
#' \item \code{bounds} - the type of bounds used in the process,
#' \item \code{res} - result of the model estimation, the output of the \code{nloptr()} function, explaining
#' how optimisation went,
#' \item \code{other} - the list with other parameters, such as shape for distributions or ARIMA
#' polynomials.
#' }
#'
#' @seealso \code{\link[smooth]{es}, \link[smooth]{msarima}}
#'
#' @examples
#'
#' ### The main examples are provided in the adam vignette, check it out via:
#' \dontrun{vignette("adam","smooth")}
#'
#' # Model selection using a specified pool of models
#' ourModel <- adam(rnorm(100,100,10), model=c("ANN","ANA","AAA"), lags=c(5,10))
#' \donttest{adamSummary <- summary(ourModel)
#' xtable(adamSummary)}
#'
#' \donttest{forecast(ourModel)
#' par(mfcol=c(3,4))
#' plot(ourModel, c(1:11))}
#'
#' # Model combination using a specified pool
#' \donttest{ourModel <- adam(rnorm(100,100,10), model=c("ANN","AAN","MNN","CCC"),
#' lags=c(5,10))}
#'
#' # ADAM ARIMA
#' \donttest{ourModel <- adam(rnorm(100,100,10), model="NNN",
#' lags=c(1,4), orders=list(ar=c(1,0),i=c(1,0),ma=c(1,1)))}
#'
#' @importFrom greybox dlaplace dalaplace ds dgnorm
#' @importFrom greybox stepwise alm is.occurrence is.alm polyprod
#' @importFrom stats dnorm dlogis dt dlnorm dgamma frequency confint vcov predict
#' @importFrom stats formula update model.frame model.matrix contrasts setNames terms reformulate
#' @importFrom stats acf pacf
#' @importFrom statmod dinvgauss
#' @importFrom nloptr nloptr
#' @importFrom pracma hessian
#' @importFrom zoo zoo
#' @importFrom utils head
#' @rdname adam
#' @export adam
adam <- function(data, model="ZXZ", lags=c(frequency(data)), orders=list(ar=c(0),i=c(0),ma=c(0),select=FALSE),
constant=FALSE, formula=NULL, regressors=c("use","select","adapt"),
occurrence=c("none","auto","fixed","general","odds-ratio","inverse-odds-ratio","direct"),
distribution=c("default","dnorm","dlaplace","ds","dgnorm",
"dlnorm","dinvgauss","dgamma"),
loss=c("likelihood","MSE","MAE","HAM","LASSO","RIDGE","MSEh","TMSE","GTMSE","MSCE"),
outliers=c("ignore","use","select"), level=0.99,
h=0, holdout=FALSE,
persistence=NULL, phi=NULL, initial=c("optimal","backcasting","complete"), arma=NULL,
ic=c("AICc","AIC","BIC","BICc"), bounds=c("usual","admissible","none"),
silent=TRUE, ...){
# Copyright (C) 2019 - Inf Ivan Svetunkov
# Start measuring the time of calculations
startTime <- Sys.time();
cl <- match.call();
# Record the parental environment. Needed for ARIMA initialisation
env <- parent.frame();
ellipsis <- list(...);
# Assume that the model is not provided
profilesRecentProvided <- FALSE;
profilesRecentTable <- NULL;
# paste0() is needed in order to get rid of potential issues with names
yName <- paste0(deparse(substitute(data)),collapse="");
# If a previous model is provided as a model, write down the variables
if(is.adam(model) || is.adam.sim(model)){
# If this is the simulated data, extract the parameters
# if(is.adam.sim(model) & !is.null(dim(model$data))){
# warning("The provided model has several submodels. Choosing a random one.",call.=FALSE);
# i <- round(runif(1,1:length(model$persistence)));
# persistence <- model$persistence[,i];
# initial <- model$initial[,i];
# initialSeason <- model$initialSeason[,i];
# if(any(model$iprob!=1)){
# occurrence <- "a";
# }
# }
# else{
initial <- model$initial;
initialEstimated <- model$initialEstimated;
distribution <- model$distribution;
loss <- model$loss;
persistence <- model$persistence;
phi <- model$phi;
if(model$initialType!="complete"){
initial <- model$initial;
}
else{
initial <- "b";
}
occurrence <- model$occurrence;
ic <- model$ic;
bounds <- model$bounds;
# lambda for LASSO
ellipsis$lambda <- model$other$lambda;
# parameters for distributions
ellipsis$alpha <- model$other$alpha;
ellipsis$shape <- model$other$shape;
ellipsis$nu <- model$other$nu;
ellipsis$B <- model$B;
CFValue <- model$lossValue;
logLikADAMValue <- logLik(model);
lagsModelAll <- modelLags(model);
# This needs to be fixed to align properly in case of various seasonals
profilesRecentTable <- model$profileInitial;
profilesRecentProvided[] <- TRUE;
regressors <- model$regressors;
if(is.null(formula)){
formula <- formula(model);
}
# Parameters of the original ARIMA model
lags <- lags(model);
orders <- orders(model);
constant <- model$constant;
if(is.null(constant)){
constant <- FALSE;
}
arma <- model$arma;
model <- modelType(model);
modelDo <- "use";
# if(any(unlist(gregexpr("C",model))!=-1)){
# initial <- "o";
# }
}
else if(inherits(model,"ets")){
# Extract smoothing parameters
i <- 1;
lags <- 1;
persistence <- coef(model)[i];
if(model$components[2]!="N"){
i <- i+1;
persistence <- c(persistence,coef(model)[i]);
if(model$components[3]!="N"){
i <- i+1;
persistence <- c(persistence,coef(model)[i]);
}
}
else{
if(model$components[3]!="N"){
i <- i+1;
persistence <- c(persistence,coef(model)[i]);
}
}
# Damping parameter
if(model$components[4]=="TRUE"){
i <- i+1;
phi <- coef(model)[i];
}
# Initials
i <- i+1;
initial <- coef(model)[i];
# Initial for the trend
if(model$components[2]!="N"){
i <- i+1;
lags <- c(lags,1);
initial <- c(initial,coef(model)[i]);
}
# Initials of seasonal component
if(model$components[3]!="N"){
if(model$components[2]!="N"){
initial <- c(initial,rev(model$states[1,-c(1:2)]));
}
else{
initial <- c(initial,rev(model$states[1,-c(1)]));
}
lags <- c(lags,model$m);
}
model <- modelType(model);
distribution <- "dnorm";
loss <- "likelihood";
modelDo <- "use"
}
else if(is.character(model)){
modelDo <- "";
# Everything is okay
}
else{
modelDo <- "";
warning("A model of an unknown class was provided. Switching to 'ZZZ'.",call.=FALSE);
model <- "ZZZ";
}
#### Check the parameters of the function and create variables based on them ####
checkerReturn <- parametersChecker(data, model, lags, formula, orders, constant, arma,
outliers, level,
persistence, phi, initial,
distribution, loss, h, holdout, occurrence, ic, bounds,
regressors, yName,
silent, modelDo, ParentEnvironment=environment(), ellipsis, fast=FALSE);
#### Return regression if it is pure ####
if(is.alm(checkerReturn)){
obsInSample <- nobs(checkerReturn);
nParam <- length(checkerReturn$coefficient);
modelReturned <- list(model="Regression");
modelReturned$timeElapsed <- Sys.time()-startTime;
modelReturned$call <- checkerReturn$call;
if(is.null(formula)){
formula <- formula(checkerReturn);
}
if(holdout){
# Robustify the names of variables
colnames(data) <- make.names(colnames(data),unique=TRUE);
modelReturned$holdout <- data[obsInSample+c(1:h),,drop=FALSE];
}
else{
modelReturned$holdout <- NULL;
}
responseName <- all.vars(formula)[1];
y <- data[,responseName];
# Extract indeces from the data
yIndex <- try(time(y),silent=TRUE);
# If we cannot extract time, do something
if(inherits(yIndex,"try-error")){
if(!is.data.frame(data) && !is.null(dim(data))){
yIndex <- as.POSIXct(rownames(data));
}
else if(is.data.frame(data)){
yIndex <- c(1:nrow(data));
}
else{
yIndex <- c(1:length(data));
}
}
# Prepare fitted, residuals and the forecasts
if(inherits(y ,"zoo")){
modelReturned$data <- data[1:obsInSample,,drop=FALSE];
modelReturned$fitted <- zoo(fitted(checkerReturn), order.by=yIndex[1:obsInSample]);
modelReturned$residuals <- zoo(residuals(checkerReturn), order.by=yIndex[1:obsInSample]);
# If we need to forecast and we had holdout=FALSE...
if(h>0){
if(holdout){
modelReturned$forecast <- zoo(forecast(checkerReturn,h=h,newdata=tail(data,h),interval="none")$mean,
order.by=yIndex[obsInSample+1:h]);
}
else{
modelReturned$forecast <- zoo(forecast(checkerReturn,h=h,interval="none")$mean,
order.by=yIndex[obsInSample+1:h]);
}
}
else{
modelReturned$forecast <- zoo(NA, order.by=yIndex[obsInSample+1]);
}
modelReturned$states <- zoo(matrix(coef(checkerReturn), obsInSample+1, nParam, byrow=TRUE,
dimnames=list(NULL, names(coef(checkerReturn)))),
order.by=c(yIndex[1]-diff(yIndex[1:2]),yIndex[1:obsInSample]));
}
else{
yFrequency <- frequency(y);
modelReturned$data <- ts(data[1:obsInSample,,drop=FALSE], start=yIndex[1], frequency=yFrequency);
modelReturned$fitted <- ts(fitted(checkerReturn), start=yIndex[1], frequency=yFrequency);
modelReturned$residuals <- ts(residuals(checkerReturn), start=yIndex[1], frequency=yFrequency);
if(h>0){
if(holdout){
modelReturned$forecast <- ts(forecast(checkerReturn,h=h,newdata=tail(data,h),interval="none")$mean,
start=yIndex[obsInSample+1], frequency=yFrequency);
}
else{
modelReturned$forecast <- ts(as.numeric(forecast(checkerReturn,h=h,interval="none")$mean),
start=yIndex[obsInSample]+diff(yIndex[1:2]), frequency=yFrequency);
}
}
else{
modelReturned$forecast <- ts(NA, start=yIndex[obsInSample]+diff(yIndex[1:2]), frequency=yFrequency);
}
modelReturned$states <- ts(matrix(coef(checkerReturn), obsInSample+1, nParam, byrow=TRUE,
dimnames=list(NULL, names(coef(checkerReturn)))),
start=yIndex[1]-diff(yIndex[1:2]), frequency=yFrequency);
}
modelReturned$persistence <- rep(0, nParam);
names(modelReturned$persistence) <- paste0("delta",c(1:nParam));
modelReturned$phi <- 1;
modelReturned$transition <- diag(nParam);
modelReturned$measurement <- checkerReturn$data;
modelReturned$measurement[,1] <- 1;
colnames(modelReturned$measurement) <- colnames(modelReturned$states);
modelReturned$initial <- list(xreg=coef(checkerReturn));
modelReturned$initialType <- "optimal";
modelReturned$initialEstimated <- TRUE;
names(modelReturned$initialEstimated) <- "xreg";
modelReturned$orders <- list(ar=0,i=0,ma=0);
modelReturned$arma <- NULL;
# Number of estimated parameters
parametersNumber <- matrix(0,2,5,
dimnames=list(c("Estimated","Provided"),
c("nParamInternal","nParamXreg","nParamOccurrence","nParamScale","nParamAll")));
parametersNumber[1,2] <- nParam;
if(is.occurrence(checkerReturn$occurrence)){
parametersNumber[1,3] <- nParam;
}
parametersNumber[1,5] <- sum(parametersNumber[1,1:3]);
modelReturned$nParam <- parametersNumber;
modelReturned$formula <- formula(checkerReturn);
modelReturned$regressors <- "use";
modelReturned$loss <- checkerReturn$loss;
modelReturned$lossValue <- checkerReturn$lossValue;
modelReturned$lossFunction <- checkerReturn$lossFunction;
modelReturned$logLik <- logLik(checkerReturn);
modelReturned$distribution <- checkerReturn$distribution;
modelReturned$scale <- checkerReturn$scale;
modelReturned$other <- checkerReturn$other;
modelReturned$B <- coef(checkerReturn);
modelReturned$lags <- 1;
modelReturned$lagsAll <- rep(1,nParam);
modelReturned$FI <- checkerReturn$FI;
modelReturned$occurrence <- checkerReturn$occurrence;
if(holdout){
# This won't work if transformations of the response variable are done...
modelReturned$accuracy <- measures(modelReturned$holdout[,responseName],modelReturned$forecast,
modelReturned$data[,responseName]);
}
else{
modelReturned$accuracy <- NULL;
}
class(modelReturned) <- c("adam","smooth");
if(!silent){
plot(modelReturned,7)
}
return(modelReturned);
}
#### If select was provided in the model, do auto.adam selection ####
if(!is.null(checkerReturn$select) && checkerReturn$select){
return(do.call("auto.adam",list(data=substitute(data), model=model, lags=lags, orders=orders,
formula=formula, regressors=regressors,
distribution=distribution, loss=loss,
h=h, holdout=holdout, outliers=outliers, level=level,
persistence=persistence, phi=phi, initial=initial, arma=arma,
occurrence=occurrence,
ic=ic, bounds=bounds, silent=silent, ...)));
}
#### The function creates the technical variables (lags etc) based on the type of the model ####
architector <- function(etsModel, Etype, Ttype, Stype, lags, lagsModelSeasonal,
xregNumber, obsInSample, initialType,
arimaModel, lagsModelARIMA, xregModel, constantRequired,
profilesRecentTable=NULL, profilesRecentProvided=FALSE){
# If there is ETS
if(etsModel){
modelIsTrendy <- Ttype!="N";
if(modelIsTrendy){
# Make lags (1, 1)
lagsModel <- matrix(c(1,1),ncol=1);
componentsNamesETS <- c("level","trend");
}
else{
# Make lags (1, ...)
lagsModel <- matrix(c(1),ncol=1);
componentsNamesETS <- c("level");
}
modelIsSeasonal <- Stype!="N";
if(modelIsSeasonal){
# If the lags are for the non-seasonal model
lagsModel <- matrix(c(lagsModel,lagsModelSeasonal),ncol=1);
componentsNumberETSSeasonal <- length(lagsModelSeasonal);
if(componentsNumberETSSeasonal>1){
componentsNamesETS <- c(componentsNamesETS,paste0("seasonal",c(1:componentsNumberETSSeasonal)));
}
else{
componentsNamesETS <- c(componentsNamesETS,"seasonal");
}
}
else{
componentsNumberETSSeasonal <- 0;
}
lagsModelAll <- lagsModel;
componentsNumberETS <- length(lagsModel);
}
else{
modelIsTrendy <- modelIsSeasonal <- FALSE;
componentsNumberETS <- componentsNumberETSSeasonal <- 0;
componentsNamesETS <- NULL;
lagsModelAll <- lagsModel <- NULL;
}
# If there is ARIMA
if(arimaModel){
lagsModelAll <- matrix(c(lagsModel,lagsModelARIMA), ncol=1);
}
# If constant is needed, add it
if(constantRequired){
lagsModelAll <- matrix(c(lagsModelAll,1), ncol=1);
}
# If there are xreg
if(xregModel){
lagsModelAll <- matrix(c(lagsModelAll,rep(1,xregNumber)), ncol=1);
}
lagsModelMax <- max(lagsModelAll);
# Define the number of cols that should be in the matvt
obsStates <- obsInSample + lagsModelMax;
# Create ADAM profiles for correct treatment of seasonality
adamProfiles <- adamProfileCreator(lagsModelAll, lagsModelMax, obsAll,
lags=lags, yIndex=yIndexAll, yClasses=yClasses);
if(profilesRecentProvided){
profilesRecentTable <- profilesRecentTable[,1:lagsModelMax,drop=FALSE];
}
else{
profilesRecentTable <- adamProfiles$recent;
}
indexLookupTable <- adamProfiles$lookup;
return(list(lagsModel=lagsModel,lagsModelAll=lagsModelAll, lagsModelMax=lagsModelMax,
componentsNumberETS=componentsNumberETS, componentsNumberETSSeasonal=componentsNumberETSSeasonal,
componentsNumberETSNonSeasonal=componentsNumberETS-componentsNumberETSSeasonal,
componentsNamesETS=componentsNamesETS, obsStates=obsStates, modelIsTrendy=modelIsTrendy,
modelIsSeasonal=modelIsSeasonal,
indexLookupTable=indexLookupTable, profilesRecentTable=profilesRecentTable));
}
#### The function creates the necessary matrices based on the model and provided parameters ####
# This is needed in order to initialise the estimation
creator <- function(etsModel, Etype, Ttype, Stype, modelIsTrendy, modelIsSeasonal,
lags, lagsModel, lagsModelARIMA, lagsModelAll, lagsModelMax,
profilesRecentTable=NULL, profilesRecentProvided=FALSE,
obsStates, obsInSample, obsAll, componentsNumberETS, componentsNumberETSSeasonal,
componentsNamesETS, otLogical, yInSample,
# Persistence and phi
persistence, persistenceEstimate,
persistenceLevel, persistenceLevelEstimate, persistenceTrend, persistenceTrendEstimate,
persistenceSeasonal, persistenceSeasonalEstimate,
persistenceXreg, persistenceXregEstimate, persistenceXregProvided,
phi,
# Initials
initialType, initialEstimate,
initialLevel, initialLevelEstimate, initialTrend, initialTrendEstimate,
initialSeasonal, initialSeasonalEstimate,
initialArima, initialArimaEstimate, initialArimaNumber,
initialXregEstimate, initialXregProvided,
# ARIMA elements
arimaModel, arRequired, iRequired, maRequired, armaParameters,
arOrders, iOrders, maOrders,
componentsNumberARIMA, componentsNamesARIMA,
# Explanatory variables
xregModel, xregModelInitials, xregData, xregNumber, xregNames,
xregParametersPersistence,
# Constant
constantRequired, constantEstimate, constantValue, constantName){
# Matrix of states. Time in columns, components in rows
matVt <- matrix(NA, componentsNumberETS+componentsNumberARIMA+xregNumber+constantRequired, obsStates,
dimnames=list(c(componentsNamesETS,componentsNamesARIMA,xregNames,constantName),NULL));
# Measurement rowvector
matWt <- matrix(1, obsAll, componentsNumberETS+componentsNumberARIMA+xregNumber+constantRequired,
dimnames=list(NULL,c(componentsNamesETS,componentsNamesARIMA,xregNames,constantName)));
# If xreg are provided, then fill in the respective values in Wt vector
if(xregModel){
matWt[,componentsNumberETS+componentsNumberARIMA+1:xregNumber] <- xregData;
}
# Transition matrix
matF <- diag(componentsNumberETS+componentsNumberARIMA+xregNumber+constantRequired);
# Persistence vector
vecG <- matrix(0, componentsNumberETS+componentsNumberARIMA+xregNumber+constantRequired, 1,
dimnames=list(c(componentsNamesETS,componentsNamesARIMA,xregNames,constantName),NULL));
j <- 0;
# ETS model, persistence
if(etsModel){
j <- j+1;
rownames(vecG)[j] <- "alpha";
if(!persistenceLevelEstimate){
vecG[j,] <- persistenceLevel;
}
if(modelIsTrendy){
j <- j+1;
rownames(vecG)[j] <- "beta";
if(!persistenceTrendEstimate){
vecG[j,] <- persistenceTrend;
}
}
if(modelIsSeasonal){
if(!all(persistenceSeasonalEstimate)){
vecG[j+which(!persistenceSeasonalEstimate),] <- persistenceSeasonal;
}
if(componentsNumberETSSeasonal>1){
rownames(vecG)[j+c(1:componentsNumberETSSeasonal)] <- paste0("gamma",c(1:componentsNumberETSSeasonal));
}
else{
rownames(vecG)[j+1] <- "gamma";
}
j <- j+componentsNumberETSSeasonal;
}
}
# ARIMA model, names for persistence
if(arimaModel){
# Remove diagonal from the ARIMA part of the matrix
matF[j+1:componentsNumberARIMA,j+1:componentsNumberARIMA] <- 0;
if(componentsNumberARIMA>1){
rownames(vecG)[j+1:componentsNumberARIMA] <- paste0("psi",c(1:componentsNumberARIMA));
}
else{
rownames(vecG)[j+1:componentsNumberARIMA] <- "psi";
}
j <- j+componentsNumberARIMA;
}
# Modify transition to do drift
if(!arimaModel && constantRequired){
matF[1,ncol(matF)] <- 1;
}
# Regression, persistence
if(xregModel){
if(persistenceXregProvided && !persistenceXregEstimate){
vecG[j+1:xregNumber,] <- persistenceXreg;
}
rownames(vecG)[j+1:xregNumber] <- paste0("delta",xregParametersPersistence);
}
# Damping parameter value
if(etsModel && modelIsTrendy){
matF[1,2] <- phi;
matF[2,2] <- phi;
matWt[,2] <- phi;
}
# If the arma parameters were provided, fill in the persistence
if(arimaModel && (!arEstimate && !maEstimate)){
# Call polynomial
# arimaPolynomials <- polynomialiser(NULL, arOrders, iOrders, maOrders,
# arRequired, maRequired, arEstimate, maEstimate, armaParameters, lags);
arimaPolynomials <- lapply(adamPolynomialiser(0, arOrders, iOrders, maOrders,
arEstimate, maEstimate, armaParameters, lags), as.vector);
# Fill in the transition matrix
if(nrow(nonZeroARI)>0){
matF[componentsNumberETS+nonZeroARI[,2],componentsNumberETS+nonZeroARI[,2]] <-
-arimaPolynomials$ariPolynomial[nonZeroARI[,1]];
}
# Fill in the persistence vector
if(nrow(nonZeroARI)>0){
vecG[componentsNumberETS+nonZeroARI[,2]] <- -arimaPolynomials$ariPolynomial[nonZeroARI[,1]];
}
if(nrow(nonZeroMA)>0){
vecG[componentsNumberETS+nonZeroMA[,2]] <- vecG[componentsNumberETS+nonZeroMA[,2]] +
arimaPolynomials$maPolynomial[nonZeroMA[,1]];
}
}
else{
arimaPolynomials <- NULL;
}
if(!profilesRecentProvided){
# ETS model, initial state
# If something needs to be estimated...
if(etsModel){
if(initialEstimate){
# For the seasonal models
if(modelIsSeasonal){
if(obsNonzero>=lagsModelMax*2){
# If either Etype or Stype are multiplicative, do multiplicative decomposition
decompositionType <- c("additive","multiplicative")[any(c(Etype,Stype)=="M")+1];
yDecomposition <- msdecompose(yInSample, lags[lags!=1], type=decompositionType);
j <- 1;
# level
if(initialLevelEstimate){
matVt[j,1:lagsModelMax] <- yDecomposition$initial[1];
# matVt[j,1:lagsModelMax] <- mean(yInSample[1:lagsModelMax]);
if(xregModel){
if(Etype=="A"){
matVt[j,1:lagsModelMax] <- matVt[j,1:lagsModelMax] -
as.vector(xregModelInitials[[1]]$initialXreg %*% xregData[1,]);
}
else{
matVt[j,1:lagsModelMax] <- matVt[j,1:lagsModelMax] /
as.vector(exp(xregModelInitials[[2]]$initialXreg %*% xregData[1,]));
}
}
}
else{
matVt[j,1:lagsModelMax] <- initialLevel;
}
j <- j+1;
# If trend is needed
if(modelIsTrendy){
if(initialTrendEstimate){
if(Ttype=="A" && Stype=="M"){
# if(initialLevelEstimate){
# # level fix
# matVt[j-1,1:lagsModelMax] <- exp(mean(log(yInSample[otLogical][1:lagsModelMax])));
# }
# trend
matVt[j,1:lagsModelMax] <- prod(yDecomposition$initial)-yDecomposition$initial[1];
# If the initial trend is higher than the lowest value, initialise with zero.
# This is a failsafe mechanism for the mixed models
if(matVt[j,1]<0 && abs(matVt[j,1])>min(abs(yInSample[otLogical]))){
matVt[j,1:lagsModelMax] <- 0;
}
}
else if(Ttype=="M" && Stype=="A"){
# if(initialLevelEstimate){
# # level fix
# matVt[j-1,1:lagsModelMax] <- exp(mean(log(yInSample[otLogical][1:lagsModelMax])));
# }
# trend
matVt[j,1:lagsModelMax] <- sum(abs(yDecomposition$initial))/abs(yDecomposition$initial[1]);
}
else if(Ttype=="M"){
# trend is too dangerous, make it start from 1.
matVt[j,1:lagsModelMax] <- 1;
}
else{
# trend
matVt[j,1:lagsModelMax] <- yDecomposition$initial[2];
}
# This is a failsafe for multiplicative trend models, so that the thing does not explode
if(Ttype=="M" && any(matVt[j,1:lagsModelMax]>1.1)){
matVt[j,1:lagsModelMax] <- 1;
}
# This is a failsafe for multiplicative trend models, so that the thing does not explode
if(Ttype=="M" && any(matVt[1,1:lagsModelMax]<0)){
matVt[1,1:lagsModelMax] <- yInSample[otLogical][1];
}
}
else{
matVt[j,1:lagsModelMax] <- initialTrend;
}
j <- j+1;
}
#### Seasonal components
# For pure models use stuff as is
if(all(c(Etype,Stype)=="A") || all(c(Etype,Stype)=="M") ||
(Etype=="A" & Stype=="M")){
for(i in 1:componentsNumberETSSeasonal){
if(initialSeasonalEstimate[i]){
matVt[i+j-1,1:lagsModel[i+j-1]] <- yDecomposition$seasonal[[i]];
# Renormalise the initial seasons
if(Stype=="A"){
matVt[i+j-1,1:lagsModel[i+j-1]] <-
matVt[i+j-1,1:lagsModel[i+j-1]] -
mean(matVt[i+j-1,1:lagsModel[i+j-1]]);
}
else{
matVt[i+j-1,1:lagsModel[i+j-1]] <-
matVt[i+j-1,1:lagsModel[i+j-1]] /
exp(mean(log(matVt[i+j-1,1:lagsModel[i+j-1]])));
}
}
else{
matVt[i+j-1,1:lagsModel[i+j-1]] <- initialSeasonal[[i]];
}
}
}
# For mixed models use a different set of initials
else if(Etype=="M" && Stype=="A"){
for(i in 1:componentsNumberETSSeasonal){
if(initialSeasonalEstimate[i]){
matVt[i+j-1,1:lagsModel[i+j-1]] <- log(yDecomposition$seasonal[[i]])*min(yInSample[otLogical]);
# Renormalise the initial seasons
if(Stype=="A"){
matVt[i+j-1,1:lagsModel[i+j-1]] <- matVt[i+j-1,1:lagsModel[i+j-1]] -
mean(matVt[i+j-1,1:lagsModel[i+j-1]]);
}
else{
matVt[i+j-1,1:lagsModel[i+j-1]] <- matVt[i+j-1,1:lagsModel[i+j-1]] /
exp(mean(log(matVt[i+j-1,1:lagsModel[i+j-1]])));
}
}
else{
matVt[i+j-1,1:lagsModel[i+j-1]] <- initialSeasonal[[i]];
}
}
}
}
else{
# If either Etype or Stype are multiplicative, do multiplicative decomposition
j <- 1;
# level
if(initialLevelEstimate){
matVt[j,1:lagsModelMax] <- mean(yInSample[1:lagsModelMax]);
if(xregModel){
if(Etype=="A"){
matVt[j,1:lagsModelMax] <- matVt[j,1:lagsModelMax] -
as.vector(xregModelInitials[[1]]$initialXreg %*% xregData[1,]);
}
else{
matVt[j,1:lagsModelMax] <- matVt[j,1:lagsModelMax] /
as.vector(exp(xregModelInitials[[2]]$initialXreg %*% xregData[1,]));
}
}
}
else{
matVt[j,1:lagsModelMax] <- initialLevel;
}
j <- j+1;
if(modelIsTrendy){
if(initialTrendEstimate){
if(Ttype=="A"){
# trend
matVt[j,1:lagsModelMax] <- yInSample[2]-yInSample[1];
}
else if(Ttype=="M"){
if(initialLevelEstimate){
# level fix
matVt[j-1,1:lagsModelMax] <- exp(mean(log(yInSample[otLogical][1:lagsModelMax])));
}
# trend
matVt[j,1:lagsModelMax] <- yInSample[2]/yInSample[1];
}
# This is a failsafe for multiplicative trend models, so that the thing does not explode
if(Ttype=="M" && any(matVt[j,1:lagsModelMax]>1.1)){
matVt[j,1:lagsModelMax] <- 1;
}
}
else{
matVt[j,1:lagsModelMax] <- initialTrend;
}
# Do roll back. Especially useful for backcasting and multisteps
if(Ttype=="A"){
matVt[j-1,1:lagsModelMax] <- matVt[j-1,1] - matVt[j,1]*lagsModelMax;
}
else if(Ttype=="M"){
matVt[j-1,1:lagsModelMax] <- matVt[j-1,1] / matVt[j,1]^lagsModelMax;
}
j <- j+1;
}
#### Seasonal components
# For pure models use stuff as is
if(Stype=="A"){
for(i in 1:componentsNumberETSSeasonal){
if(initialSeasonalEstimate[i]){
matVt[i+j-1,1:lagsModel[i+j-1]] <- yInSample[1:lagsModel[i+j-1]]-matVt[1,1];
# Renormalise the initial seasons
matVt[i+j-1,1:lagsModel[i+j-1]] <- matVt[i+j-1,1:lagsModel[i+j-1]] -
mean(matVt[i+j-1,1:lagsModel[i+j-1]]);
}
else{
matVt[i+j-1,1:lagsModel[i+j-1]] <- initialSeasonal[[i]];
}
}
}
# For mixed models use a different set of initials
else{
for(i in 1:componentsNumberETSSeasonal){
if(initialSeasonalEstimate[i]){
# abs() is needed for mixed ETS+ARIMA
matVt[i+j-1,1:lagsModel[i+j-1]] <- yInSample[1:lagsModel[i+j-1]]/abs(matVt[1,1]);
# Renormalise the initial seasons
matVt[i+j-1,1:lagsModel[i+j-1]] <- matVt[i+j-1,1:lagsModel[i+j-1]] /
exp(mean(log(matVt[i+j-1,1:lagsModel[i+j-1]])));
}
else{
matVt[i+j-1,1:lagsModel[i+j-1]] <- initialSeasonal[[i]];
}
}
}
}
}
# Non-seasonal models
else{
# level
if(initialLevelEstimate){
matVt[1,1:lagsModelMax] <- mean(yInSample[1:max(lagsModelMax,ceiling(obsInSample*0.2))]);
# if(xregModel){
# if(Etype=="A"){
# matVt[1,1:lagsModelMax] <- matVt[1,lagsModelMax] -
# as.vector(xregModelInitials[[1]]$initialXreg %*% xregData[1,]);
# }
# else{
# matVt[1,1:lagsModelMax] <- matVt[1,lagsModelMax] /
# as.vector(exp(xregModelInitials[[2]]$initialXreg %*% xregData[1,]));
# }
# }
}
else{
matVt[1,1:lagsModelMax] <- initialLevel;
}
if(modelIsTrendy){
if(initialTrendEstimate){
matVt[2,1:lagsModelMax] <- switch(Ttype,
"A" = mean(diff(yInSample[1:max(lagsModelMax+1,
ceiling(obsInSample*0.2))]),
na.rm=TRUE),
"M" = exp(mean(diff(log(yInSample[otLogical])),na.rm=TRUE)));
}
else{
matVt[2,1:lagsModelMax] <- initialTrend;
}
}
}
if(initialLevelEstimate && Etype=="M" && matVt[1,lagsModelMax]==0){
matVt[1,1:lagsModelMax] <- mean(yInSample);
}
}
# Else, insert the provided ones... make sure that this is not a backcasting
else if(!initialEstimate && initialType=="provided"){
j <- 1;
matVt[j,1:lagsModelMax] <- initialLevel;
if(modelIsTrendy){
j <- j+1;
matVt[j,1:lagsModelMax] <- initialTrend;
}
if(modelIsSeasonal){
for(i in 1:componentsNumberETSSeasonal){
# This is misaligned, but that's okay, because this goes directly to profileRecent
# matVt[j+i,(lagsModelMax-lagsModel[j+i])+1:lagsModel[j+i]] <- initialSeasonal[[i]];
matVt[j+i,1:lagsModel[j+i]] <- initialSeasonal[[i]];
}
}
j <- j+componentsNumberETSSeasonal;
}
}
# If ARIMA orders are specified, prepare initials
if(arimaModel){
if(initialArimaEstimate){
matVt[componentsNumberETS+1:componentsNumberARIMA, 1:initialArimaNumber] <-
switch(Etype, "A"=0, "M"=1);
if(any(lags>1)){
yDecomposition <- tail(msdecompose(yInSample,
lags[lags!=1],
type=switch(Etype,
"A"="additive",
"M"="multiplicative"))$seasonal,1)[[1]];
}
else{
yDecomposition <- switch(Etype,
"A"=mean(diff(yInSample[otLogical])),
"M"=exp(mean(diff(log(yInSample[otLogical])))));
}
matVt[componentsNumberETS+componentsNumberARIMA, 1:initialArimaNumber] <-
rep(yDecomposition,ceiling(initialArimaNumber/max(lags)))[1:initialArimaNumber];
# rep(yInSample[1:initialArimaNumber],each=componentsNumberARIMA);
# Failsafe mechanism in case the sample is too small
# matVt[is.na(matVt)] <- switch(Etype, "A"=0, "M"=1);
# If this is just ARIMA with optimisation, refine the initials
# if(!etsModel && initialType!="complete"){
# arimaPolynomials <- polynomialiser(rep(0.1,sum(c(arOrders,maOrders))), arOrders, iOrders, maOrders,
# arRequired, maRequired, arEstimate, maEstimate, armaParameters, lags);
# if(nrow(nonZeroARI)>0 && nrow(nonZeroARI)>=nrow(nonZeroMA)){
# matVt[componentsNumberETS+nonZeroARI[,2],
# 1:initialArimaNumber] <-
# switch(Etype,
# "A"=arimaPolynomials$ariPolynomial[nonZeroARI[,1]] %*%
# t(matVt[componentsNumberETS+componentsNumberARIMA, 1:initialArimaNumber]) /
# tail(arimaPolynomials$ariPolynomial,1),
# "M"=exp(arimaPolynomials$ariPolynomial[nonZeroARI[,1]] %*%
# t(log(matVt[componentsNumberETS+componentsNumberARIMA, 1:initialArimaNumber])) /
# tail(arimaPolynomials$ariPolynomial,1)));
# }
# else{
# matVt[componentsNumberETS+nonZeroMA[,2],
# 1:initialArimaNumber] <-
# switch(Etype,
# "A"=arimaPolynomials$maPolynomial[nonZeroMA[,1]] %*%
# t(matVt[componentsNumberETS+componentsNumberARIMA, 1:initialArimaNumber]) /
# tail(arimaPolynomials$maPolynomial,1),
# "M"=exp(arimaPolynomials$maPolynomial[nonZeroMA[,1]] %*%
# t(log(matVt[componentsNumberETS+componentsNumberARIMA, 1:initialArimaNumber])) /
# tail(arimaPolynomials$maPolynomial,1)));
# }
# }
}
else{
# Fill in the matrix with 0 / 1, just in case if the state will not be updated anymore
matVt[componentsNumberETS+1:componentsNumberARIMA, 1:initialArimaNumber] <-
switch(Etype, "A"=0, "M"=1);
# Insert the provided initials
matVt[componentsNumberETS+componentsNumberARIMA, 1:initialArimaNumber] <-
initialArima[1:initialArimaNumber];
# matVt[componentsNumberETS+nonZeroARI[,2], 1:initialArimaNumber] <-
# switch(Etype,
# "A"=arimaPolynomials$ariPolynomial[nonZeroARI[,1]] %*% t(initialArima[1:initialArimaNumber]) /
# tail(arimaPolynomials$ariPolynomial,1),
# "M"=exp(arimaPolynomials$ariPolynomial[nonZeroARI[,1]] %*% t(log(initialArima[1:initialArimaNumber])) /
# tail(arimaPolynomials$ariPolynomial,1)));
# If only AR is needed, but provided or if both are needed, but provided
# if(((arRequired && !arEstimate) && !maRequired) ||
# ((arRequired && !arEstimate) && (maRequired && !maEstimate)) ||
# (iRequired && !arEstimate && !maEstimate)){
# matVt[componentsNumberETS+nonZeroARI[,2],1:initialArimaNumber] <-
# switch(Etype,
# "A"=arimaPolynomials$ariPolynomial[nonZeroARI[,1]] %*%
# t(initialArima[1:initialArimaNumber]) /
# tail(arimaPolynomials$ariPolynomial,1),
# "M"=exp(arimaPolynomials$ariPolynomial[nonZeroARI[,1]] %*%
# t(log(initialArima[1:initialArimaNumber])) /
# tail(arimaPolynomials$ariPolynomial,1)));
# }
# If only MA is needed, but provided
# else if(((maRequired && !maEstimate) && !arRequired)){
# matVt[componentsNumberETS+nonZeroMA[,2],1:initialArimaNumber] <-
# switch(Etype,
# "A"=arimaPolynomials$maPolynomial[nonZeroMA[,1]] %*%
# t(initialArima[1:initialArimaNumber]) /
# tail(arimaPolynomials$maPolynomial,1),
# "M"=exp(arimaPolynomials$maPolynomial[nonZeroMA[,1]] %*%
# t(log(initialArima[1:initialArimaNumber])) /
# tail(arimaPolynomials$maPolynomial,1)));
# }
}
}
# Fill in the initials for xreg
if(xregModel){
if(Etype=="A" || initialXregProvided || is.null(xregModelInitials[[2]])){
matVt[componentsNumberETS+componentsNumberARIMA+1:xregNumber,
1:lagsModelMax] <- xregModelInitials[[1]]$initialXreg;
}
else{
matVt[componentsNumberETS+componentsNumberARIMA+1:xregNumber,
1:lagsModelMax] <- xregModelInitials[[2]]$initialXreg;
}
}
# Add constant if needed
if(constantRequired){
if(constantEstimate){
# Add the mean of data
if(sum(iOrders)==0 && !etsModel){
matVt[componentsNumberETS+componentsNumberARIMA+xregNumber+1,] <- mean(yInSample[otLogical]);
}
# Add first differences
else{
matVt[componentsNumberETS+componentsNumberARIMA+xregNumber+1,] <-
switch(Etype,
"A"=mean(diff(yInSample[otLogical])),
"M"=exp(mean(diff(log(yInSample[otLogical])))));
}
}
else{
matVt[componentsNumberETS+componentsNumberARIMA+xregNumber+1,] <- constantValue;
}
# If ETS model is used, change the initial level
if(etsModel && initialLevelEstimate){
if(Etype=="A"){
matVt[1,1:lagsModelMax] <- matVt[1,1:lagsModelMax] -
matVt[componentsNumberETS+componentsNumberARIMA+xregNumber+1,1];
}
else{
matVt[1,1:lagsModelMax] <- matVt[1,1:lagsModelMax] /
matVt[componentsNumberETS+componentsNumberARIMA+xregNumber+1,1];
}
}
# If ARIMA is done, debias states
if(arimaModel && initialArimaEstimate){
if(Etype=="A"){
matVt[componentsNumberETS+nonZeroARI[,2],1:initialArimaNumber] <-
matVt[componentsNumberETS+nonZeroARI[,2],1:initialArimaNumber] -
matVt[componentsNumberETS+componentsNumberARIMA+xregNumber+1,1];
}
else{
matVt[componentsNumberETS+nonZeroARI[,2],1:initialArimaNumber] <-
matVt[componentsNumberETS+nonZeroARI[,2],1:initialArimaNumber] /
matVt[componentsNumberETS+componentsNumberARIMA+xregNumber+1,1];
}
}
}
}
else{
matVt[,1:lagsModelMax] <- profilesRecentTable;
}
return(list(matVt=matVt, matWt=matWt, matF=matF, vecG=vecG, arimaPolynomials=arimaPolynomials));
}
#### The function fills in the existing matrices with values of A ####
# This is needed in order to do the estimation and the fit
filler <- function(B,
etsModel, Etype, Ttype, Stype, modelIsTrendy, modelIsSeasonal,
componentsNumberETS, componentsNumberETSNonSeasonal,
componentsNumberETSSeasonal, componentsNumberARIMA,
lags, lagsModel, lagsModelMax,
# The main matrices
matVt, matWt, matF, vecG,
# Persistence and phi
persistenceEstimate, persistenceLevelEstimate, persistenceTrendEstimate,
persistenceSeasonalEstimate, persistenceXregEstimate,
phiEstimate,
# Initials
initialType, initialEstimate,
initialLevelEstimate, initialTrendEstimate, initialSeasonalEstimate,
initialArimaEstimate, initialXregEstimate,
# ARIMA
arimaModel, arEstimate, maEstimate, arOrders, iOrders, maOrders,
arRequired, maRequired, armaParameters,
nonZeroARI, nonZeroMA, arimaPolynomials,
# Explanatory variables
xregModel, xregNumber,
xregParametersMissing, xregParametersIncluded,
xregParametersEstimated, xregParametersPersistence,
# Constant
constantEstimate){
j <- 0;
# Fill in persistence
if(persistenceEstimate){
# Persistence of ETS
if(etsModel){
i <- 1;
# alpha
if(persistenceLevelEstimate){
j[] <- j+1;
vecG[i] <- B[j];
}
# beta
if(modelIsTrendy){
i[] <- 2;
if(persistenceTrendEstimate){
j[] <- j+1;
vecG[i] <- B[j];
}
}
# gamma1, gamma2, ...
if(modelIsSeasonal){
if(any(persistenceSeasonalEstimate)){
vecG[i+which(persistenceSeasonalEstimate)] <- B[j+c(1:sum(persistenceSeasonalEstimate))];
j[] <- j+sum(persistenceSeasonalEstimate);
}
i[] <- componentsNumberETS;
}
}
# Persistence of xreg
if(xregModel && persistenceXregEstimate){
xregPersistenceNumber <- max(xregParametersPersistence);
vecG[j+componentsNumberARIMA+1:length(xregParametersPersistence)] <-
B[j+1:xregPersistenceNumber][xregParametersPersistence];
j[] <- j+xregPersistenceNumber;
}
}
# Damping parameter
if(etsModel && phiEstimate){
j[] <- j+1;
matWt[,2] <- B[j];
matF[1:2,2] <- B[j];
}
# ARMA parameters. This goes before xreg in persistence
if(arimaModel){
# Call the function returning ARI and MA polynomials
# arimaPolynomials <- polynomialiser(B[j+1:sum(c(arOrders*arEstimate,maOrders*maEstimate))], arOrders, iOrders, maOrders,
# arRequired, maRequired, arEstimate, maEstimate, armaParameters, lags);
arimaPolynomials <- lapply(adamPolynomialiser(B[j+1:sum(c(arOrders*arEstimate,maOrders*maEstimate))],
arOrders, iOrders, maOrders,
arEstimate, maEstimate, armaParameters, lags), as.vector);
# Fill in the transition matrix
if(nrow(nonZeroARI)>0){
matF[componentsNumberETS+nonZeroARI[,2],componentsNumberETS+1:(componentsNumberARIMA+constantRequired)] <-
-arimaPolynomials$ariPolynomial[nonZeroARI[,1]];
}
# Fill in the persistence vector
if(nrow(nonZeroARI)>0){
vecG[componentsNumberETS+nonZeroARI[,2]] <- -arimaPolynomials$ariPolynomial[nonZeroARI[,1]];
}
if(nrow(nonZeroMA)>0){
vecG[componentsNumberETS+nonZeroMA[,2]] <- vecG[componentsNumberETS+nonZeroMA[,2]] +
arimaPolynomials$maPolynomial[nonZeroMA[,1]];
}
j[] <- j+sum(c(arOrders*arEstimate,maOrders*maEstimate));
}
# Initials of ETS if something needs to be estimated
if(etsModel && all(initialType!=c("complete","backcasting")) && initialEstimate){
i <- 1;
if(initialLevelEstimate){
j[] <- j+1;
matVt[i,1:lagsModelMax] <- B[j];
}
i[] <- i+1;
if(modelIsTrendy && initialTrendEstimate){
j[] <- j+1;
matVt[i,1:lagsModelMax] <- B[j];
i[] <- i+1;
}
if(modelIsSeasonal && any(initialSeasonalEstimate)){
for(k in 1:componentsNumberETSSeasonal){
if(initialSeasonalEstimate[k]){
matVt[componentsNumberETSNonSeasonal+k, 2:lagsModel[componentsNumberETSNonSeasonal+k]-1] <-
B[j+2:(lagsModel[componentsNumberETSNonSeasonal+k])-1];
matVt[componentsNumberETSNonSeasonal+k, lagsModel[componentsNumberETSNonSeasonal+k]] <-
switch(Stype,
"A"=-sum(B[j+2:(lagsModel[componentsNumberETSNonSeasonal+k])-1]),
"M"=1/prod(B[j+2:(lagsModel[componentsNumberETSNonSeasonal+k])-1]));
j[] <- j+lagsModel[componentsNumberETSNonSeasonal+k]-1;
}
}
}
}
# Initials of ARIMA
if(arimaModel){
if(all(initialType!=c("complete","backcasting")) && initialArimaEstimate){
# matVt[componentsNumberETS+componentsNumberARIMA, 1:initialArimaNumber] <- B[j+1:initialArimaNumber];
# for(i in (componentsNumberARIMA-1):1){
# indeces <-
# (1+lagsModelAll[componentsNumberETS+i+1] -
# lagsModelAll[componentsNumberETS+i]):lagsModelAll[componentsNumberETS+i+1];
# matVt[componentsNumberETS+i,
# 1:lagsModelAll[componentsNumberETS+i]] <-
# (matVt[componentsNumberETS+componentsNumberARIMA, indeces] -
# # We need a sum of states here...
# matVt[componentsNumberETS+i+1, 1:lagsModelAll[componentsNumberETS+i]]);
# }
matVt[componentsNumberETS+nonZeroARI[,2], 1:initialArimaNumber] <-
switch(Etype,
"A"=arimaPolynomials$ariPolynomial[nonZeroARI[,1]] %*% t(B[j+1:initialArimaNumber]),
"M"=exp(arimaPolynomials$ariPolynomial[nonZeroARI[,1]] %*% t(log(B[j+1:initialArimaNumber]))));
# switch(Etype,
# "A"=arimaPolynomials$ariPolynomial[nonZeroARI[,1]] %*% t(B[j+1:initialArimaNumber]) /
# tail(arimaPolynomials$ariPolynomial,1),
# "M"=exp(arimaPolynomials$ariPolynomial[nonZeroARI[,1]] %*% t(log(B[j+1:initialArimaNumber])) /
# tail(arimaPolynomials$ariPolynomial,1)));
j[] <- j+initialArimaNumber;
}
# This is needed in order to propagate initials of ARIMA to all components
else if(any(c(arEstimate,maEstimate))){
# if(nrow(nonZeroARI)>0 && nrow(nonZeroARI)>=nrow(nonZeroMA)){
# if(nrow(nonZeroARI)>0){
matVt[componentsNumberETS+nonZeroARI[,2], 1:initialArimaNumber] <-
switch(Etype,
"A"= arimaPolynomials$ariPolynomial[nonZeroARI[,1]] %*%
t(matVt[componentsNumberETS+componentsNumberARIMA, 1:initialArimaNumber]),
"M"=exp(arimaPolynomials$ariPolynomial[nonZeroARI[,1]] %*%
t(log(matVt[componentsNumberETS+componentsNumberARIMA, 1:initialArimaNumber]))));
# switch(Etype,
# "A"= arimaPolynomials$ariPolynomial[nonZeroARI[,1]] %*%
# t(matVt[componentsNumberETS+componentsNumberARIMA, 1:initialArimaNumber]) /
# tail(arimaPolynomials$ariPolynomial,1),
# "M"=exp(arimaPolynomials$ariPolynomial[nonZeroARI[,1]] %*%
# t(log(matVt[componentsNumberETS+componentsNumberARIMA, 1:initialArimaNumber])) /
# tail(arimaPolynomials$ariPolynomial,1)));
# }
# else{
# matVt[componentsNumberETS+nonZeroMA[,2],
# 1:initialArimaNumber] <-
# switch(Etype,
# "A"=arimaPolynomials$maPolynomial[nonZeroMA[,1]] %*%
# t(matVt[componentsNumberETS+componentsNumberARIMA, 1:initialArimaNumber]) /
# tail(arimaPolynomials$maPolynomial,1),
# "M"=exp(arimaPolynomials$maPolynomial[nonZeroMA[,1]] %*%
# t(log(matVt[componentsNumberETS+componentsNumberARIMA, 1:initialArimaNumber])) /
# tail(arimaPolynomials$maPolynomial,1)));
# }
}
}
# Initials of the xreg
if(xregModel && (initialType!="complete") && initialEstimate && initialXregEstimate){
xregNumberToEstimate <- sum(xregParametersEstimated);
matVt[componentsNumberETS+componentsNumberARIMA+which(xregParametersEstimated==1),
1:lagsModelMax] <- B[j+1:xregNumberToEstimate];
j[] <- j+xregNumberToEstimate;
# Normalise initials
for(i in which(xregParametersMissing!=0)){
matVt[componentsNumberETS+componentsNumberARIMA+i,
1:lagsModelMax] <- -sum(matVt[componentsNumberETS+componentsNumberARIMA+
which(xregParametersIncluded==xregParametersMissing[i]),
1:lagsModelMax]);
}
}
# Constant
if(constantEstimate){
matVt[componentsNumberETS+componentsNumberARIMA+xregNumber+1,] <- B[j+1];
}
return(list(matVt=matVt, matWt=matWt, matF=matF, vecG=vecG, arimaPolynomials=arimaPolynomials));
}
#### The function initialises the vector B for ETS ####
initialiser <- function(etsModel, Etype, Ttype, Stype, modelIsTrendy, modelIsSeasonal,
componentsNumberETSNonSeasonal, componentsNumberETSSeasonal, componentsNumberETS,
lags, lagsModel, lagsModelSeasonal, lagsModelARIMA, lagsModelMax,
matVt,
# persistence values
persistenceEstimate, persistenceLevelEstimate, persistenceTrendEstimate,
persistenceSeasonalEstimate, persistenceXregEstimate,
# initials
phiEstimate, initialType, initialEstimate,
initialLevelEstimate, initialTrendEstimate, initialSeasonalEstimate,
initialArimaEstimate, initialXregEstimate,
# ARIMA elements
arimaModel, arRequired, maRequired, arEstimate, maEstimate, arOrders, maOrders,
componentsNumberARIMA, componentsNamesARIMA, initialArimaNumber,
# Explanatory variables
xregModel, xregNumber,
xregParametersEstimated, xregParametersPersistence,
# Constant and other stuff
constantEstimate, constantName, otherParameterEstimate){
# The vector of logicals for persistence elements
persistenceEstimateVector <- c(persistenceLevelEstimate,modelIsTrendy&persistenceTrendEstimate,
modelIsSeasonal&persistenceSeasonalEstimate);
# The order:
# Persistence of states and for xreg, phi, AR and MA parameters, initials, initialsARIMA, initials for xreg
B <- Bl <- Bu <- vector("numeric",
# Values of the persistence vector + phi
etsModel*(persistenceLevelEstimate + modelIsTrendy*persistenceTrendEstimate +
modelIsSeasonal*sum(persistenceSeasonalEstimate) + phiEstimate) +
xregModel*persistenceXregEstimate*max(xregParametersPersistence) +
# AR and MA values
arimaModel*(arEstimate*sum(arOrders)+maEstimate*sum(maOrders)) +
# initials of ETS
etsModel*all(initialType!=c("complete","backcasting"))*
(initialLevelEstimate +
(modelIsTrendy*initialTrendEstimate) +
(modelIsSeasonal*sum(initialSeasonalEstimate*(lagsModelSeasonal-1)))) +
# initials of ARIMA
all(initialType!=c("complete","backcasting"))*arimaModel*initialArimaNumber*initialArimaEstimate +
# initials of xreg
(initialType!="complete")*xregModel*initialXregEstimate*sum(xregParametersEstimated) +
constantEstimate + otherParameterEstimate);
j <- 0;
if(etsModel){
# Fill in persistence
if(persistenceEstimate && any(persistenceEstimateVector)){
if(any(c(Etype,Ttype,Stype)=="M")){
# A special type of model which is not safe: AAM, MAA, MAM
if((Etype=="A" && Ttype=="A" && Stype=="M") || (Etype=="A" && Ttype=="M" && Stype=="A") ||
(any(initialType==c("complete","backcasting")) &&
((Etype=="M" && Ttype=="A" && Stype=="A") || (Etype=="M" && Ttype=="A" && Stype=="M")))){
B[1:sum(persistenceEstimateVector)] <-
c(0.01,0,rep(0,componentsNumberETSSeasonal))[which(persistenceEstimateVector)];
}
# MMA is the worst. Set everything to zero and see if anything can be done...
else if((Etype=="M" && Ttype=="M" && Stype=="A")){
B[1:sum(persistenceEstimateVector)] <-
c(0,0,rep(0,componentsNumberETSSeasonal))[which(persistenceEstimateVector)];
}
else if(Etype=="M" && Ttype=="A"){
if(any(initialType==c("complete","backcasting"))){
B[1:sum(persistenceEstimateVector)] <-
c(0.1,0,rep(0.01,componentsNumberETSSeasonal))[which(persistenceEstimateVector)];
}
else{
B[1:sum(persistenceEstimateVector)] <-
c(0.2,0.01,rep(0.01,componentsNumberETSSeasonal))[which(persistenceEstimateVector)];
}
}
else if(Etype=="M" && Ttype=="M"){
B[1:sum(persistenceEstimateVector)] <-
c(0.1,0.05,rep(0.01,componentsNumberETSSeasonal))[which(persistenceEstimateVector)];
}
else{
B[1:sum(persistenceEstimateVector)] <-
c(0.1,0.05,rep(0.05,componentsNumberETSSeasonal))[which(persistenceEstimateVector)];
}
}
else{
B[1:sum(persistenceEstimateVector)] <-
c(0.1,0.05,rep(0.11,componentsNumberETSSeasonal))[which(persistenceEstimateVector)];
}
if(bounds=="usual"){
Bl[1:sum(persistenceEstimateVector)] <- rep(0, sum(persistenceEstimateVector));
Bu[1:sum(persistenceEstimateVector)] <- rep(1, sum(persistenceEstimateVector));
}
else{
Bl[1:sum(persistenceEstimateVector)] <- rep(-5, sum(persistenceEstimateVector));
Bu[1:sum(persistenceEstimateVector)] <- rep(5, sum(persistenceEstimateVector));
}
# Names for B
if(persistenceLevelEstimate){
j[] <- j+1
names(B)[j] <- "alpha";
}
if(modelIsTrendy && persistenceTrendEstimate){
j[] <- j+1
names(B)[j] <- "beta";
}
if(modelIsSeasonal && any(persistenceSeasonalEstimate)){
if(componentsNumberETSSeasonal>1){
names(B)[j+c(1:sum(persistenceSeasonalEstimate))] <-
paste0("gamma",c(1:componentsNumberETSSeasonal));
}
else{
names(B)[j+1] <- "gamma";
}
j[] <- j+sum(persistenceSeasonalEstimate);
}
}
}
# Persistence if xreg is provided
if(xregModel && persistenceXregEstimate){
xregPersistenceNumber <- max(xregParametersPersistence);
B[j+1:xregPersistenceNumber] <- rep(switch(Etype,"A"=0.01,"M"=0),xregPersistenceNumber);
Bl[j+1:xregPersistenceNumber] <- rep(-5, xregPersistenceNumber);
Bu[j+1:xregPersistenceNumber] <- rep(5, xregPersistenceNumber);
names(B)[j+1:xregPersistenceNumber] <- paste0("delta",c(1:xregPersistenceNumber));
j[] <- j+xregPersistenceNumber;
}
# Damping parameter
if(etsModel && phiEstimate){
j[] <- j+1;
B[j] <- 0.95;
names(B)[j] <- "phi";
Bl[j] <- 0;
Bu[j] <- 1;
}
# ARIMA parameters (AR / MA)
if(arimaModel){
# These are filled in lags-wise
if(any(c(arEstimate,maEstimate))){
acfValues <- rep(-0.1, maOrders %*% lags);
pacfValues <- rep(0.1, arOrders %*% lags);
# If this is ETS + ARIMA model or no differences model, then don't bother with initials
# The latter does not make sense because of non-stationarity in ACF / PACF
# Otherwise use ACF / PACF values as starting parameters for ARIMA
if(!(etsModel || all(iOrders==0))){
yDifferenced <- yInSample;
# If the model has differences, take them
if(any(iOrders>0)){
for(i in 1:length(iOrders)){
if(iOrders[i]>0){
yDifferenced <- diff(yDifferenced,lag=lags[i],differences=iOrders[i]);
}
}
}
# Do ACF/PACF initialisation only for non-seasonal models
if(all(lags<=1)){
if(maRequired && maEstimate){
# If the sample is smaller than lags, it will be substituted by default values
acfValues[1:min(maOrders %*% lags, length(yDifferenced)-1)] <-
acf(yDifferenced,lag.max=max(1,maOrders %*% lags),plot=FALSE)$acf[-1];
}
if(arRequired && arEstimate){
# If the sample is smaller than lags, it will be substituted by default values
pacfValues[1:min(arOrders %*% lags, length(yDifferenced)-1)] <-
pacf(yDifferenced,lag.max=max(1,arOrders %*% lags),plot=FALSE)$acf;
}
}
}
for(i in 1:length(lags)){
if(arRequired && arEstimate && arOrders[i]>0){
if(all(!is.nan(pacfValues[c(1:arOrders[i])*lags[i]]))){
B[j+c(1:arOrders[i])] <- pacfValues[c(1:arOrders[i])*lags[i]];
}
else{
B[j+c(1:arOrders[i])] <- 0.1;
}
if(sum(B[j+c(1:arOrders[i])])>1){
B[j+c(1:arOrders[i])] <- B[j+c(1:arOrders[i])] / sum(B[j+c(1:arOrders[i])]) - 0.01;
}
# B[j+c(1:arOrders[i])] <- rep(0.1,arOrders[i]);
Bl[j+c(1:arOrders[i])] <- -5;
Bu[j+c(1:arOrders[i])] <- 5;
names(B)[j+1:arOrders[i]] <- paste0("phi",1:arOrders[i],"[",lags[i],"]");
j[] <- j + arOrders[i];
}
if(maRequired && maEstimate && maOrders[i]>0){
if(all(!is.nan(acfValues[c(1:maOrders[i])*lags[i]]))){
B[j+c(1:maOrders[i])] <- acfValues[c(1:maOrders[i])*lags[i]];
}
else{
B[j+c(1:maOrders[i])] <- 0.1;
}
if(sum(B[j+c(1:maOrders[i])])>1){
B[j+c(1:maOrders[i])] <- B[j+c(1:maOrders[i])] / sum(B[j+c(1:maOrders[i])]) - 0.01;
}
# B[j+c(1:maOrders[i])] <- rep(-0.1,maOrders[i]);
Bl[j+c(1:maOrders[i])] <- -5;
Bu[j+c(1:maOrders[i])] <- 5;
names(B)[j+1:maOrders[i]] <- paste0("theta",1:maOrders[i],"[",lags[i],"]");
j[] <- j + maOrders[i];
}
}
}
arimaPolynomials <- lapply(adamPolynomialiser(B[j+1:sum(c(arOrders*arEstimate,maOrders*maEstimate))],
arOrders, iOrders, maOrders,
arEstimate, maEstimate, armaParameters, lags), as.vector)
}
# Initials
if(etsModel && all(initialType!=c("complete","backcasting")) && initialEstimate){
if(initialLevelEstimate){
j[] <- j+1;
B[j] <- matVt[1,1];
names(B)[j] <- "level";
if(Etype=="A"){
Bl[j] <- -Inf;
Bu[j] <- Inf;
}
else{
Bl[j] <- 0;
Bu[j] <- Inf;
}
}
if(modelIsTrendy && initialTrendEstimate){
j[] <- j+1;
B[j] <- matVt[2,1];
names(B)[j] <- "trend";
if(Ttype=="A"){
Bl[j] <- -Inf;
Bu[j] <- Inf;
}
else{
Bl[j] <- 0;
# 2 is already too much for the multiplicative model
Bu[j] <- 2;
}
}
if(modelIsSeasonal && any(initialSeasonalEstimate)){
if(componentsNumberETSSeasonal>1){
for(k in 1:componentsNumberETSSeasonal){
if(initialSeasonalEstimate[k]){
# -1 is needed in order to remove the redundant seasonal element (normalisation)
B[j+2:lagsModel[componentsNumberETSNonSeasonal+k]-1] <-
matVt[componentsNumberETSNonSeasonal+k, 2:lagsModel[componentsNumberETSNonSeasonal+k]-1];
names(B)[j+2:(lagsModel[componentsNumberETSNonSeasonal+k])-1] <-
paste0("seasonal",k,"_",2:lagsModel[componentsNumberETSNonSeasonal+k]-1);
if(Stype=="A"){
Bl[j+2:lagsModel[componentsNumberETSNonSeasonal+k]-1] <- -Inf;
Bu[j+2:lagsModel[componentsNumberETSNonSeasonal+k]-1] <- Inf;
}
else{
Bl[j+2:lagsModel[componentsNumberETSNonSeasonal+k]-1] <- 0;
Bu[j+2:lagsModel[componentsNumberETSNonSeasonal+k]-1] <- Inf;
}
j[] <- j+(lagsModelSeasonal[k]-1);
}
}
}
else{
# -1 is needed in order to remove the redundant seasonal element (normalisation)
B[j+2:(lagsModel[componentsNumberETS])-1] <- matVt[componentsNumberETS,2:lagsModel[componentsNumberETS]-1];
names(B)[j+2:(lagsModel[componentsNumberETS])-1] <- paste0("seasonal_",2:lagsModel[componentsNumberETS]-1);
if(Stype=="A"){
Bl[j+2:(lagsModel[componentsNumberETS])-1] <- -Inf;
Bu[j+2:(lagsModel[componentsNumberETS])-1] <- Inf;
}
else{
Bl[j+2:(lagsModel[componentsNumberETS])-1] <- 0;
Bu[j+2:(lagsModel[componentsNumberETS])-1] <- Inf;
}
j[] <- j+(lagsModel[componentsNumberETS]-1);
}
}
}
# ARIMA initials
if(arimaModel && all(initialType!=c("complete","backcasting")) && initialArimaEstimate){
B[j+1:initialArimaNumber] <- head(matVt[componentsNumberETS+componentsNumberARIMA,1:lagsModelMax],initialArimaNumber);
names(B)[j+1:initialArimaNumber] <- paste0("ARIMAState",1:initialArimaNumber);
# Fix initial state if the polynomial is not zero
if(tail(arimaPolynomials$ariPolynomial,1)!=0){
B[j+1:initialArimaNumber] <- B[j+1:initialArimaNumber] / tail(arimaPolynomials$ariPolynomial,1);
}
if(Etype=="A"){
Bl[j+1:initialArimaNumber] <- -Inf;
Bu[j+1:initialArimaNumber] <- Inf;
}
else{
# Make sure that ARIMA states are positive to avoid errors
B[j+1:initialArimaNumber] <- abs(B[j+1:initialArimaNumber]);
Bl[j+1:initialArimaNumber] <- 0;
Bu[j+1:initialArimaNumber] <- Inf;
}
j[] <- j+initialArimaNumber;
}
# Initials of the xreg
if(initialType!="complete" && initialXregEstimate){
xregNumberToEstimate <- sum(xregParametersEstimated);
B[j+1:xregNumberToEstimate] <- matVt[componentsNumberETS+componentsNumberARIMA+
which(xregParametersEstimated==1),1];
names(B)[j+1:xregNumberToEstimate] <- rownames(matVt)[componentsNumberETS+componentsNumberARIMA+
which(xregParametersEstimated==1)];
if(Etype=="A"){
Bl[j+1:xregNumberToEstimate] <- -Inf;
Bu[j+1:xregNumberToEstimate] <- Inf;
}
else{
Bl[j+1:xregNumberToEstimate] <- -Inf;
Bu[j+1:xregNumberToEstimate] <- Inf;
}
j[] <- j+xregNumberToEstimate;
}
if(constantEstimate){
j[] <- j+1;
B[j] <- matVt[componentsNumberETS+componentsNumberARIMA+xregNumber+1,1];
names(B)[j] <- constantName;
if(etsModel || sum(iOrders)!=0){
if(Etype=="A"){
Bu[j] <- quantile(diff(yInSample[otLogical]),0.6);
Bl[j] <- -Bu[j];
}
else{
Bu[j] <- exp(quantile(diff(log(yInSample[otLogical])),0.6));
Bl[j] <- exp(quantile(diff(log(yInSample[otLogical])),0.4));
}
# Failsafe for weird cases, when upper bound is the same or lower than the lower one
if(Bu[j]<=Bl[j]){
Bu[j] <- Inf;
Bl[j] <- switch(Etype,"A"=-Inf,"M"=0);
}
# Failsafe for cases, when the B is outside of bounds
if(B[j]<=Bl[j]){
Bl[j] <- switch(Etype,"A"=-Inf,"M"=0);
}
if(B[j]>=Bu[j]){
Bu[j] <- Inf;
}
}
else{
# if(Etype=="A"){
# B[j]*1.01 is needed to make sure that the bounds cover the initial value
Bu[j] <- max(abs(yInSample[otLogical]),abs(B[j])*1.01);
Bl[j] <- -Bu[j];
# }
# else{
# Bu[j] <- 1.5;
# Bl[j] <- 0.1;
# }
# If this is just a constant
}
}
# Add lambda if it is needed
if(otherParameterEstimate){
j[] <- j+1;
B[j] <- other;
names(B)[j] <- "other";
Bl[j] <- 1e-10;
Bu[j] <- Inf;
}
return(list(B=B,Bl=Bl,Bu=Bu));
}
##### Function returns scale parameter for the provided parameters #####
scaler <- function(distribution, Etype, errors, yFitted, obsInSample, other){
# as.complex() is needed in order to make the optimiser work in exotic cases
return(switch(distribution,
"dnorm"=sqrt(sum(errors^2)/obsInSample),
"dlaplace"=sum(abs(errors))/obsInSample,
"ds"=sum(sqrt(abs(errors))) / (obsInSample*2),
"dgnorm"=(other*sum(abs(errors)^other)/obsInSample)^{1/other},
# "dlogis"=sqrt(sum(errors^2)/obsInSample * 3 / pi^2),
# "dt"=sqrt(sum(errors^2)/obsInSample),
"dalaplace"=sum(errors*(other-(errors<=0)*1))/obsInSample,
# This condition guarantees that E(1+e_t)=1
# abs is needed for cases, when we get imaginary values - a failsafe
"dlnorm"=sqrt(2*abs(switch(Etype,
"A"=1-sqrt(abs(1-sum(log(abs(1+errors/yFitted))^2)/obsInSample)),
"M"=1-sqrt(abs(1-sum(log(1+errors)^2)/obsInSample))))),
# "A"=Re(sqrt(sum(log(as.complex(1+errors/yFitted))^2)/obsInSample)),
# "M"=sqrt(sum(log(1+errors)^2)/obsInSample)),
"dllaplace"=switch(Etype,
"A"=Re(sum(abs(log(as.complex(1+errors/yFitted))))/obsInSample),
"M"=sum(abs(log(1+errors))/obsInSample)),
"dls"=switch(Etype,
"A"=Re(sum(sqrt(abs(log(as.complex(1+errors/yFitted))))/obsInSample)),
"M"=sum(sqrt(abs(log(1+errors)))/obsInSample)),
"dlgnorm"=switch(Etype,
"A"=Re((other*sum(abs(log(as.complex(1+errors/yFitted)))^other)/obsInSample)^{1/other}),
"M"=(other*sum(abs(log(as.complex(1+errors)))^other)/obsInSample)^{1/other}),
"dinvgauss"=switch(Etype,
"A"=sum((errors/yFitted)^2/(1+errors/yFitted))/obsInSample,
"M"=sum((errors)^2/(1+errors))/obsInSample),
"dgamma"=switch(Etype,
"A"=sum((errors/yFitted)^2)/obsInSample,
"M"=sum(errors^2)/obsInSample)
# "M"=mean((errors)^2/(1+errors))),
));
}
#### The function inverts the measurement matrix, setting infinte values to zero
# This is needed for the stability check for xreg models with regressors="adapt"
measurementInverter <- function(measurement){
measurement[] <- 1/measurement;
measurement[is.infinite(measurement)] <- 0;
return(measurement);
}
##### Cost Function for ETS #####
CF <- function(B,
etsModel, Etype, Ttype, Stype, modelIsTrendy, modelIsSeasonal, yInSample,
ot, otLogical, occurrenceModel, obsInSample,
componentsNumberETS, componentsNumberETSSeasonal, componentsNumberETSNonSeasonal,
componentsNumberARIMA,
lags, lagsModel, lagsModelAll, lagsModelMax,
indexLookupTable, profilesRecentTable,
# The main matrices
matVt, matWt, matF, vecG,
# Persistence and phi
persistenceEstimate, persistenceLevelEstimate, persistenceTrendEstimate,
persistenceSeasonalEstimate, persistenceXregEstimate, phiEstimate,
# Initials
initialType, initialEstimate,
initialLevelEstimate, initialTrendEstimate, initialSeasonalEstimate,
initialArimaEstimate, initialXregEstimate,
# ARIMA
arimaModel, nonZeroARI, nonZeroMA, arEstimate, maEstimate, arimaPolynomials,
arOrders, iOrders, maOrders, arRequired, maRequired, armaParameters,
# xreg
xregModel, xregNumber,
xregParametersMissing, xregParametersIncluded,
xregParametersEstimated, xregParametersPersistence,
# Constant
constantRequired, constantEstimate,
# Other stuff
bounds, loss, lossFunction, distribution, horizon, multisteps,
denominator=NULL, yDenominator=NULL,
other, otherParameterEstimate, lambda,
arPolynomialMatrix, maPolynomialMatrix){
# Fill in the matrices
adamElements <- filler(B,
etsModel, Etype, Ttype, Stype, modelIsTrendy, modelIsSeasonal,
componentsNumberETS, componentsNumberETSNonSeasonal,
componentsNumberETSSeasonal, componentsNumberARIMA,
lags, lagsModel, lagsModelMax,
matVt, matWt, matF, vecG,
persistenceEstimate, persistenceLevelEstimate, persistenceTrendEstimate,
persistenceSeasonalEstimate, persistenceXregEstimate,
phiEstimate,
initialType, initialEstimate,
initialLevelEstimate, initialTrendEstimate, initialSeasonalEstimate,
initialArimaEstimate, initialXregEstimate,
arimaModel, arEstimate, maEstimate, arOrders, iOrders, maOrders,
arRequired, maRequired, armaParameters,
nonZeroARI, nonZeroMA, arimaPolynomials,
xregModel, xregNumber,
xregParametersMissing, xregParametersIncluded,
xregParametersEstimated, xregParametersPersistence, constantEstimate);
# If we estimate parameters of distribution, take it from the B vector
if(otherParameterEstimate){
# Take absolute value, just to be on safe side. We don't need negatives anyway.
other[] <- abs(B[length(B)]);
# Beta in GN is restricted by 0.25 if it is optimised.
if(any(distribution==c("dgnorm","dlgnorm")) && other<0.25){
return(1E+10/other);
}
}
# Check the bounds, classical restrictions
#### The usual bounds ####
if(bounds=="usual"){
# Stationarity and invertibility conditions for ARIMA
if(arimaModel && any(c(arEstimate,maEstimate))){
# Calculate the polynomial roots for AR
if(arEstimate && sum(-(adamElements$arimaPolynomials$arPolynomial[-1]))>=1){
arPolynomialMatrix[,1] <- -adamElements$arimaPolynomials$arPolynomial[-1];
arPolyroots <- abs(eigen(arPolynomialMatrix, symmetric=FALSE, only.values=TRUE)$values);
if(any(arPolyroots>1)){
return(1E+100*max(arPolyroots));
}
}
# Calculate the polynomial roots of MA
if(maEstimate && sum(adamElements$arimaPolynomials$maPolynomial[-1])>=1){
maPolynomialMatrix[,1] <- adamElements$arimaPolynomials$maPolynomial[-1];
maPolyroots <- abs(eigen(maPolynomialMatrix, symmetric=FALSE, only.values=TRUE)$values);
if(any(maPolyroots>1)){
return(1E+100*max(abs(maPolyroots)));
}
}
}
# Smoothing parameters & phi restrictions in case of ETS
if(etsModel){
if(any(adamElements$vecG[1:componentsNumberETS]>1) || any(adamElements$vecG[1:componentsNumberETS]<0)){
return(1E+300);
}
if(modelIsTrendy){
if((adamElements$vecG[2]>adamElements$vecG[1])){
return(1E+300);
}
if(modelIsSeasonal && any(adamElements$vecG[componentsNumberETSNonSeasonal+c(1:componentsNumberETSSeasonal)]>
(1-adamElements$vecG[1]))){
return(1E+300);
}
}
else{
if(modelIsSeasonal && any(adamElements$vecG[componentsNumberETSNonSeasonal+c(1:componentsNumberETSSeasonal)]>
(1-adamElements$vecG[1]))){
return(1E+300);
}
}
# This is the restriction on the damping parameter
if(phiEstimate && (adamElements$matF[2,2]>1 || adamElements$matF[2,2]<0)){
return(1E+300);
}
}
# Smoothing parameters for the explanatory variables (0, 1) region
if(xregModel && regressors=="adapt"){
if(any(adamElements$vecG[componentsNumberETS+componentsNumberARIMA+1:xregNumber]>1) ||
any(adamElements$vecG[componentsNumberETS+componentsNumberARIMA+1:xregNumber]<0)){
return(1E+100*max(abs(adamElements$vecG[componentsNumberETS+componentsNumberARIMA+1:xregNumber]-0.5)));
}
}
}
#### The admissible bounds ####
else if(bounds=="admissible"){
# Stationarity condition of ARIMA
if(arimaModel){
# Calculate the polynomial roots for AR
if(arEstimate && (sum(-(adamElements$arimaPolynomials$arPolynomial[-1]))>=1 |
sum(-(adamElements$arimaPolynomials$arPolynomial[-1]))<0)){
arPolynomialMatrix[,1] <- -adamElements$arimaPolynomials$arPolynomial[-1];
eigenValues <- abs(eigen(arPolynomialMatrix, symmetric=FALSE, only.values=TRUE)$values);
if(any(eigenValues>1)){
return(1E+100*max(eigenValues));
}
}
}
# Stability / invertibility condition for ETS / ARIMA.
if(etsModel || arimaModel){
if(xregModel){
if(regressors=="adapt"){
# We check the condition on average
eigenValues <- abs(eigen((adamElements$matF -
diag(as.vector(adamElements$vecG)) %*%
t(measurementInverter(adamElements$matWt[1:obsInSample,,drop=FALSE])) %*%
adamElements$matWt[1:obsInSample,,drop=FALSE] / obsInSample),
symmetric=FALSE, only.values=TRUE)$values);
}
else{
# We drop the X parts from matrices
indices <- c(1:(componentsNumberETS+componentsNumberARIMA))
eigenValues <- abs(eigen(adamElements$matF[indices,indices,drop=FALSE] -
adamElements$vecG[indices,,drop=FALSE] %*%
adamElements$matWt[obsInSample,indices,drop=FALSE],
symmetric=FALSE, only.values=TRUE)$values);
}
}
else{
if(etsModel || (arimaModel && maEstimate && (sum(adamElements$arimaPolynomials$maPolynomial[-1])>=1 |
sum(adamElements$arimaPolynomials$maPolynomial[-1])<0))){
eigenValues <- abs(eigen(adamElements$matF -
adamElements$vecG %*% adamElements$matWt[obsInSample,,drop=FALSE],
symmetric=FALSE, only.values=TRUE)$values);
}
else{
eigenValues <- 0;
}
}
if(any(eigenValues>1+1E-50)){
return(1E+100*max(eigenValues));
}
}
}
# Write down the initials in the recent profile
profilesRecentTable[] <- adamElements$matVt[,1:lagsModelMax];
# print(round(B,3))
# print(adamElements$vecG)
# print(profilesRecentTable)
#### Fitter and the losses calculation ####
adamFitted <- adamFitterWrap(adamElements$matVt, adamElements$matWt, adamElements$matF, adamElements$vecG,
lagsModelAll, indexLookupTable, profilesRecentTable,
Etype, Ttype, Stype, componentsNumberETS, componentsNumberETSSeasonal,
componentsNumberARIMA, xregNumber, constantRequired,
yInSample, ot, any(initialType==c("complete","backcasting")),
nIterations);
if(!multisteps){
if(loss=="likelihood"){
# Scale for different functions
scale <- scaler(distribution, Etype, adamFitted$errors[otLogical],
adamFitted$yFitted[otLogical], obsInSample, other);
# Calculate the likelihood
## as.complex() is needed for failsafe in case of exotic models
CFValue <- -sum(switch(distribution,
"dnorm"=switch(Etype,
"A"=dnorm(x=yInSample[otLogical], mean=adamFitted$yFitted[otLogical],
sd=scale, log=TRUE),
"M"=dnorm(x=yInSample[otLogical], mean=adamFitted$yFitted[otLogical],
sd=scale*adamFitted$yFitted[otLogical], log=TRUE)),
"dlaplace"=switch(Etype,
"A"=dlaplace(q=yInSample[otLogical], mu=adamFitted$yFitted[otLogical],
scale=scale, log=TRUE),
"M"=dlaplace(q=yInSample[otLogical], mu=adamFitted$yFitted[otLogical],
scale=scale*adamFitted$yFitted[otLogical], log=TRUE)),
"ds"=switch(Etype,
"A"=ds(q=yInSample[otLogical],mu=adamFitted$yFitted[otLogical],
scale=scale, log=TRUE),
"M"=ds(q=yInSample[otLogical],mu=adamFitted$yFitted[otLogical],
scale=scale*sqrt(adamFitted$yFitted[otLogical]), log=TRUE)),
"dgnorm"=switch(Etype,
"A"=dgnorm(q=yInSample[otLogical],mu=adamFitted$yFitted[otLogical],
scale=scale, shape=other, log=TRUE),
# suppressWarnings is needed, because the check is done for scalar alpha
"M"=suppressWarnings(dgnorm(q=yInSample[otLogical],
mu=adamFitted$yFitted[otLogical],
scale=scale*(adamFitted$yFitted[otLogical]),
shape=other, log=TRUE))),
# "dlogis"=switch(Etype,
# "A"=dlogis(x=yInSample[otLogical],
# location=adamFitted$yFitted[otLogical],
# scale=scale, log=TRUE),
# "M"=dlogis(x=yInSample[otLogical],
# location=adamFitted$yFitted[otLogical],
# scale=scale*adamFitted$yFitted[otLogical], log=TRUE)),
# "dt"=switch(Etype,
# "A"=dt(adamFitted$errors[otLogical], df=abs(other), log=TRUE),
# "M"=dt(adamFitted$errors[otLogical]*adamFitted$yFitted[otLogical],
# df=abs(other), log=TRUE)),
"dalaplace"=switch(Etype,
"A"=dalaplace(q=yInSample[otLogical],
mu=adamFitted$yFitted[otLogical],
scale=scale, alpha=other, log=TRUE),
"M"=dalaplace(q=yInSample[otLogical],
mu=adamFitted$yFitted[otLogical],
scale=scale*adamFitted$yFitted[otLogical],
alpha=other, log=TRUE)),
"dlnorm"=dlnorm(x=yInSample[otLogical],
meanlog=Re(log(as.complex(adamFitted$yFitted[otLogical])))-scale^2/2,
sdlog=scale, log=TRUE),
"dllaplace"=dlaplace(q=log(yInSample[otLogical]),
mu=Re(log(as.complex(adamFitted$yFitted[otLogical]))),
scale=scale, log=TRUE) -log(yInSample[otLogical]),
"dls"=ds(q=log(yInSample[otLogical]),
mu=Re(log(as.complex(adamFitted$yFitted[otLogical]))),
scale=scale, log=TRUE) -log(yInSample[otLogical]),
"dlgnorm"=dgnorm(q=log(yInSample[otLogical]),
mu=Re(log(as.complex(adamFitted$yFitted[otLogical]))),
scale=scale, shape=other, log=TRUE) -log(yInSample[otLogical]),
# abs() is needed for rare cases, when negative values are produced for E="A" models
"dinvgauss"=dinvgauss(x=yInSample[otLogical], mean=abs(adamFitted$yFitted[otLogical]),
dispersion=abs(scale/adamFitted$yFitted[otLogical]), log=TRUE),
# abs() is a failsafe mechanism for weird cases of negative values in mixed models
"dgamma"=dgamma(x=yInSample[otLogical], shape=1/scale,
scale=scale*abs(adamFitted$yFitted[otLogical]), log=TRUE)
));
# Differential entropy for the logLik of occurrence model
if(occurrenceModel || any(!otLogical)){
CFValueEntropy <- switch(distribution,
"dnorm" = obsZero*(log(sqrt(2*pi)*scale)+0.5),
"dlnorm" = obsZero*(log(sqrt(2*pi)*scale)+0.5)-scale^2/2,
"dlogis" = obsZero*2,
"dlaplace" =,
"dllaplace" =,
"dalaplace" = obsZero*(1 + log(2*scale)),
"ds" =,
"dls" = obsZero*(2 + 2*log(2*scale)),
"dgnorm" =,
"dlgnorm" = obsZero*(1/other-log(other/(2*scale*gamma(1/other)))),
"dt" = obsZero*((scale+1)/2 *
(digamma((scale+1)/2)-digamma(scale/2)) +
log(sqrt(scale) * beta(scale/2,0.5))),
# "dinvgauss" = obsZero*(0.5*(log(pi/2)+1+suppressWarnings(log(scale)))));
# "dinvgauss" =0);
"dinvgauss" = 0.5*(obsZero*(log(pi/2)+1+suppressWarnings(log(scale)))-
sum(log(adamFitted$yFitted[!otLogical]))),
"dgamma" = obsZero*(1/scale + log(gamma(1/scale)) +
(1-1/scale)*digamma(1/scale)) +
sum(log(scale*adamFitted$yFitted[!otLogical]))
);
# If the entropy is NA then something is wrong. It shouldn't be!
if(is.na(CFValueEntropy)){
CFValueEntropy[] <- Inf;
}
# If it is negative (it shouldn't be), substitute with zero.
# Otherwise occurrence screws the demand sizes model
if(CFValueEntropy<0){
CFValueEntropy[] <- 0;
}
CFValue <- CFValue + CFValueEntropy;
}
}
else if(loss=="MSE"){
CFValue <- sum(adamFitted$errors^2)/obsInSample;
}
else if(loss=="MAE"){
CFValue <- sum(abs(adamFitted$errors))/obsInSample;
}
else if(loss=="HAM"){
CFValue <- sum(sqrt(abs(adamFitted$errors)))/obsInSample;
}
else if(any(loss==c("LASSO","RIDGE"))){
### All of this is needed in order to get rid of initial level, trend, seasonal and xreg parameters
# Define, how many elements to skip (we don't normalise smoothing parameters)
persistenceToSkip <- componentsNumberETS + persistenceXregEstimate*xregNumber +
phiEstimate + sum(arOrders) + sum(maOrders);
# Shrink phi to 1
if(phiEstimate){
B[componentsNumberETS + persistenceXregEstimate*xregNumber + 1] <-
1-B[componentsNumberETS + persistenceXregEstimate*xregNumber + 1];
}
j <- componentsNumberETS + persistenceXregEstimate*xregNumber + phiEstimate;
# No good understanding how to shrink ARMA. Do these just because:
# Shrink AR parameters to 1 and
# Shrink MA parameters to 0
if(arimaModel && (sum(maOrders)>0 || sum(arOrders)>0)){
for(i in 1:length(lags)){
B[j+c(1:arOrders[i])] <- 1-B[j+c(1:arOrders[i])];
B[j+arOrders[i]+c(1:maOrders[i])] <- B[j+arOrders[i]+c(1:maOrders[i])];
j[] <- j+arOrders[i]+maOrders[i];
}
}
# Don't do anything with the initial states of ETS and ARIMA. Just drop them (don't shrink)
if(any(initialType==c("optimal","backcasting"))){
# If there are explanatory variables, shrink their parameters
if(xregNumber>0){
# Normalise parameters of xreg if they are additive. Otherwise leave - they will be small and close to zero
B <- switch(Etype,
"A"=c(B[1:persistenceToSkip],tail(B,xregNumber) / denominator),
"M"=c(B[1:persistenceToSkip],tail(B,xregNumber)));
}
else{
B <- B[1:persistenceToSkip];
}
}
CFValue <- (switch(Etype,
"A"=(1-lambda)* sqrt(sum((adamFitted$errors/yDenominator)^2)/obsInSample),
"M"=(1-lambda)* sqrt(sum(log(1+adamFitted$errors)^2)/obsInSample)) +
switch(loss,
"LASSO"=lambda * sum(abs(B)),
"RIDGE"=lambda * sqrt(sum(B^2))));
}
else if(loss=="custom"){
CFValue <- lossFunction(actual=yInSample,fitted=adamFitted$yFitted,B=B);
}
}
else{
# Call for the Rcpp function to produce a matrix of multistep errors
adamErrors <- adamErrorerWrap(adamFitted$matVt, adamElements$matWt, adamElements$matF,
lagsModelAll, indexLookupTable, profilesRecentTable,
Etype, Ttype, Stype,
componentsNumberETS, componentsNumberETSSeasonal,
componentsNumberARIMA, xregNumber, constantRequired, h,
yInSample, ot);
# Not done yet: "aMSEh","aTMSE","aGTMSE","aMSCE","aGPL"
CFValue <- switch(loss,
"MSEh"=sum(adamErrors[,h]^2)/(obsInSample-h),
"TMSE"=sum(colSums(adamErrors^2)/(obsInSample-h)),
"GTMSE"=sum(log(colSums(adamErrors^2)/(obsInSample-h))),
"MSCE"=sum(rowSums(adamErrors)^2)/(obsInSample-h),
"MAEh"=sum(abs(adamErrors[,h]))/(obsInSample-h),
"TMAE"=sum(colSums(abs(adamErrors))/(obsInSample-h)),
"GTMAE"=sum(log(colSums(abs(adamErrors))/(obsInSample-h))),
"MACE"=sum(abs(rowSums(adamErrors)))/(obsInSample-h),
"HAMh"=sum(sqrt(abs(adamErrors[,h])))/(obsInSample-h),
"THAM"=sum(colSums(sqrt(abs(adamErrors)))/(obsInSample-h)),
"GTHAM"=sum(log(colSums(sqrt(abs(adamErrors)))/(obsInSample-h))),
"CHAM"=sum(sqrt(abs(rowSums(adamErrors))))/(obsInSample-h),
"GPL"=log(det(t(adamErrors) %*% adamErrors/(obsInSample-h))),
0);
}
if(is.na(CFValue) || is.nan(CFValue)){
CFValue[] <- 1e+300;
}
return(CFValue);
}
#### The function returns log-likelihood of the model ####
logLikADAM <- function(B,
etsModel, Etype, Ttype, Stype, modelIsTrendy, modelIsSeasonal, yInSample,
ot, otLogical, occurrenceModel, pFitted, obsInSample,
componentsNumberETS, componentsNumberETSSeasonal, componentsNumberETSNonSeasonal,
componentsNumberARIMA,
lags, lagsModel, lagsModelAll, lagsModelMax,
indexLookupTable, profilesRecentTable,
matVt, matWt, matF, vecG,
persistenceEstimate, persistenceLevelEstimate, persistenceTrendEstimate,
persistenceSeasonalEstimate, persistenceXregEstimate,
phiEstimate, initialType, initialEstimate,
initialLevelEstimate, initialTrendEstimate, initialSeasonalEstimate,
initialArimaEstimate, initialXregEstimate,
arimaModel, nonZeroARI, nonZeroMA, arEstimate, maEstimate, arimaPolynomials,
arOrders, iOrders, maOrders, arRequired, maRequired, armaParameters,
xregModel, xregNumber,
xregParametersMissing, xregParametersIncluded,
xregParametersEstimated, xregParametersPersistence,
constantRequired, constantEstimate,
bounds, loss, lossFunction, distribution, horizon, multisteps,
denominator=NULL, yDenominator=NULL,
other, otherParameterEstimate, lambda,
arPolynomialMatrix, maPolynomialMatrix, hessianCalculation=FALSE){
# If this is just for the calculation of hessian, return to the original values of parameters
# if(hessianCalculation && any(initialType==c("optimal","backcasting"))){
# persistenceToSkip <- 0;
# if(initialType=="optimal"){
# # Define, how many elements to skip (we don't normalise smoothing parameters)
# if(persistenceXregEstimate){
# persistenceToSkip[] <- componentsNumberETS+componentsNumberARIMA+xregNumber;
# }
# else{
# persistenceToSkip[] <- componentsNumberETS+componentsNumberARIMA;
# }
# j <- 1;
# if(phiEstimate){
# j[] <- 2;
# }
# # Level
# B[persistenceToSkip+j] <- B[persistenceToSkip+j] * sd(yInSample);
# # Trend
# if(Ttype!="N"){
# j[] <- j+1;
# if(Ttype=="A"){
# B[persistenceToSkip+j] <- B[persistenceToSkip+j] * sd(yInSample);
# }
# }
# # Seasonality
# if(Stype=="A"){
# if(componentsNumberETSSeasonal>1){
# for(k in 1:componentsNumberETSSeasonal){
# if(initialSeasonalEstimateFI[k]){
# # -1 is needed in order to remove the redundant seasonal element (normalisation)
# B[persistenceToSkip+j+2:lagsModel[componentsNumberETSNonSeasonal+k]-1] <-
# B[persistenceToSkip+j+2:lagsModel[componentsNumberETSNonSeasonal+k]-1] *
# sd(yInSample);
# j[] <- j+(lagsModelSeasonal[k]-1);
# }
# }
# }
# else{
# # -1 is needed in order to remove the redundant seasonal element (normalisation)
# B[persistenceToSkip+j+2:(lagsModel[componentsNumberETS])-1] <-
# B[persistenceToSkip+j+2:(lagsModel[componentsNumberETS])-1] * sd(yInSample);
# }
# }
# }
#
# # Normalise parameters of xreg if they are additive. Otherwise leave - they will be small and close to zero
# if(xregNumber>0 && Etype=="A"){
# denominator <- tail(colMeans(abs(matWt)),xregNumber);
# # If it is lower than 1, then we are probably dealing with (0, 1). No need to normalise
# denominator[abs(denominator)<1] <- 1;
# B[persistenceToSkip+sum(lagsModel)+c(1:xregNumber)] <- tail(B,xregNumber) * denominator;
# }
# }
if(!multisteps){
if(any(loss==c("LASSO","RIDGE"))){
return(0);
}
else{
distributionNew <- switch(loss,
"MSE"="dnorm",
"MAE"="dlaplace",
"HAM"="ds",
distribution);
lossNew <- switch(loss,
"MSE"=,"MAE"=,"HAM"="likelihood",
loss)
# bounds="none" switches off the checks of parameters.
logLikReturn <- -CF(B,
etsModel, Etype, Ttype, Stype, modelIsTrendy, modelIsSeasonal, yInSample,
ot, otLogical, occurrenceModel, obsInSample,
componentsNumberETS, componentsNumberETSSeasonal, componentsNumberETSNonSeasonal,
componentsNumberARIMA,
lags, lagsModel, lagsModelAll, lagsModelMax,
indexLookupTable, profilesRecentTable,
matVt, matWt, matF, vecG,
persistenceEstimate, persistenceLevelEstimate, persistenceTrendEstimate,
persistenceSeasonalEstimate, persistenceXregEstimate,
phiEstimate, initialType, initialEstimate,
initialLevelEstimate, initialTrendEstimate, initialSeasonalEstimate,
initialArimaEstimate, initialXregEstimate,
arimaModel, nonZeroARI, nonZeroMA, arEstimate, maEstimate, arimaPolynomials,
arOrders, iOrders, maOrders, arRequired, maRequired, armaParameters,
xregModel, xregNumber,
xregParametersMissing, xregParametersIncluded,
xregParametersEstimated, xregParametersPersistence,
constantRequired, constantEstimate,
bounds="none", lossNew, lossFunction, distributionNew, horizon, multisteps,
denominator, yDenominator, other, otherParameterEstimate, lambda,
arPolynomialMatrix, maPolynomialMatrix);
# print(B);
# print(logLikReturn)
# If this is an occurrence model, add the probabilities
if(occurrenceModel){
if(is.infinite(logLikReturn)){
logLikReturn[] <- 0;
}
if(any(c(1-pFitted[!otLogical]==0,pFitted[otLogical]==0))){
# return(-Inf);
ptNew <- pFitted[(pFitted!=0) & (pFitted!=1)];
otNew <- ot[(pFitted!=0) & (pFitted!=1)];
# Just return the original likelihood if the probability is weird
if(length(ptNew)==0){
return(logLikReturn);
}
else{
return(logLikReturn + sum(log(ptNew[otNew==1])) + sum(log(1-ptNew[otNew==0])));
}
}
else{
return(logLikReturn + sum(log(pFitted[otLogical])) + sum(log(1-pFitted[!otLogical])));
}
}
else{
return(logLikReturn);
}
}
}
else{
# Use the predictive likelihoods from the GPL paper:
# - Normal for MSEh, MSCE, GPL and their analytical counterparts
# - Laplace for MAEh and MACE,
# - S for HAMh and CHAM
# bounds="none" switches off the checks of parameters.
logLikReturn <- CF(B,
etsModel, Etype, Ttype, Stype, modelIsTrendy, modelIsSeasonal, yInSample,
ot, otLogical, occurrenceModel, obsInSample,
componentsNumberETS, componentsNumberETSSeasonal, componentsNumberETSNonSeasonal,
componentsNumberARIMA,
lags, lagsModel, lagsModelAll, lagsModelMax,
indexLookupTable, profilesRecentTable,
matVt, matWt, matF, vecG,
persistenceEstimate, persistenceLevelEstimate, persistenceTrendEstimate,
persistenceSeasonalEstimate, persistenceXregEstimate,
phiEstimate, initialType, initialEstimate,
initialLevelEstimate, initialTrendEstimate, initialSeasonalEstimate,
initialArimaEstimate, initialXregEstimate,
arimaModel, nonZeroARI, nonZeroMA, arEstimate, maEstimate, arimaPolynomials,
arOrders, iOrders, maOrders, arRequired, maRequired, armaParameters,
xregModel, xregNumber,
xregParametersMissing, xregParametersIncluded,
xregParametersEstimated, xregParametersPersistence,
constantRequired, constantEstimate,
bounds="none", loss, lossFunction, distribution, horizon, multisteps,
denominator, yDenominator,
other, otherParameterEstimate, lambda,
arPolynomialMatrix, maPolynomialMatrix);
# Concentrated log-likelihoods for the multistep losses
logLikReturn[] <- -switch(loss,
"MSEh"=, "aMSEh"=, "TMSE"=, "aTMSE"=, "MSCE"=, "aMSCE"=
(obsInSample-h)/2*(log(2*pi)+1+log(logLikReturn)),
"GTMSE"=, "aGTMSE"=
(obsInSample-h)/2*(log(2*pi)+1+logLikReturn),
"MAEh"=, "TMAE"=, "GTMAE"=, "MACE"=
(obsInSample-h)*(log(2)+1+log(logLikReturn)),
"HAMh"=, "THAM"=, "GTHAM"=, "CHAM"=
(obsInSample-h)*(log(4)+2+2*log(logLikReturn)),
#### Divide GPL by 8 in order to make it comparable with the univariate ones
"GPL"=, "aGPL"=
(obsInSample-h)/2*(h*log(2*pi)+h+logLikReturn)/h);
# This is not well motivated at the moment, but should make likelihood comparable, taking T instead of T-h
logLikReturn[] <- logLikReturn / (obsInSample-h) * obsInSample;
# In case of multiplicative model, we assume a normal or similar distribution
if(Etype=="M"){
# Fill in the matrices
adamElements <- filler(B,
etsModel, Etype, Ttype, Stype, modelIsTrendy, modelIsSeasonal,
componentsNumberETS, componentsNumberETSNonSeasonal,
componentsNumberETSSeasonal, componentsNumberARIMA,
lags, lagsModel, lagsModelMax,
matVt, matWt, matF, vecG,
persistenceEstimate, persistenceLevelEstimate, persistenceTrendEstimate,
persistenceSeasonalEstimate, persistenceXregEstimate,
phiEstimate,
initialType, initialEstimate,
initialLevelEstimate, initialTrendEstimate, initialSeasonalEstimate,
initialArimaEstimate, initialXregEstimate,
arimaModel, arEstimate, maEstimate, arOrders, iOrders, maOrders,
arRequired, maRequired, armaParameters,
nonZeroARI, nonZeroMA, arimaPolynomials,
xregModel, xregNumber,
xregParametersMissing, xregParametersIncluded,
xregParametersEstimated, xregParametersPersistence, constantEstimate);
# Write down the initials in the recent profile
profilesRecentTable[] <- adamElements$matVt[,1:lagsModelMax];
# Fit the model again to extract the fitted values
adamFitted <- adamFitterWrap(adamElements$matVt, adamElements$matWt, adamElements$matF, adamElements$vecG,
lagsModelAll, indexLookupTable, profilesRecentTable,
Etype, Ttype, Stype, componentsNumberETS, componentsNumberETSSeasonal,
componentsNumberARIMA, xregNumber, constantRequired,
yInSample, ot, any(initialType==c("complete","backcasting")),
nIterations);
logLikReturn[] <- logLikReturn - sum(log(abs(adamFitted$yFitted)));
}
return(logLikReturn);
}
}
#### The function estimates the ETS model and returns B, logLik, nParam and CF(B) ####
estimator <- function(etsModel, Etype, Ttype, Stype, lags, lagsModelSeasonal, lagsModelARIMA,
obsStates, obsInSample,
yInSample, persistence, persistenceEstimate,
persistenceLevel, persistenceLevelEstimate,
persistenceTrend, persistenceTrendEstimate,
persistenceSeasonal, persistenceSeasonalEstimate,
persistenceXreg, persistenceXregEstimate, persistenceXregProvided,
phi, phiEstimate,
initialType, initialLevel, initialTrend, initialSeasonal,
initialArima, initialEstimate,
initialLevelEstimate, initialTrendEstimate, initialSeasonalEstimate,
initialArimaEstimate, initialXregEstimate, initialXregProvided,
arimaModel, arRequired, iRequired, maRequired, armaParameters,
componentsNumberARIMA, componentsNamesARIMA,
formula, xregModel, xregModelInitials, xregData, xregNumber, xregNames, regressors,
xregParametersMissing, xregParametersIncluded,
xregParametersEstimated, xregParametersPersistence,
constantRequired, constantEstimate, constantValue, constantName,
ot, otLogical, occurrenceModel, pFitted,
bounds, loss, lossFunction, distribution,
horizon, multisteps, other, otherParameterEstimate, lambda){
# Create the basic variables
adamArchitect <- architector(etsModel, Etype, Ttype, Stype, lags, lagsModelSeasonal,
xregNumber, obsInSample, initialType,
arimaModel, lagsModelARIMA, xregModel, constantRequired,
profilesRecentTable, profilesRecentProvided);
list2env(adamArchitect, environment());
# Create the matrices for the specific ETS model
adamCreated <- creator(etsModel, Etype, Ttype, Stype, modelIsTrendy, modelIsSeasonal,
lags, lagsModel, lagsModelARIMA, lagsModelAll, lagsModelMax,
profilesRecentTable, profilesRecentProvided,
obsStates, obsInSample, obsAll, componentsNumberETS, componentsNumberETSSeasonal,
componentsNamesETS, otLogical, yInSample,
persistence, persistenceEstimate,
persistenceLevel, persistenceLevelEstimate, persistenceTrend, persistenceTrendEstimate,
persistenceSeasonal, persistenceSeasonalEstimate,
persistenceXreg, persistenceXregEstimate, persistenceXregProvided,
phi,
initialType, initialEstimate,
initialLevel, initialLevelEstimate, initialTrend, initialTrendEstimate,
initialSeasonal, initialSeasonalEstimate,
initialArima, initialArimaEstimate, initialArimaNumber,
initialXregEstimate, initialXregProvided,
arimaModel, arRequired, iRequired, maRequired, armaParameters,
arOrders, iOrders, maOrders,
componentsNumberARIMA, componentsNamesARIMA,
xregModel, xregModelInitials, xregData, xregNumber, xregNames,
xregParametersPersistence,
constantRequired, constantEstimate, constantValue, constantName);
# Initialise B
BValues <- initialiser(etsModel, Etype, Ttype, Stype, modelIsTrendy, modelIsSeasonal,
componentsNumberETSNonSeasonal, componentsNumberETSSeasonal, componentsNumberETS,
lags, lagsModel, lagsModelSeasonal, lagsModelARIMA, lagsModelMax,
adamCreated$matVt,
persistenceEstimate, persistenceLevelEstimate, persistenceTrendEstimate,
persistenceSeasonalEstimate, persistenceXregEstimate,
phiEstimate, initialType, initialEstimate,
initialLevelEstimate, initialTrendEstimate, initialSeasonalEstimate,
initialArimaEstimate, initialXregEstimate,
arimaModel, arRequired, maRequired, arEstimate, maEstimate, arOrders, maOrders,
componentsNumberARIMA, componentsNamesARIMA, initialArimaNumber,
xregModel, xregNumber,
xregParametersEstimated, xregParametersPersistence,
constantEstimate, constantName, otherParameterEstimate);
if(!is.null(B)){
if(!is.null(names(B))){
B <- B[names(B) %in% names(BValues$B)];
BValues$B[] <- B;
}
else{
BValues$B[] <- B;
names(B) <- names(BValues$B);
}
}
# print(BValues$B);
#### Preheating initials for ARIMA ####
# Preheat the initial state of ARIMA. Do this only for optimal initials and if B is not provided
if(arimaModel && initialType=="optimal" && initialArimaEstimate && is.null(B)){
# Estimate ARIMA with backcasting first
clNew <- cl;
# If environment is provided, use it
if(!is.null(ellipsis$environment)){
env <- ellipsis$environment;
}
# Modify model in case of ETS+ARIMA
if(etsModel){
clNew$model <- paste0(Etype, Ttype, "d"[phiEstimate], Stype);
}
# Use complete backcasting
clNew$initial <- "complete";
# Shut things up
clNew$silent <- TRUE;
# If this is an xreg model, we do selection, and there's no formula, create one
if(xregModel && !is.null(clNew$regressors) && clNew$regressors=="select"){
clNew$formula <- as.formula(paste0(responseName,"~",paste0(xregNames,collapse="+")));
}
# Switch off regressors selection
if(!is.null(clNew$regressors) && clNew$regressors=="select"){
clNew$regressors <- "use";
}
# Get rid of explanatory variables if they are not needed
if(!xregModel && (!is.null(ncol(data)) && ncol(data)>1)){
clNew$data <- data[,responseName];
}
# Call for ADAM with backcasting
adamBack <- suppressWarnings(eval(clNew, envir=env));
# Vector of initial estimates of parameters
B <- BValues$B;
# Number of smoothing, dampening and ARMA parameters
nParametersBack <- (etsModel*(persistenceLevelEstimate + modelIsTrendy*persistenceTrendEstimate +
modelIsSeasonal*sum(persistenceSeasonalEstimate) + phiEstimate) +
xregModel*persistenceXregEstimate*max(xregParametersPersistence) +
# AR and MA values
arimaModel*(arEstimate*sum(arOrders)+maEstimate*sum(maOrders)));
if(nParametersBack>0){
# Use the estimated parameters
B[1:nParametersBack] <- adamBack$B[1:nParametersBack];
}
# Remove redundant seasonal initials
if(modelIsSeasonal){
if(length(lagsModelSeasonal)>1){
for(i in 1:length(lagsModelSeasonal)){
adamBack$initial$seasonal[[i]] <- adamBack$initial$seasonal[[i]][1:(lagsModelSeasonal[i]-1)];
}
}
else{
adamBack$initial$seasonal <- adamBack$initial$seasonal[1:(lagsModelSeasonal-1)];
}
}
# If there are explanatory variables, use only those initials that are required
if(xregModel){
adamBack$initial$xreg <- adamBack$initial$xreg[xregParametersEstimated==1];
}
initialsUnlisted <- unlist(adamBack$initial);
# If initials are reasonable, use them
if(!any(is.na(initialsUnlisted))){
B[nParametersBack + c(1:length(initialsUnlisted))] <- initialsUnlisted;
}
# If the constant is used and it's good, record it
if(constantEstimate && !is.na(adamBack$constant)){
B[nParametersBack+componentsNumberETS+componentsNumberARIMA+xregNumber+1] <- adamBack$constant;
}
# Other parameters (shape etc)
if(otherParameterEstimate){
B[length(B)] <- abs(tail(adamBack$B,1));
}
# Parameter bounds
lb <- BValues$Bl;
ub <- BValues$Bu;
# Make sure that the bounds are reasonable
if(any(is.na(lb))){
lb[is.na(lb)] <- -Inf;
}
if(any(lb>B)){
lb[lb>B] <- -Inf;
}
if(any(is.na(ub))){
ub[is.na(ub)] <- Inf;
}
if(any(ub<B)){
ub[ub<B] <- Inf;
}
# adamCreatedARIMA <- filler(BValues$B,
# etsModel, Etype, Ttype, Stype, modelIsTrendy, modelIsSeasonal,
# componentsNumberETS, componentsNumberETSNonSeasonal,
# componentsNumberETSSeasonal, componentsNumberARIMA,
# lags, lagsModel, lagsModelMax,
# adamCreated$matVt, adamCreated$matWt, adamCreated$matF, adamCreated$vecG,
# persistenceEstimate, persistenceLevelEstimate, persistenceTrendEstimate,
# persistenceSeasonalEstimate, persistenceXregEstimate,
# phiEstimate,
# initialType, initialEstimate,
# initialLevelEstimate, initialTrendEstimate, initialSeasonalEstimate,
# initialArimaEstimate, initialXregEstimate,
# arimaModel, arEstimate, maEstimate, arOrders, iOrders, maOrders,
# arRequired, maRequired, armaParameters,
# nonZeroARI, nonZeroMA, adamCreated$arimaPolynomials,
# xregModel, xregNumber,
# xregParametersMissing, xregParametersIncluded,
# xregParametersEstimated, xregParametersPersistence, constantEstimate);
#
# # Write down the initials in the recent profile
# profilesRecentTable[] <- adamCreatedARIMA$matVt[,1:lagsModelMax];
#
# # Do initial fit to get the state values from the backcasting
# adamFitted <- adamFitterWrap(adamCreatedARIMA$matVt, adamCreatedARIMA$matWt, adamCreatedARIMA$matF, adamCreatedARIMA$vecG,
# lagsModelAll, indexLookupTable, profilesRecentTable,
# Etype, Ttype, Stype, componentsNumberETS, componentsNumberETSSeasonal,
# componentsNumberARIMA, xregNumber, constantRequired,
# yInSample, ot, TRUE, nIterations);
#
# adamCreated$matVt[,1:lagsModelMax] <- adamFitted$matVt[,1:lagsModelMax];
# # Produce new initials
# BValuesNew <- initialiser(etsModel, Etype, Ttype, Stype, modelIsTrendy, modelIsSeasonal,
# componentsNumberETSNonSeasonal, componentsNumberETSSeasonal, componentsNumberETS,
# lags, lagsModel, lagsModelSeasonal, lagsModelARIMA, lagsModelMax,
# adamCreated$matVt,
# persistenceEstimate, persistenceLevelEstimate, persistenceTrendEstimate,
# persistenceSeasonalEstimate, persistenceXregEstimate,
# phiEstimate, initialType, initialEstimate,
# initialLevelEstimate, initialTrendEstimate, initialSeasonalEstimate,
# initialArimaEstimate, initialXregEstimate,
# arimaModel, arRequired, maRequired, arEstimate, maEstimate, arOrders, maOrders,
# componentsNumberARIMA, componentsNamesARIMA, initialArimaNumber,
# xregModel, xregNumber,
# xregParametersEstimated, xregParametersPersistence,
# constantEstimate, constantName, otherParameterEstimate);
# B <- BValuesNew$B;
# # Failsafe, just in case if the initial values contain NA / NaN
# if(any(is.na(B))){
# B[is.na(B)] <- BValues$B[is.na(B)];
# }
# if(any(is.nan(B))){
# B[is.nan(B)] <- BValues$B[is.nan(B)];
# }
# # Fix for mixed ETS models producing negative values
# if(Etype=="M" & any(c(Ttype,Stype)=="A") ||
# Ttype=="M" & any(c(Etype,Stype)=="A") ||
# Stype=="M" & any(c(Etype,Ttype)=="A")){
# if(Etype=="M" && (!is.null(B["level"]) && B["level"]<=0)){
# B["level"] <- yInSample[1];
# }
# if(Ttype=="M" && B["trend"]<=0){
# B["trend"] <- 1;
# }
# if(Stype=="M" && any(B[substr(names(B),1,8)=="seasonal"]<=0)){
# B[B[substr(names(B),1,8)=="seasonal"]<=0] <- 1;
# }
# }
}
# Create the vector of initials for the optimisation
if(is.null(B)){
B <- BValues$B
}
if(is.null(lb)){
lb <- BValues$Bl;
}
if(is.null(ub)){
ub <- BValues$Bu;
}
# Companion matrices for the polynomials calculation -> stationarity / stability checks
if(arimaModel){
# AR polynomials
arPolynomialMatrix <- matrix(0, arOrders %*% lags, arOrders %*% lags);
if(nrow(arPolynomialMatrix)>1){
arPolynomialMatrix[2:nrow(arPolynomialMatrix)-1,2:nrow(arPolynomialMatrix)] <- diag(nrow(arPolynomialMatrix)-1);
}
# MA polynomials
maPolynomialMatrix <- matrix(0, maOrders %*% lags, maOrders %*% lags);
if(nrow(maPolynomialMatrix)>1){
maPolynomialMatrix[2:nrow(maPolynomialMatrix)-1,2:nrow(maPolynomialMatrix)] <- diag(nrow(maPolynomialMatrix)-1);
}
}
else{
maPolynomialMatrix <- arPolynomialMatrix <- NULL;
}
# If the distribution is default, change it according to the error term
if(distribution=="default"){
distributionNew <- switch(loss,
"likelihood"= switch(Etype, "A"= "dnorm", "M"= "dgamma"),
"MAEh"=, "MACE"=, "MAE"= "dlaplace",
"HAMh"=, "CHAM"=, "HAM"= "ds",
"MSEh"=, "MSCE"=, "MSE"=, "GPL"=, "dnorm");
}
else{
distributionNew <- distribution;
}
# print(B)
# print(BValues)
# print(Etype)
# print(Ttype)
# print(Stype)
# print(arOrders)
# stop()
print_level_hidden <- print_level;
if(print_level==41){
cat("Initial parameters:",B,"\n");
print_level[] <- 0;
}
maxevalUsed <- maxeval;
if(is.null(maxeval)){
maxevalUsed <- length(B) * 40;
# If this is pure ARIMA, take more time
# if(arimaModel && !etsModel){
# maxevalUsed <- length(B) * 80;
# }
# # If it is xregModel, do at least 500 iterations
# else
if(xregModel){
maxevalUsed[] <- length(B) * 100;
maxevalUsed[] <- max(1000,maxevalUsed);
}
}
# Prepare the denominator needed for the shrinkage of explanatory variables in LASSO / RIDGE
if(any(loss==c("LASSO","RIDGE"))){
if(xregNumber>0){
denominator <- apply(matWt, 2, sd);
denominator[is.infinite(denominator)] <- 1;
}
else{
denominator <- NULL;
}
yDenominator <- max(sd(diff(yInSample)),1);
}
else{
denominator <- NULL;
yDenominator <- NULL;
}
##### Parameter estimation ####
# Parameters are chosen to speed up the optimisation process and have decent accuracy
res <- suppressWarnings(nloptr(B, CF, lb=lb, ub=ub,
opts=list(algorithm=algorithm, xtol_rel=xtol_rel, xtol_abs=xtol_abs,
ftol_rel=ftol_rel, ftol_abs=ftol_abs,
maxeval=maxevalUsed, maxtime=maxtime, print_level=print_level),
etsModel=etsModel, Etype=Etype, Ttype=Ttype, Stype=Stype, modelIsTrendy=modelIsTrendy,
modelIsSeasonal=modelIsSeasonal, yInSample=yInSample,
ot=ot, otLogical=otLogical, occurrenceModel=occurrenceModel, obsInSample=obsInSample,
componentsNumberETS=componentsNumberETS,
componentsNumberETSSeasonal=componentsNumberETSSeasonal,
componentsNumberETSNonSeasonal=componentsNumberETSNonSeasonal,
componentsNumberARIMA=componentsNumberARIMA,
lags=lags, lagsModel=lagsModel, lagsModelAll=lagsModelAll, lagsModelMax=lagsModelMax,
indexLookupTable=indexLookupTable, profilesRecentTable=profilesRecentTable,
matVt=adamCreated$matVt, matWt=adamCreated$matWt,
matF=adamCreated$matF, vecG=adamCreated$vecG,
persistenceEstimate=persistenceEstimate, persistenceLevelEstimate=persistenceLevelEstimate,
persistenceTrendEstimate=persistenceTrendEstimate,
persistenceSeasonalEstimate=persistenceSeasonalEstimate,
persistenceXregEstimate=persistenceXregEstimate,
phiEstimate=phiEstimate, initialType=initialType,
initialEstimate=initialEstimate, initialLevelEstimate=initialLevelEstimate,
initialTrendEstimate=initialTrendEstimate, initialSeasonalEstimate=initialSeasonalEstimate,
initialArimaEstimate=initialArimaEstimate, initialXregEstimate=initialXregEstimate,
arimaModel=arimaModel, nonZeroARI=nonZeroARI, nonZeroMA=nonZeroMA,
arimaPolynomials=adamCreated$arimaPolynomials,
arEstimate=arEstimate, maEstimate=maEstimate,
arOrders=arOrders, iOrders=iOrders, maOrders=maOrders,
arRequired=arRequired, maRequired=maRequired, armaParameters=armaParameters,
xregModel=xregModel, xregNumber=xregNumber,
xregParametersMissing=xregParametersMissing,
xregParametersIncluded=xregParametersIncluded,
xregParametersEstimated=xregParametersEstimated,
xregParametersPersistence=xregParametersPersistence,
constantRequired=constantRequired, constantEstimate=constantEstimate,
bounds=bounds, loss=loss, lossFunction=lossFunction, distribution=distributionNew,
horizon=horizon, multisteps=multisteps,
denominator=denominator, yDenominator=yDenominator,
other=other, otherParameterEstimate=otherParameterEstimate, lambda=lambda,
arPolynomialMatrix=arPolynomialMatrix, maPolynomialMatrix=maPolynomialMatrix));
if(is.infinite(res$objective) || res$objective==1e+300){
# If the optimisation didn't work, give it another try with zero initials for smoothing parameters
if(etsModel){
B[1:componentsNumberETS] <- 0;
}
if(arimaModel){
B[componentsNumberETS+persistenceXregEstimate*xregNumber+
c(1:sum(arOrders*arEstimate,maOrders*maEstimate))] <- 0.01;
}
# print(B)
res <- suppressWarnings(nloptr(B, CF, lb=lb, ub=ub,
opts=list(algorithm=algorithm, xtol_rel=xtol_rel,
ftol_rel=ftol_rel, ftol_abs=ftol_abs,
maxeval=maxevalUsed, maxtime=maxtime, print_level=print_level),
etsModel=etsModel, Etype=Etype, Ttype=Ttype, Stype=Stype, modelIsTrendy=modelIsTrendy,
modelIsSeasonal=modelIsSeasonal, yInSample=yInSample,
ot=ot, otLogical=otLogical, occurrenceModel=occurrenceModel, obsInSample=obsInSample,
componentsNumberETS=componentsNumberETS,
componentsNumberETSSeasonal=componentsNumberETSSeasonal,
componentsNumberETSNonSeasonal=componentsNumberETSNonSeasonal,
componentsNumberARIMA=componentsNumberARIMA,
lags=lags, lagsModel=lagsModel, lagsModelAll=lagsModelAll, lagsModelMax=lagsModelMax,
indexLookupTable=indexLookupTable, profilesRecentTable=profilesRecentTable,
matVt=adamCreated$matVt, matWt=adamCreated$matWt,
matF=adamCreated$matF, vecG=adamCreated$vecG,
persistenceEstimate=persistenceEstimate,
persistenceLevelEstimate=persistenceLevelEstimate,
persistenceTrendEstimate=persistenceTrendEstimate,
persistenceSeasonalEstimate=persistenceSeasonalEstimate,
persistenceXregEstimate=persistenceXregEstimate,
phiEstimate=phiEstimate, initialType=initialType,
initialEstimate=initialEstimate, initialLevelEstimate=initialLevelEstimate,
initialTrendEstimate=initialTrendEstimate, initialSeasonalEstimate=initialSeasonalEstimate,
initialArimaEstimate=initialArimaEstimate, initialXregEstimate=initialXregEstimate,
arimaModel=arimaModel, nonZeroARI=nonZeroARI, nonZeroMA=nonZeroMA,
arimaPolynomials=adamCreated$arimaPolynomials,
arEstimate=arEstimate, maEstimate=maEstimate,
arOrders=arOrders, iOrders=iOrders, maOrders=maOrders,
arRequired=arRequired, maRequired=maRequired, armaParameters=armaParameters,
xregModel=xregModel, xregNumber=xregNumber,
xregParametersMissing=xregParametersMissing,
xregParametersIncluded=xregParametersIncluded,
xregParametersEstimated=xregParametersEstimated,
xregParametersPersistence=xregParametersPersistence,
constantRequired=constantRequired, constantEstimate=constantEstimate,
bounds=bounds, loss=loss, lossFunction=lossFunction, distribution=distributionNew,
horizon=horizon, multisteps=multisteps,
denominator=denominator, yDenominator=yDenominator,
other=other, otherParameterEstimate=otherParameterEstimate, lambda=lambda,
arPolynomialMatrix=arPolynomialMatrix, maPolynomialMatrix=maPolynomialMatrix));
}
if(print_level_hidden>0){
print(res);
}
##### !!! Check the obtained parameters and the loss value and remove redundant parameters !!! #####
# Cases to consider:
# 1. Some smoothing parameters are zero or one;
# 2. The cost function value is -Inf (due to no variability in the sample);
# Prepare the values to return
B[] <- res$solution;
CFValue <- res$objective;
# A fix for the special case of LASSO/RIDGE with lambda==1
if(any(loss==c("LASSO","RIDGE")) && lambda==1){
CFValue[] <- 0;
}
nParamEstimated <- length(B);
# Return a proper logLik class
logLikADAMValue <- structure(logLikADAM(B,
etsModel, Etype, Ttype, Stype, modelIsTrendy, modelIsSeasonal, yInSample,
ot, otLogical, occurrenceModel, pFitted, obsInSample,
componentsNumberETS, componentsNumberETSSeasonal, componentsNumberETSNonSeasonal,
componentsNumberARIMA,
lags, lagsModel, lagsModelAll, lagsModelMax,
indexLookupTable, profilesRecentTable,
adamCreated$matVt, adamCreated$matWt, adamCreated$matF, adamCreated$vecG,
persistenceEstimate, persistenceLevelEstimate, persistenceTrendEstimate,
persistenceSeasonalEstimate, persistenceXregEstimate,
phiEstimate, initialType, initialEstimate,
initialLevelEstimate, initialTrendEstimate, initialSeasonalEstimate,
initialArimaEstimate, initialXregEstimate,
arimaModel, nonZeroARI, nonZeroMA, arEstimate, maEstimate,
adamCreated$arimaPolynomials,
arOrders, iOrders, maOrders, arRequired, maRequired, armaParameters,
xregModel, xregNumber,
xregParametersMissing, xregParametersIncluded,
xregParametersEstimated, xregParametersPersistence,
constantRequired, constantEstimate,
bounds, loss, lossFunction, distributionNew, horizon, multisteps,
denominator, yDenominator, other, otherParameterEstimate, lambda,
arPolynomialMatrix, maPolynomialMatrix),
# In case of likelihood, we typically have one more parameter to estimate - scale.
nobs=obsInSample,df=nParamEstimated+(loss=="likelihood"),class="logLik");
xregIndex <- 1;
#### If we do variables selection, do it here, then reestimate the model. ####
if(regressors=="select"){
# This is a failsafe for weird cases, when something went wrong with
if(any(is.nan(adamCreated$matVt[,1:lagsModelMax]))){
adamCreated[] <- creator(etsModel, Etype, Ttype, Stype, modelIsTrendy, modelIsSeasonal,
lags, lagsModel, lagsModelARIMA, lagsModelAll, lagsModelMax,
profilesRecentTable, profilesRecentProvided,
obsStates, obsInSample, obsAll, componentsNumberETS, componentsNumberETSSeasonal,
componentsNamesETS, otLogical, yInSample,
persistence, persistenceEstimate,
persistenceLevel, persistenceLevelEstimate, persistenceTrend, persistenceTrendEstimate,
persistenceSeasonal, persistenceSeasonalEstimate,
persistenceXreg, persistenceXregEstimate, persistenceXregProvided,
phi,
initialType, initialEstimate,
initialLevel, initialLevelEstimate, initialTrend, initialTrendEstimate,
initialSeasonal, initialSeasonalEstimate,
initialArima, initialArimaEstimate, initialArimaNumber,
initialXregEstimate, initialXregProvided,
arimaModel, arRequired, iRequired, maRequired, armaParameters,
arOrders, iOrders, maOrders,
componentsNumberARIMA, componentsNamesARIMA,
xregModel, xregModelInitials, xregData, xregNumber, xregNames,
xregParametersPersistence,
constantRequired, constantEstimate, constantValue, constantName);
}
# Fill in the matrices
adamCreated[] <- filler(B,
etsModel, Etype, Ttype, Stype, modelIsTrendy, modelIsSeasonal,
componentsNumberETS, componentsNumberETSNonSeasonal,
componentsNumberETSSeasonal, componentsNumberARIMA,
lags, lagsModel, lagsModelMax,
adamCreated$matVt, adamCreated$matWt, adamCreated$matF, adamCreated$vecG,
persistenceEstimate, persistenceLevelEstimate, persistenceTrendEstimate,
persistenceSeasonalEstimate, persistenceXregEstimate,
phiEstimate,
initialType, initialEstimate,
initialLevelEstimate, initialTrendEstimate, initialSeasonalEstimate,
initialArimaEstimate, initialXregEstimate,
arimaModel, arEstimate, maEstimate, arOrders, iOrders, maOrders,
arRequired, maRequired, armaParameters,
nonZeroARI, nonZeroMA, adamCreated$arimaPolynomials,
xregModel, xregNumber,
xregParametersMissing, xregParametersIncluded,
xregParametersEstimated, xregParametersPersistence, constantEstimate);
# Write down the initials in the recent profile
profilesRecentTable[] <- adamCreated$matVt[,1:lagsModelMax];
# Fit the model to the data
adamFitted <- adamFitterWrap(adamCreated$matVt, adamCreated$matWt, adamCreated$matF, adamCreated$vecG,
lagsModelAll, indexLookupTable, profilesRecentTable,
Etype, Ttype, Stype, componentsNumberETS, componentsNumberETSSeasonal,
componentsNumberARIMA, xregNumber, constantRequired,
yInSample, ot, any(initialType==c("complete","backcasting")),
nIterations);
# Extract the errors correctly
errors <- switch(distributionNew,
"dlnorm"=, "dllaplace"=, "dls"=,
"dlgnorm"=, "dinvgauss"=, "dgamma"=switch(Etype,
"A"=1+adamFitted$errors/adamFitted$yFitted,
"M"=adamFitted$errors),
"dnorm"=, "dlaplace"=, "ds"=, "dgnorm"=, "dlogis"=, "dt"=, "dalaplace"=,adamFitted$errors);
# Extract the errors and amend them to correspond to the distribution
errors[] <- errors + switch(Etype,"A"=0,"M"=1);
# This is failsafe for cases, when errors contain negative values, although they shouldn't
if(any(distributionNew==c("dinvgauss","dgamma","dlnorm","dllaplace","dls","dlgnorm")) &&
any(c(Etype,Ttype,Stype)=="A") && any(errors<=0)){
errors[errors<=0] <- 1e-100;
}
df <- length(B)+1;
if(any(distributionNew==c("dalaplace","dgnorm","dlgnorm","dt")) && otherParameterEstimate){
other <- abs(B[length(B)]);
df[] <- df - 1;
}
# Call the xregSelector providing the original matrix with the data
xregIndex[] <- switch(Etype,"A"=1,"M"=2);
xregModelInitials[[xregIndex]] <- xregSelector(errors=errors,
xregData=xregDataOriginal[1:obsInSample,
colnames(xregDataOriginal)!=responseName,
drop=FALSE],
ic=ic,
df=df, distribution=distributionNew, occurrence=oesModel,
other=other);
xregNumber <- length(xregModelInitials[[xregIndex]]$initialXreg);
xregNames <- names(xregModelInitials[[xregIndex]]$initialXreg);
# Fix the names of variables
xregNames[] <- make.names(xregNames, unique=TRUE);
# If there are some variables, then do the proper reestimation and return the new values
if(xregNumber>0){
xregModel[] <- TRUE;
initialXregEstimate[] <- initialXregEstimateOriginal;
persistenceXregEstimate[] <- persistenceXregEstimateOriginal;
# xregData <- xregDataOriginal[,xregNames,drop=FALSE];
# Redefine loss for ALM
lossNew <- switch(loss,
"MSEh"=,"TMSE"=,"GTMSE"=,"MSCE"="MSE",
"MAEh"=,"TMAE"=,"GTMAE"=,"MACE"="MAE",
"HAMh"=,"THAM"=,"GTHAM"=,"CHAM"="HAM",
loss);
if(lossNew=="custom"){
lossNew <- lossFunction;
}
# Fix the name of the response variable
xregModelInitials[[xregIndex]]$formula[[2]] <- as.name(responseName);
formula <- xregModelInitials[[xregIndex]]$formula;
xregModelInitials[[which(c(1,2)!=xregIndex)]]$formula <- formulaToUse <- formula;
# Fix formula if dnorm / dlaplace / ds etc are used for Etype=="M"
trendIncluded <- any(all.vars(formulaToUse)[-1]=="trend");
if((length(formulaToUse[[2]])==1 ||
(length(formulaToUse[[2]])>1 & !any(as.character(formulaToUse[[2]])=="log"))) &&
(Etype=="M" && any(distribution==c("dnorm","dlaplace","ds","dgnorm","dlogis","dt","dalaplace")))){
if(trendIncluded){
formulaToUse <- update(formulaToUse,log(.)~.);
}
else{
formulaToUse <- update(formulaToUse,log(.)~.+trend);
}
}
else{
if(!trendIncluded){
formulaToUse <- update(formulaToUse,.~.+trend);
}
}
# Estimate alm again in order to get proper initials
almModel <- do.call(alm,list(formula=formulaToUse,
data=data[1:obsInSample,,drop=FALSE],
distribution=distributionNew, loss=lossNew, occurrence=oesModel));
# Remove trend
if(!trendIncluded){
almModel$coefficients <- almModel$coefficients[names(almModel$coefficients)!="trend"];
almModel$data <- almModel$data[,colnames(almModel$data)!="trend",drop=FALSE];
}
almIntercept <- almModel$coefficients["(Intercept)"];
xregModelInitials[[xregIndex]]$initialXreg <- coef(almModel)[-1];
#### Fix xreg vectors based on the selected stuff ####
xregNames <- colnames(almModel$data)[-1];
# Robustify the names of variables
colnames(data) <- make.names(colnames(data),unique=TRUE);
# The names of the original variables
xregNamesOriginal <- all.vars(formula)[-1];
# Expand the variables. We cannot use alm, because it is based on obsInSample
xregData <- model.frame(formula,data=as.data.frame(data));
# Binary, flagging factors in the data
xregFactors <- (attr(terms(xregData),"dataClasses")=="factor")[-1];
# Expanded stuff with all levels for factors
if(any(xregFactors)){
# Levels for the factors
xregFactorsLevels <- lapply(data,levels);
xregFactorsLevels[[responseName]] <- NULL;
xregModelMatrix <- model.matrix(xregData,xregData,
contrasts.arg=lapply(xregData[attr(terms(xregData),"dataClasses")=="factor"],
contrasts, contrasts=FALSE));
xregNamesModified <- colnames(xregModelMatrix)[-1];
}
else{
xregModelMatrix <- model.matrix(xregData,data=xregData);
xregNamesModified <- xregNames;
}
xregData <- as.matrix(xregModelMatrix);
# Remove intercept
interceptIsPresent <- FALSE;
if(any(colnames(xregData)=="(Intercept)")){
interceptIsPresent[] <- TRUE;
xregData <- xregData[,-1,drop=FALSE];
}
xregNumber <- ncol(xregData);
# If there are factors not in the alm data, create additional initials
if(any(xregFactors) && any(!(xregNamesModified %in% xregNames))){
# The indices of the original parameters
xregParametersMissing <- setNames(vector("numeric",xregNumber),xregNamesModified);
# # The indices of the original parameters
xregParametersIncluded <- setNames(vector("numeric",xregNumber),xregNamesModified);
# The vector, marking the same values of smoothing parameters
if(interceptIsPresent){
xregParametersPersistence <- setNames(attr(xregModelMatrix,"assign")[-1],xregNamesModified);
}
else{
xregParametersPersistence <- setNames(attr(xregModelMatrix,"assign"),xregNamesModified);
}
xregAbsent <- !(xregNamesModified %in% xregNames);
xregParametersNew <- setNames(rep(NA,xregNumber),xregNamesModified);
xregParametersNew[!xregAbsent] <- xregModelInitials[[xregIndex]]$initialXreg;
# Go through new names and find, where they came from. Then get the missing parameters
for(i in which(xregAbsent)){
# Find the name of the original variable
# Use only the last value... hoping that the names like x and x1 are not used.
xregNameFoundID <- sapply(xregNamesOriginal,grepl,xregNamesModified[i]);
xregNameFound <- tail(names(xregNameFoundID)[xregNameFoundID],1);
# Get the indices of all k-1 levels
xregParametersIncluded[xregNames[xregNames %in% paste0(xregNameFound,
xregFactorsLevels[[xregNameFound]])]] <- i;
# Get the index of the absent one
xregParametersMissing[i] <- i;
# Fill in the absent one, add intercept
xregParametersNew[i] <- almIntercept;
xregParametersNew[xregNamesModified[xregParametersIncluded==i]] <- almIntercept +
xregParametersNew[xregNamesModified[xregParametersIncluded==i]];
# normalise all of them
xregParametersNew[xregNamesModified[c(which(xregParametersIncluded==i),i)]] <-
xregParametersNew[xregNamesModified[c(which(xregParametersIncluded==i),i)]] -
mean(xregParametersNew[xregNamesModified[c(which(xregParametersIncluded==i),i)]]);
}
# Write down the new parameters
xregModelInitials[[xregIndex]]$initialXreg <- xregParametersNew;
xregNames <- xregNamesModified;
# The vector of parameters that should be estimated (numeric + original levels of factors)
xregParametersEstimated <- xregParametersIncluded
xregParametersEstimated[xregParametersEstimated!=0] <- 1;
xregParametersEstimated[xregParametersMissing==0 & xregParametersIncluded==0] <- 1;
}
else{
xregFactors <- FALSE;
xregParametersPersistence <- setNames(c(1:xregNumber),xregNames);
xregParametersEstimated <- setNames(rep(1,xregNumber),xregNames);
xregParametersMissing <- setNames(c(1:xregNumber),xregNames);
xregParametersIncluded <- setNames(c(1:xregNumber),xregNames);
}
return(estimator(etsModel, Etype, Ttype, Stype, lags, lagsModelSeasonal, lagsModelARIMA,
obsStates, obsInSample,
yInSample, persistence, persistenceEstimate,
persistenceLevel, persistenceLevelEstimate,
persistenceTrend, persistenceTrendEstimate,
persistenceSeasonal, persistenceSeasonalEstimate,
persistenceXreg, persistenceXregEstimate, persistenceXregProvided,
phi, phiEstimate,
initialType, initialLevel, initialTrend, initialSeasonal,
initialArima, initialEstimate,
initialLevelEstimate, initialTrendEstimate, initialSeasonalEstimate,
initialArimaEstimate, initialXregEstimate, initialXregProvided,
arimaModel, arRequired, iRequired, maRequired, armaParameters,
componentsNumberARIMA, componentsNamesARIMA,
formula, xregModel, xregModelInitials, xregData, xregNumber, xregNames, regressors="use",
xregParametersMissing, xregParametersIncluded,
xregParametersEstimated, xregParametersPersistence,
constantRequired, constantEstimate, constantValue, constantName,
ot, otLogical, occurrenceModel, pFitted,
bounds, loss, lossFunction, distribution,
horizon, multisteps, other, otherParameterEstimate, lambda));
}
}
return(list(B=B, CFValue=CFValue, nParamEstimated=nParamEstimated, logLikADAMValue=logLikADAMValue,
xregModel=xregModel, xregData=xregData, xregNumber=xregNumber,
xregNames=xregNames, xregModelInitials=xregModelInitials, formula=formula,
initialXregEstimate=initialXregEstimate, persistenceXregEstimate=persistenceXregEstimate,
xregParametersMissing=xregParametersMissing,xregParametersIncluded=xregParametersIncluded,
xregParametersEstimated=xregParametersEstimated,xregParametersPersistence=xregParametersPersistence,
arimaPolynomials=adamCreated$arimaPolynomials,
res=res));
}
#### The function creates a pool of models and selects the best of them ####
selector <- function(model, modelsPool, allowMultiplicative,
etsModel, Etype, Ttype, Stype, damped, lags,
lagsModelSeasonal, lagsModelARIMA,
obsStates, obsInSample,
yInSample, persistence, persistenceEstimate,
persistenceLevel, persistenceLevelEstimate,
persistenceTrend, persistenceTrendEstimate,
persistenceSeasonal, persistenceSeasonalEstimate,
persistenceXreg, persistenceXregEstimate, persistenceXregProvided,
phi, phiEstimate,
initialType, initialLevel, initialTrend, initialSeasonal,
initialArima, initialEstimate,
initialLevelEstimate, initialTrendEstimate, initialSeasonalEstimate,
initialArimaEstimate, initialXregEstimate, initialXregProvided,
arimaModel, arRequired, iRequired, maRequired, armaParameters,
componentsNumberARIMA, componentsNamesARIMA,
xregModel, xregModelInitials, xregData, xregNumber, xregNames, regressors,
xregParametersMissing, xregParametersIncluded,
xregParametersEstimated, xregParametersPersistence,
constantRequired, constantEstimate, constantValue, constantName,
ot, otLogical, occurrenceModel, pFitted, icFunction,
bounds, loss, lossFunction, distribution,
horizon, multisteps, other, otherParameterEstimate, lambda){
# Check if the pool was provided. In case of "no", form the big and the small ones
if(is.null(modelsPool)){
# The variable saying that the pool was not provided.
if(!silent){
cat("Forming the pool of models based on... ");
}
# Define the whole pool of errors
if(!allowMultiplicative){
poolErrors <- c("A");
poolTrends <- c("N","A","Ad");
poolSeasonals <- c("N","A");
}
else{
poolErrors <- c("A","M");
poolTrends <- c("N","A","Ad","M","Md");
poolSeasonals <- c("N","A","M");
}
# Some preparation variables
# If Etype is not Z, then check on additive errors
if(Etype!="Z"){
poolErrors <- poolErrorsSmall <- Etype;
}
else{
poolErrorsSmall <- "A";
}
# If Ttype is not Z, then create a pool with specified type
if(Ttype!="Z"){
if(Ttype=="X"){
poolTrendsSmall <- c("N","A");
poolTrends <- c("N","A","Ad");
checkTrend <- TRUE;
}
else if(Ttype=="Y"){
poolTrendsSmall <- c("N","M");
poolTrends <- c("N","M","Md");
checkTrend <- TRUE;
}
else{
if(damped){
poolTrends <- poolTrendsSmall <- paste0(Ttype,"d");
}
else{
poolTrends <- poolTrendsSmall <- Ttype;
}
checkTrend <- FALSE;
}
}
else{
poolTrendsSmall <- c("N","A");
checkTrend <- TRUE;
}
# If Stype is not Z, then create specific pools
if(Stype!="Z"){
if(Stype=="X"){
poolSeasonals <- poolSeasonalsSmall <- c("N","A");
checkSeasonal <- TRUE;
}
else if(Stype=="Y"){
poolSeasonalsSmall <- c("N","M");
poolSeasonals <- c("N","M");
checkSeasonal <- TRUE;
}
else{
poolSeasonalsSmall <- Stype;
poolSeasonals <- Stype;
checkSeasonal <- FALSE;
}
}
else{
poolSeasonalsSmall <- c("N","A","M");
checkSeasonal <- TRUE;
}
# If ZZZ, then the vector is: "ANN" "ANA" "ANM" "AAN" "AAA" "AAM"
# Otherwise id depends on the provided restrictions
poolSmall <- paste0(rep(poolErrorsSmall,length(poolTrendsSmall)*length(poolSeasonalsSmall)),
rep(poolTrendsSmall,each=length(poolSeasonalsSmall)),
rep(poolSeasonalsSmall,length(poolTrendsSmall)));
# Align error and seasonality, if the error was not forced to be additive
# The new pool: "ANN" "ANA" "MNM" "AAN" "AAA" "MAM"
if(any(substr(poolSmall,3,3)=="M") && all(Etype!=c("A","X"))){
multiplicativeSeason <- (substr(poolSmall,3,3)=="M");
poolSmall[multiplicativeSeason] <- paste0("M",substr(poolSmall[multiplicativeSeason],2,3));
}
modelsTested <- NULL;
modelCurrent <- NA;
# Counter + checks for the components
j <- 1;
i <- 0;
check <- TRUE;
besti <- bestj <- 1;
results <- vector("list",length(poolSmall));
#### Branch and bound is here ####
while(check){
i <- i + 1;
modelCurrent[] <- poolSmall[j];
if(!silent){
cat(modelCurrent,"\b, ");
}
Etype[] <- substring(modelCurrent,1,1);
Ttype[] <- substring(modelCurrent,2,2);
if(nchar(modelCurrent)==4){
phi[] <- 0.95;
phiEstimate[] <- TRUE;
Stype[] <- substring(modelCurrent,4,4);
}
else{
phi[] <- 1;
phiEstimate[] <- FALSE;
Stype[] <- substring(modelCurrent,3,3);
}
results[[i]] <- estimator(etsModel, Etype, Ttype, Stype, lags, lagsModelSeasonal, lagsModelARIMA,
obsStates, obsInSample,
yInSample, persistence, persistenceEstimate,
persistenceLevel, persistenceLevelEstimate,
persistenceTrend, persistenceTrendEstimate,
persistenceSeasonal, persistenceSeasonalEstimate,
persistenceXreg, persistenceXregEstimate, persistenceXregProvided,
phi, phiEstimate,
initialType, initialLevel, initialTrend, initialSeasonal,
initialArima, initialEstimate,
initialLevelEstimate, initialTrendEstimate, initialSeasonalEstimate,
initialArimaEstimate, initialXregEstimate, initialXregProvided,
arimaModel, arRequired, iRequired, maRequired, armaParameters,
componentsNumberARIMA, componentsNamesARIMA,
formula, xregModel, xregModelInitials, xregData, xregNumber, xregNames, regressors,
xregParametersMissing, xregParametersIncluded,
xregParametersEstimated, xregParametersPersistence,
constantRequired, constantEstimate, constantValue, constantName,
ot, otLogical, occurrenceModel, pFitted,
bounds, loss, lossFunction, distribution,
horizon, multisteps, other, otherParameterEstimate, lambda);
results[[i]]$IC <- icFunction(results[[i]]$logLikADAMValue);
results[[i]]$Etype <- Etype;
results[[i]]$Ttype <- Ttype;
results[[i]]$Stype <- Stype;
results[[i]]$phiEstimate <- phiEstimate;
if(phiEstimate){
results[[i]]$phi <- results[[i]]$B[names(results[[i]]$B)=="phi"];
}
else{
results[[i]]$phi <- 1;
}
results[[i]]$model <- modelCurrent;
modelsTested <- c(modelsTested,modelCurrent);
if(j>1){
# If the first is better than the second, then choose first
if(results[[besti]]$IC <= results[[i]]$IC){
# If Ttype is the same, then we check seasonality
if(substring(modelCurrent,2,2)==substring(poolSmall[bestj],2,2)){
poolSeasonals <- results[[besti]]$Stype;
checkSeasonal <- FALSE;
j <- which(poolSmall!=poolSmall[bestj] &
substring(poolSmall,nchar(poolSmall),nchar(poolSmall))==poolSeasonals);
}
# Otherwise we checked trend
else{
poolTrends <- results[[bestj]]$Ttype;
checkTrend[] <- FALSE;
}
}
else{
# If the trend is the same
if(substring(modelCurrent,2,2) == substring(poolSmall[besti],2,2)){
poolSeasonals <- poolSeasonals[poolSeasonals!=results[[besti]]$Stype];
if(length(poolSeasonals)>1){
# Select another seasonal model, that is not from the previous iteration and not the current one
bestj[] <- j;
besti[] <- i;
# j[] <- 3;
j <- 3;
}
else{
bestj[] <- j;
besti[] <- i;
# Move to checking the trend
j <- which(substring(poolSmall,nchar(poolSmall),nchar(poolSmall))==poolSeasonals &
substring(poolSmall,2,2)!=substring(modelCurrent,2,2));
checkSeasonal[] <- FALSE;
}
}
else{
poolTrends <- poolTrends[poolTrends!=results[[bestj]]$Ttype];
besti[] <- i;
bestj[] <- j;
checkTrend[] <- FALSE;
}
}
if(all(!c(checkTrend,checkSeasonal))){
check[] <- FALSE;
}
}
else{
j <- 2;
}
# If this is NULL, then this was a short pool and we checked everything
if(length(j)==0){
j <- length(poolSmall);
}
if(j>length(poolSmall)){
check[] <- FALSE;
}
}
# Prepare a bigger pool based on the small one
modelsPool <- unique(c(modelsTested,
paste0(rep(poolErrors,each=length(poolTrends)*length(poolSeasonals)),
poolTrends,
rep(poolSeasonals,each=length(poolTrends)))));
j <- length(modelsTested);
}
else{
j <- 0;
results <- vector("list",length(modelsPool));
}
modelsNumber <- length(modelsPool);
#### Run the full pool of models ####
if(!silent){
cat("Estimation progress: ");
}
# Start loop of models
while(j < modelsNumber){
j <- j + 1;
if(!silent){
if(j==1){
cat("\b");
}
cat(paste0(rep("\b",nchar(round((j-1)/modelsNumber,2)*100)+1),collapse=""));
cat(round(j/modelsNumber,2)*100,"\b%");
}
modelCurrent <- modelsPool[j];
# print(modelCurrent)
Etype <- substring(modelCurrent,1,1);
Ttype <- substring(modelCurrent,2,2);
if(nchar(modelCurrent)==4){
phi[] <- 0.95;
Stype <- substring(modelCurrent,4,4);
phiEstimate <- TRUE;
}
else{
phi[] <- 1;
Stype <- substring(modelCurrent,3,3);
phiEstimate <- FALSE;
}
results[[j]] <- estimator(etsModel, Etype, Ttype, Stype, lags, lagsModelSeasonal, lagsModelARIMA,
obsStates, obsInSample,
yInSample, persistence, persistenceEstimate,
persistenceLevel, persistenceLevelEstimate,
persistenceTrend, persistenceTrendEstimate,
persistenceSeasonal, persistenceSeasonalEstimate,
persistenceXreg, persistenceXregEstimate, persistenceXregProvided,
phi, phiEstimate,
initialType, initialLevel, initialTrend, initialSeasonal,
initialArima, initialEstimate,
initialLevelEstimate, initialTrendEstimate, initialSeasonalEstimate,
initialArimaEstimate, initialXregEstimate, initialXregProvided,
arimaModel, arRequired, iRequired, maRequired, armaParameters,
componentsNumberARIMA, componentsNamesARIMA,
formula, xregModel, xregModelInitials, xregData, xregNumber, xregNames, regressors,
xregParametersMissing, xregParametersIncluded,
xregParametersEstimated, xregParametersPersistence,
constantRequired, constantEstimate, constantValue, constantName,
ot, otLogical, occurrenceModel, pFitted,
bounds, loss, lossFunction, distribution,
horizon, multisteps, other, otherParameterEstimate, lambda);
results[[j]]$IC <- icFunction(results[[j]]$logLikADAMValue);
results[[j]]$Etype <- Etype;
results[[j]]$Ttype <- Ttype;
results[[j]]$Stype <- Stype;
results[[j]]$phiEstimate <- phiEstimate;
if(phiEstimate){
results[[j]]$phi <- results[[j]]$B[names(results[[j]]$B)=="phi"];
}
else{
results[[j]]$phi <- 1;
}
results[[j]]$model <- modelCurrent;
}
if(!silent){
cat("... Done! \n");
}
# Extract ICs and find the best
icSelection <- vector("numeric",modelsNumber);
for(i in 1:modelsNumber){
icSelection[i] <- results[[i]]$IC;
}
names(icSelection) <- modelsPool;
icSelection[is.nan(icSelection)] <- 1E100;
return(list(results=results,icSelection=icSelection));
}
##### Function uses residuals in order to determine the needed xreg #####
xregSelector <- function(errors, xregData, ic, df, distribution, occurrence, other){
alpha <- shape <- nu <- NULL;
if(distribution=="dalaplace"){
alpha <- other;
}
else if(any(distribution==c("dgnorm","dlgnorm"))){
shape <- other;
}
else if(distribution=="dt"){
nu <- other;
}
stepwiseModel <- suppressWarnings(stepwise(data.frame(errorsIvan41=errors,xregData[1:obsInSample,,drop=FALSE]),
ic=ic, df=df, distribution=distribution, occurrence=occurrence, silent=TRUE,
alpha=alpha, shape=shape, nu=nu));
return(list(initialXreg=coef(stepwiseModel)[-1],other=stepwiseModel$other,formula=formula(stepwiseModel)));
}
##### Function prepares all the matrices and vectors for return #####
preparator <- function(B, etsModel, Etype, Ttype, Stype,
lagsModel, lagsModelMax, lagsModelAll,
componentsNumberETS, componentsNumberETSSeasonal,
xregNumber, distribution, loss,
persistenceEstimate, persistenceLevelEstimate, persistenceTrendEstimate,
persistenceSeasonalEstimate, persistenceXregEstimate,
phiEstimate, otherParameterEstimate,
initialType, initialEstimate,
initialLevelEstimate, initialTrendEstimate, initialSeasonalEstimate,
initialArimaEstimate, initialXregEstimate,
matVt, matWt, matF, vecG,
occurrenceModel, ot, oesModel,
parametersNumber, CFValue,
arimaModel, arRequired, maRequired,
arEstimate, maEstimate, arOrders, iOrders, maOrders,
nonZeroARI, nonZeroMA,
arimaPolynomials, armaParameters,
constantRequired, constantEstimate){
if(modelDo!="use"){
# Fill in the matrices
adamElements <- filler(B,
etsModel, Etype, Ttype, Stype, modelIsTrendy, modelIsSeasonal,
componentsNumberETS, componentsNumberETSNonSeasonal,
componentsNumberETSSeasonal, componentsNumberARIMA,
lags, lagsModel, lagsModelMax,
matVt, matWt, matF, vecG,
persistenceEstimate, persistenceLevelEstimate, persistenceTrendEstimate,
persistenceSeasonalEstimate, persistenceXregEstimate,
phiEstimate,
initialType, initialEstimate,
initialLevelEstimate, initialTrendEstimate, initialSeasonalEstimate,
initialArimaEstimate, initialXregEstimate,
arimaModel, arEstimate, maEstimate, arOrders, iOrders, maOrders,
arRequired, maRequired, armaParameters,
nonZeroARI, nonZeroMA, arimaPolynomials,
xregModel, xregNumber,
xregParametersMissing, xregParametersIncluded,
xregParametersEstimated, xregParametersPersistence, constantEstimate);
list2env(adamElements, environment());
}
# Write down phi
if(phiEstimate){
phi[] <- B[names(B)=="phi"];
}
# Write down the initials in the recent profile
profilesRecentTable[] <- matVt[,1:lagsModelMax];
profilesRecentInitial <- matVt[,1:lagsModelMax, drop=FALSE];
# Fit the model to the data
adamFitted <- adamFitterWrap(matVt, matWt, matF, vecG,
lagsModelAll, indexLookupTable, profilesRecentTable,
Etype, Ttype, Stype, componentsNumberETS, componentsNumberETSSeasonal,
componentsNumberARIMA, xregNumber, constantRequired,
yInSample, ot, any(initialType==c("complete","backcasting")),
nIterations);
matVt[] <- adamFitted$matVt;
# Write down the recent profile for future use
profilesRecentTable <- adamFitted$profile;
# Make sure that there are no negative values in multiplicative components
# This might appear in case of bounds="a"
if(Ttype=="M" && (any(is.na(matVt[2,])) || any(matVt[2,]<=0))){
i <- which(any(matVt[2,]<=0));
matVt[2,i] <- 1e-6;
profilesRecentTable[2,i] <- 1e-6;
}
if(Stype=="M" && all(!is.na(matVt[componentsNumberETSNonSeasonal+1:componentsNumberETSSeasonal,])) &&
any(matVt[componentsNumberETSNonSeasonal+1:componentsNumberETSSeasonal,]<=0)){
i <- which(matVt[componentsNumberETSNonSeasonal+1:componentsNumberETSSeasonal,]<=0);
matVt[componentsNumberETSNonSeasonal+1:componentsNumberETSSeasonal,i] <- 1e-6;
i <- which(profilesRecentTable[componentsNumberETSNonSeasonal+1:componentsNumberETSSeasonal,]<=0);
profilesRecentTable[componentsNumberETSNonSeasonal+1:componentsNumberETSSeasonal,i] <- 1e-6;
}
# Prepare fitted and error with ts / zoo
if(any(yClasses=="ts")){
yFitted <- ts(rep(NA,obsInSample), start=yStart, frequency=yFrequency);
errors <- ts(rep(NA,obsInSample), start=yStart, frequency=yFrequency);
}
else{
yFitted <- zoo(rep(NA,obsInSample), order.by=yInSampleIndex);
errors <- zoo(rep(NA,obsInSample), order.by=yInSampleIndex);
}
errors[] <- adamFitted$errors;
yFitted[] <- adamFitted$yFitted;
# Check what was returned in the end
if(any(is.nan(yFitted)) || any(is.na(yFitted))){
warning("Something went wrong in the estimation of the model and NaNs were produced. ",
"If this is a mixed model, consider using the pure ones instead.",
call.=FALSE, immediate.=TRUE);
}
if(occurrenceModel){
yFitted[] <- yFitted * pFitted;
}
# Fix the cases, when we have zeroes in the provided occurrence
if(occurrence=="provided"){
yFitted[!otLogical] <- yFitted[!otLogical] * pFitted[!otLogical];
}
# Produce forecasts if the horizon is non-zero
if(horizon>0){
if(any(yClasses=="ts")){
yForecast <- ts(rep(NA, horizon), start=yForecastStart, frequency=yFrequency);
}
else{
yForecast <- zoo(rep(NA, horizon), order.by=yForecastIndex);
}
yForecast[] <- adamForecasterWrap(tail(matWt,horizon), matF,
lagsModelAll,
indexLookupTable[,lagsModelMax+obsInSample+c(1:horizon),drop=FALSE],
profilesRecentTable,
Etype, Ttype, Stype,
componentsNumberETS, componentsNumberETSSeasonal,
componentsNumberARIMA, xregNumber, constantRequired,
horizon);
#### Make safety checks
# If there are NaN values
if(any(is.nan(yForecast))){
yForecast[is.nan(yForecast)] <- 0;
}
# Amend forecasts, multiplying by probability
if(occurrenceModel && !occurrenceModelProvided){
yForecast[] <- yForecast * c(suppressWarnings(forecast(oesModel, h=h))$mean);
}
else if((occurrenceModel && occurrenceModelProvided) || occurrence=="provided"){
yForecast[] <- yForecast * pForecast;
}
}
else{
if(any(yClasses=="ts")){
yForecast <- ts(NA, start=yForecastStart, frequency=yFrequency);
}
else{
yForecast <- zoo(rep(NA, horizon), order.by=yForecastIndex);
}
}
# If the distribution is default, change it according to the error term
if(distribution=="default"){
distribution[] <- switch(loss,
"likelihood"= switch(Etype, "A"= "dnorm", "M"= "dgamma"),
"MAEh"=, "MACE"=, "MAE"= "dlaplace",
"HAMh"=, "CHAM"=, "HAM"= "ds",
"MSEh"=, "MSCE"=, "MSE"=, "GPL"=, "dnorm");
}
#### Initial values to return ####
initialValue <- vector("list", etsModel*(1+modelIsTrendy+modelIsSeasonal)+arimaModel+xregModel);
initialValueETS <- vector("list", etsModel*length(lagsModel));
initialValueNames <- vector("character", etsModel*(1+modelIsTrendy+modelIsSeasonal)+arimaModel+xregModel);
# The vector that defines what was estimated in the model
initialEstimated <- vector("logical", etsModel*(1+modelIsTrendy+modelIsSeasonal*componentsNumberETSSeasonal)+
arimaModel+xregModel);
# Write down the initials of ETS
j <- 0;
if(etsModel){
# Write down level, trend and seasonal
for(i in 1:length(lagsModel)){
# In case of level / trend, we want to get the very first value
if(lagsModel[i]==1){
initialValueETS[[i]] <- head(matVt[i,1:lagsModelMax],1);
}
# In cases of seasonal components, they should be at the end of the pre-heat period
else{
initialValueETS[[i]] <- tail(matVt[i,1:lagsModelMax],lagsModel[i]);
}
}
j[] <- j+1;
# Write down level in the final list
initialEstimated[j] <- initialLevelEstimate;
initialValue[[j]] <- initialValueETS[[j]];
initialValueNames[j] <- c("level");
names(initialEstimated)[j] <- initialValueNames[j];
if(modelIsTrendy){
j[] <- 2;
initialEstimated[j] <- initialTrendEstimate;
# Write down trend in the final list
initialValue[[j]] <- initialValueETS[[j]];
# Remove the trend from ETS list
initialValueETS[[j]] <- NULL;
initialValueNames[j] <- c("trend");
names(initialEstimated)[j] <- initialValueNames[j];
}
# Write down the initial seasonals
if(modelIsSeasonal){
initialEstimated[j+c(1:componentsNumberETSSeasonal)] <- initialSeasonalEstimate;
# Remove the level from ETS list
initialValueETS[[1]] <- NULL;
j[] <- j+1;
if(length(initialSeasonalEstimate)>1){
initialValue[[j]] <- initialValueETS;
initialValueNames[[j]] <- "seasonal";
names(initialEstimated)[j+0:(componentsNumberETSSeasonal-1)] <-
paste0(initialValueNames[j],c(1:componentsNumberETSSeasonal));
}
else{
initialValue[[j]] <- initialValueETS[[1]];
initialValueNames[[j]] <- "seasonal";
names(initialEstimated)[j] <- initialValueNames[j];
}
}
}
# Write down the ARIMA initials
if(arimaModel){
j[] <- j+1;
initialEstimated[j] <- initialArimaEstimate;
if(initialArimaEstimate){
initialValue[[j]] <- head(matVt[componentsNumberETS+componentsNumberARIMA,],initialArimaNumber);
# Fix the values to get proper initials, not just the values of states
if(tail(arimaPolynomials$ariPolynomial,1)!=0){
initialValue[[j]] <- switch(Etype,
"A"=initialValue[[j]] / tail(arimaPolynomials$ariPolynomial,1),
"M"=exp(log(initialValue[[j]]) / tail(arimaPolynomials$ariPolynomial,1)));
# initialValue[[j]] <- initialValue[[j]] / tail(arimaPolynomials$ariPolynomial,1);
}
}
else{
initialValue[[j]] <- initialArima;
}
initialValueNames[j] <- "arima";
names(initialEstimated)[j] <- initialValueNames[j];
}
# Write down the xreg initials
if(xregModel){
j[] <- j+1;
initialEstimated[j] <- initialXregEstimate;
initialValue[[j]] <- matVt[componentsNumberETS+componentsNumberARIMA+1:xregNumber,lagsModelMax];
initialValueNames[j] <- "xreg";
names(initialEstimated)[j] <- initialValueNames[j];
}
names(initialValue) <- initialValueNames;
#### Persistence to return ####
persistence <- as.vector(vecG);
names(persistence) <- rownames(vecG);
# Remove xreg persistence from the returned vector
if(xregModel && regressors!="adapt"){
# persistence <- persistence[substr(names(persistence),1,5)!="delta"];
# We've selected the variables, so there's nothing to select anymore
regressors <- "use";
}
else if(!xregModel){
regressors <- NULL;
}
if(arimaModel){
armaParametersList <- vector("list",arRequired+maRequired);
j[] <- 1;
if(arRequired && arEstimate){
# Avoid damping parameter phi
armaParametersList[[j]] <- B[nchar(names(B))>3 & substr(names(B),1,3)=="phi"];
names(armaParametersList)[j] <- "ar";
j[] <- j+1;
}
# If this was provided
else if(arRequired && !arEstimate){
# Avoid damping parameter phi
armaParametersList[[j]] <- armaParameters[substr(names(armaParameters),1,3)=="phi"];
names(armaParametersList)[j] <- "ar";
j[] <- j+1;
}
if(maRequired && maEstimate){
armaParametersList[[j]] <- B[substr(names(B),1,5)=="theta"];
names(armaParametersList)[j] <- "ma";
}
else if(maRequired && !maEstimate){
armaParametersList[[j]] <- armaParameters[substr(names(armaParameters),1,5)=="theta"];
names(armaParametersList)[j] <- "ma";
}
}
else{
armaParametersList <- NULL;
}
if(any(distribution==c("dalaplace","dgnorm","dlgnorm","dt")) && otherParameterEstimate){
other <- abs(tail(B,1));
}
# which() is needed in order to overcome weird behaviour of zoo
scale <- scaler(distribution, Etype, errors[which(otLogical)], yFitted[which(otLogical)], obsInSample, other);
# Record constant if it was estimated
if(constantEstimate){
constantValue <- B[constantName];
}
# Prepare the list of distribution parameters to return
otherReturned <- vector("list",1);
# Write down parameters for distribution. It is always positive, so take abs
if(otherParameterEstimate){
otherReturned[[1]] <- abs(tail(B,1));
}
else{
otherReturned[[1]] <- other;
}
# Give names to the other values
if(distribution=="dalaplace"){
names(otherReturned) <- "alpha";
}
else if(any(distribution==c("dgnorm","dlgnorm"))){
names(otherReturned) <- "shape";
}
else if(any(distribution==c("dt"))){
names(otherReturned) <- "nu";
}
# LASSO / RIDGE lambda
if(any(loss==c("LASSO","RIDGE"))){
otherReturned$lambda <- lambda;
}
# Return ARIMA polynomials and indices for persistence and transition
if(arimaModel){
otherReturned$polynomial <- arimaPolynomials;
otherReturned$ARIMAIndices <- list(nonZeroARI=nonZeroARI,nonZeroMA=nonZeroMA);
otherReturned$arPolynomialMatrix <- matrix(0, arOrders %*% lags, arOrders %*% lags);
if(nrow(otherReturned$arPolynomialMatrix)>1){
otherReturned$arPolynomialMatrix[2:nrow(otherReturned$arPolynomialMatrix)-1,
2:nrow(otherReturned$arPolynomialMatrix)] <-
diag(nrow(otherReturned$arPolynomialMatrix)-1);
if(arRequired){
otherReturned$arPolynomialMatrix[,1] <- -arimaPolynomials$arPolynomial[-1];
}
}
otherReturned$armaParameters <- armaParameters;
}
# Amend the class of state matrix
if(any(yClasses=="ts")){
matVt <- ts(t(matVt), start=(time(y)[1]-deltat(y)*lagsModelMax), frequency=yFrequency);
}
else{
yStatesIndex <- yInSampleIndex[1] - lagsModelMax*diff(tail(yInSampleIndex,2)) +
c(1:lagsModelMax-1)*diff(tail(yInSampleIndex,2));
yStatesIndex <- c(yStatesIndex, yInSampleIndex);
matVt <- zoo(t(matVt), order.by=yStatesIndex);
}
parametersNumber[2,5] <- sum(parametersNumber[2,1:4]);
return(list(model=NA, timeElapsed=NA,
data=cbind(NA,xregData), holdout=NULL, fitted=yFitted, residuals=errors,
forecast=yForecast, states=matVt,
profile=profilesRecentTable, profileInitial=profilesRecentInitial,
persistence=persistence, phi=phi, transition=matF,
measurement=matWt, initial=initialValue, initialType=initialType,
initialEstimated=initialEstimated, orders=orders, arma=armaParametersList,
constant=constantValue, nParam=parametersNumber, occurrence=oesModel,
formula=formula, regressors=regressors,
loss=loss, lossValue=CFValue, logLik=logLikADAMValue, distribution=distribution,
scale=scale, other=otherReturned, B=B, lags=lags, lagsAll=lagsModelAll, res=res, FI=FI));
}
#### Deal with occurrence model ####
if(occurrenceModel && !occurrenceModelProvided){
modelForOES <- model;
if(model=="NNN"){
modelForOES[] <- "MNN";
}
oesModel <- suppressWarnings(oes(ot, model=modelForOES, occurrence=occurrence, ic=ic, h=horizon,
holdout=FALSE, bounds="usual", xreg=xregData, regressors=regressors, silent=TRUE));
pFitted[] <- fitted(oesModel);
parametersNumber[1,3] <- nparam(oesModel);
# print(oesModel)
# This should not happen, but just in case...
if(oesModel$occurrence=="n"){
occurrence <- "n";
otLogical <- rep(TRUE,obsInSample);
occurrenceModel <- FALSE;
ot <- matrix(otLogical*1,ncol=1);
obsNonzero <- sum(ot);
obsZero <- obsInSample - obsNonzero;
Etype[] <- switch(Etype,
"M"="A",
"Y"=,
"Z"="X",
Etype);
Ttype[] <- switch(Ttype,
"M"="A",
"Y"=,
"Z"="X",
Ttype);
Stype[] <- switch(Stype,
"M"="A",
"Y"=,
"Z"="X",
Stype);
}
}
else if(occurrenceModel && occurrenceModelProvided){
parametersNumber[2,3] <- nparam(oesModel);
}
xregDataOriginal <- xregData;
##### Prepare stuff for the variables selection if regressors="select" #####
if(regressors=="select"){
# First, record the original parameters
xregExistOriginal <- xregModel;
initialXregsProvidedOriginal <- initialXregProvided;
initialXregEstimateOriginal <- initialXregEstimate;
persistenceXregOriginal <- persistenceXreg;
persistenceXregProvidedOriginal <- persistenceXregProvided;
persistenceXregEstimateOriginal <- persistenceXregEstimate;
xregModelOriginal <- xregModelInitials;
xregNumberOriginal <- xregNumber;
xregNamesOriginal <- xregNames;
# Set the parameters to zero and do simple ETS
xregModel[] <- FALSE;
initialXregProvided <- FALSE;
initialXregEstimate[] <- FALSE;
persistenceXreg <- 0;
persistenceXregProvided <- FALSE;
persistenceXregEstimate[] <- FALSE;
xregData <- NULL;
xregNumber[] <- 0;
xregNames <- NULL;
}
##### Estimate the specified model #####
if(modelDo=="estimate"){
# If this is LASSO/RIDGE with lambda=1, use MSE to estimate initials only
lambdaOriginal <- lambda;
if(any(loss==c("LASSO","RIDGE")) && lambda==1){
if(etsModel){
# Pre-set ETS parameters
persistenceEstimate[] <- FALSE;
persistenceLevelEstimate[] <- persistenceTrendEstimate[] <-
persistenceSeasonalEstimate[] <- FALSE;
persistenceLevel <- persistenceTrend <- persistenceSeasonal <- 0;
# Phi
phiEstimate[] <- FALSE;
phi <- 1;
}
if(xregModel){
# ETSX parameters
persistenceXregEstimate[] <- FALSE;
persistenceXreg <- 0;
}
if(arimaModel){
# Pre-set ARMA parameters
arEstimate[] <- FALSE;
maEstimate[] <- FALSE;
armaParameters <- vector("numeric",sum(arOrders)+sum(maOrders));
j <- 0;
for(i in 1:length(lags)){
if(arOrders[i]>0){
armaParameters[j+1:arOrders[i]] <- 1;
names(armaParameters)[j+c(1:arOrders[i])] <- paste0("phi",1:arOrders[i],"[",lags[i],"]");
j <- j + arOrders[i];
}
if(maOrders[i]>0){
armaParameters[j+1:maOrders[i]] <- 0;
names(armaParameters)[j+c(1:maOrders[i])] <- paste0("theta",1:maOrders[i],"[",lags[i],"]");
j <- j + maOrders[i];
}
}
}
lambda <- 0;
}
# Estimate the parameters of the demand sizes model
adamEstimated <- estimator(etsModel, Etype, Ttype, Stype, lags, lagsModelSeasonal, lagsModelARIMA,
obsStates, obsInSample,
yInSample, persistence, persistenceEstimate,
persistenceLevel, persistenceLevelEstimate,
persistenceTrend, persistenceTrendEstimate,
persistenceSeasonal, persistenceSeasonalEstimate,
persistenceXreg, persistenceXregEstimate, persistenceXregProvided,
phi, phiEstimate,
initialType, initialLevel, initialTrend, initialSeasonal,
initialArima, initialEstimate,
initialLevelEstimate, initialTrendEstimate, initialSeasonalEstimate,
initialArimaEstimate, initialXregEstimate, initialXregProvided,
arimaModel, arRequired, iRequired, maRequired, armaParameters,
componentsNumberARIMA, componentsNamesARIMA,
formula, xregModel, xregModelInitials, xregData, xregNumber, xregNames, regressors,
xregParametersMissing, xregParametersIncluded,
xregParametersEstimated, xregParametersPersistence,
constantRequired, constantEstimate, constantValue, constantName,
ot, otLogical, occurrenceModel, pFitted,
bounds, loss, lossFunction, distribution,
horizon, multisteps, other, otherParameterEstimate, lambda);
list2env(adamEstimated, environment());
# A fix for the special case of lambda==1
lambda <- lambdaOriginal;
#### This part is needed in order for the filler to do its job later on
# Create the basic variables based on the estimated model
adamArchitect <- architector(etsModel, Etype, Ttype, Stype, lags, lagsModelSeasonal,
xregNumber, obsInSample, initialType,
arimaModel, lagsModelARIMA, xregModel, constantRequired,
profilesRecentTable, profilesRecentProvided);
list2env(adamArchitect, environment());
# Create the matrices for the specific ETS model
adamCreated <- creator(etsModel, Etype, Ttype, Stype, modelIsTrendy, modelIsSeasonal,
lags, lagsModel, lagsModelARIMA, lagsModelAll, lagsModelMax,
profilesRecentTable, profilesRecentProvided,
obsStates, obsInSample, obsAll, componentsNumberETS, componentsNumberETSSeasonal,
componentsNamesETS, otLogical, yInSample,
persistence, persistenceEstimate,
persistenceLevel, persistenceLevelEstimate, persistenceTrend, persistenceTrendEstimate,
persistenceSeasonal, persistenceSeasonalEstimate,
persistenceXreg, persistenceXregEstimate, persistenceXregProvided,
phi,
initialType, initialEstimate,
initialLevel, initialLevelEstimate, initialTrend, initialTrendEstimate,
initialSeasonal, initialSeasonalEstimate,
initialArima, initialArimaEstimate, initialArimaNumber,
initialXregEstimate, initialXregProvided,
arimaModel, arRequired, iRequired, maRequired, armaParameters,
arOrders, iOrders, maOrders,
componentsNumberARIMA, componentsNamesARIMA,
xregModel, xregModelInitials, xregData, xregNumber, xregNames,
xregParametersPersistence,
constantRequired, constantEstimate, constantValue, constantName);
list2env(adamCreated, environment());
icSelection <- icFunction(adamEstimated$logLikADAMValue);
####!!! If the occurrence is auto, then compare this with the model with no occurrence !!!####
parametersNumber[1,1] <- nParamEstimated;
if(xregModel){
parametersNumber[1,2] <- sum(xregParametersEstimated)*initialXregEstimate +
max(xregParametersPersistence)*persistenceXregEstimate;
parametersNumber[1,1] <- parametersNumber[1,1] - parametersNumber[1,2];
}
# If we used likelihood, scale was estimated
if((loss=="likelihood")){
parametersNumber[1,4] <- 1;
}
parametersNumber[1,5] <- sum(parametersNumber[1,1:4]);
parametersNumber[2,5] <- sum(parametersNumber[2,1:4]);
}
#### Selection of the best model ####
else if(modelDo=="select"){
adamSelected <- selector(model, modelsPool, allowMultiplicative,
etsModel, Etype, Ttype, Stype, damped, lags,
lagsModelSeasonal, lagsModelARIMA,
obsStates, obsInSample,
yInSample, persistence, persistenceEstimate,
persistenceLevel, persistenceLevelEstimate,
persistenceTrend, persistenceTrendEstimate,
persistenceSeasonal, persistenceSeasonalEstimate,
persistenceXreg, persistenceXregEstimate, persistenceXregProvided,
phi, phiEstimate,
initialType, initialLevel, initialTrend, initialSeasonal,
initialArima, initialEstimate,
initialLevelEstimate, initialTrendEstimate, initialSeasonalEstimate,
initialArimaEstimate, initialXregEstimate, initialXregProvided,
arimaModel, arRequired, iRequired, maRequired, armaParameters,
componentsNumberARIMA, componentsNamesARIMA,
xregModel, xregModelInitials, xregData, xregNumber, xregNames, regressors,
xregParametersMissing, xregParametersIncluded,
xregParametersEstimated, xregParametersPersistence,
constantRequired, constantEstimate, constantValue, constantName,
ot, otLogical, occurrenceModel, pFitted, icFunction,
bounds, loss, lossFunction, distribution,
horizon, multisteps, other, otherParameterEstimate, lambda);
icSelection <- adamSelected$icSelection;
# Take the parameters of the best model
list2env(adamSelected$results[[which.min(icSelection)[1]]], environment());
#### This part is needed in order for the filler to do its job later on
# Create the basic variables based on the estimated model
adamArchitect <- architector(etsModel, Etype, Ttype, Stype, lags, lagsModelSeasonal,
xregNumber, obsInSample, initialType,
arimaModel, lagsModelARIMA, xregModel, constantRequired,
profilesRecentTable, profilesRecentProvided);
list2env(adamArchitect, environment());
# Create the matrices for the specific ETS model
adamCreated <- creator(etsModel, Etype, Ttype, Stype, modelIsTrendy, modelIsSeasonal,
lags, lagsModel, lagsModelARIMA, lagsModelAll, lagsModelMax,
profilesRecentTable, profilesRecentProvided,
obsStates, obsInSample, obsAll, componentsNumberETS, componentsNumberETSSeasonal,
componentsNamesETS, otLogical, yInSample,
persistence, persistenceEstimate,
persistenceLevel, persistenceLevelEstimate, persistenceTrend, persistenceTrendEstimate,
persistenceSeasonal, persistenceSeasonalEstimate,
persistenceXreg, persistenceXregEstimate, persistenceXregProvided,
phi,
initialType, initialEstimate,
initialLevel, initialLevelEstimate, initialTrend, initialTrendEstimate,
initialSeasonal, initialSeasonalEstimate,
initialArima, initialArimaEstimate, initialArimaNumber,
initialXregEstimate, initialXregProvided,
arimaModel, arRequired, iRequired, maRequired, armaParameters,
arOrders, iOrders, maOrders,
componentsNumberARIMA, componentsNamesARIMA,
xregModel, xregModelInitials, xregData, xregNumber, xregNames,
xregParametersPersistence,
constantRequired, constantEstimate, constantValue, constantName);
list2env(adamCreated, environment());
parametersNumber[1,1] <- nParamEstimated;
if(xregModel){
parametersNumber[1,2] <- xregNumber*initialXregEstimate + xregNumber*persistenceXregEstimate;
parametersNumber[1,1] <- parametersNumber[1,1] - parametersNumber[1,2];
}
# If we used likelihood, scale was estimated
if((loss=="likelihood")){
parametersNumber[1,4] <- 1;
}
parametersNumber[1,5] <- sum(parametersNumber[1,1:4]);
parametersNumber[2,5] <- sum(parametersNumber[2,1:4]);
}
#### Combination of models ####
else if(modelDo=="combine"){
modelOriginal <- model;
# If the pool is not provided, then create one
if(is.null(modelsPool)){
# Define the whole pool of errors
if(!allowMultiplicative){
poolErrors <- c("A");
poolTrends <- c("N","A","Ad");
poolSeasonals <- c("N","A");
}
else{
poolErrors <- c("A","M");
poolTrends <- c("N","A","Ad","M","Md");
poolSeasonals <- c("N","A","M");
}
# Some preparation variables
# If Etype is not Z, then check on additive errors
if(Etype!="Z"){
poolErrors <- switch(Etype,
"N"="N",
"A"=,
"X"="A",
"M"=,
"Y"="M");
}
# If Ttype is not Z, then create a pool with specified type
if(Ttype!="Z"){
poolTrends <- switch(Ttype,
"N"="N",
"A"=ifelse(damped,"Ad","A"),
"M"=ifelse(damped,"Md","M"),
"X"=c("N","A","Ad"),
"Y"=c("N","M","Md"));
}
# If Stype is not Z, then crete specific pools
if(Stype!="Z"){
poolSeasonals <- switch(Stype,
"N"="N",
"A"="A",
"X"=c("N","A"),
"M"="M",
"Y"=c("N","M"));
}
modelsPool <- paste0(rep(poolErrors,length(poolTrends)*length(poolSeasonals)),
rep(poolTrends,each=length(poolSeasonals)),
rep(poolSeasonals,length(poolTrends)));
}
adamSelected <- selector(model, modelsPool, allowMultiplicative,
etsModel, Etype, Ttype, Stype, damped, lags,
lagsModelSeasonal, lagsModelARIMA,
obsStates, obsInSample,
yInSample, persistence, persistenceEstimate,
persistenceLevel, persistenceLevelEstimate,
persistenceTrend, persistenceTrendEstimate,
persistenceSeasonal, persistenceSeasonalEstimate,
persistenceXreg, persistenceXregEstimate, persistenceXregProvided,
phi, phiEstimate,
initialType, initialLevel, initialTrend, initialSeasonal,
initialArima, initialEstimate,
initialLevelEstimate, initialTrendEstimate, initialSeasonalEstimate,
initialArimaEstimate, initialXregEstimate, initialXregProvided,
arimaModel, arRequired, iRequired, maRequired, armaParameters,
componentsNumberARIMA, componentsNamesARIMA,
xregModel, xregModelInitials, xregData, xregNumber, xregNames, regressors,
xregParametersMissing, xregParametersIncluded,
xregParametersEstimated, xregParametersPersistence,
constantRequired, constantEstimate, constantValue, constantName,
ot, otLogical, occurrenceModel, pFitted, icFunction,
bounds, loss, lossFunction, distribution,
horizon, multisteps, other, otherParameterEstimate, lambda);
icSelection <- adamSelected$icSelection;
icBest <- min(icSelection);
adamSelected$icWeights <- (exp(-0.5*(icSelection-icBest)) /
sum(exp(-0.5*(icSelection-icBest))));
# This is a failsafe mechanism, just to make sure that the ridiculous models don't impact forecasts
adamSelected$icWeights[adamSelected$icWeights<1e-5] <- 0
adamSelected$icWeights <- adamSelected$icWeights/sum(adamSelected$icWeights);
# adamArchitect <- vector("list",10)
for(i in 1:length(adamSelected$results)){
# Take the parameters of the best model
list2env(adamSelected$results[[i]], environment());
#### This part is needed in order for the filler to do its job later on
# Create the basic variables based on the estimated model
adamArchitect <- architector(etsModel, Etype, Ttype, Stype, lags, lagsModelSeasonal,
xregNumber, obsInSample, initialType,
arimaModel, lagsModelARIMA, xregModel, constantRequired,
profilesRecentTable, profilesRecentProvided);
list2env(adamArchitect, environment());
adamSelected$results[[i]]$modelIsTrendy <- adamArchitect$modelIsTrendy;
adamSelected$results[[i]]$modelIsSeasonal <- adamArchitect$modelIsSeasonal;
adamSelected$results[[i]]$lagsModel <- adamArchitect$lagsModel;
adamSelected$results[[i]]$lagsModelAll <- adamArchitect$lagsModelAll;
adamSelected$results[[i]]$lagsModelMax <- adamArchitect$lagsModelMax;
adamSelected$results[[i]]$profilesRecentTable <- adamArchitect$profilesRecentTable;
adamSelected$results[[i]]$indexLookupTable <- adamArchitect$indexLookupTable;
adamSelected$results[[i]]$componentsNumberETS <- adamArchitect$componentsNumberETS;
adamSelected$results[[i]]$componentsNumberETSSeasonal <- adamArchitect$componentsNumberETSSeasonal;
adamSelected$results[[i]]$componentsNumberETSNonSeasonal <- adamArchitect$componentsNumberETSNonSeasonal;
adamSelected$results[[i]]$componentsNamesETS <- adamArchitect$componentsNamesETS;
# Create the matrices for the specific ETS model
adamCreated <- creator(etsModel, Etype, Ttype, Stype, modelIsTrendy, modelIsSeasonal,
lags, lagsModel, lagsModelARIMA, lagsModelAll, lagsModelMax,
profilesRecentTable, profilesRecentProvided,
obsStates, obsInSample, obsAll, componentsNumberETS, componentsNumberETSSeasonal,
componentsNamesETS, otLogical, yInSample,
persistence, persistenceEstimate,
persistenceLevel, persistenceLevelEstimate, persistenceTrend, persistenceTrendEstimate,
persistenceSeasonal, persistenceSeasonalEstimate,
persistenceXreg, persistenceXregEstimate, persistenceXregProvided,
phi,
initialType, initialEstimate,
initialLevel, initialLevelEstimate, initialTrend, initialTrendEstimate,
initialSeasonal, initialSeasonalEstimate,
initialArima, initialArimaEstimate, initialArimaNumber,
initialXregEstimate, initialXregProvided,
arimaModel, arRequired, iRequired, maRequired, armaParameters,
arOrders, iOrders, maOrders,
componentsNumberARIMA, componentsNamesARIMA,
xregModel, xregModelInitials, xregData, xregNumber, xregNames,
xregParametersPersistence,
constantRequired, constantEstimate, constantValue, constantName);
adamSelected$results[[i]]$matVt <- adamCreated$matVt;
adamSelected$results[[i]]$matWt <- adamCreated$matWt;
adamSelected$results[[i]]$matF <- adamCreated$matF;
adamSelected$results[[i]]$vecG <- adamCreated$vecG;
adamSelected$results[[i]]$arimaPolynomials <- adamCreated$arimaPolynomials;
parametersNumber[1,1] <- adamSelected$results[[i]]$nParamEstimated;
if(xregModel){
parametersNumber[1,2] <- xregNumber*initialXregEstimate + xregNumber*persistenceXregEstimate;
}
# If we used likelihood, scale was estimated
if((loss=="likelihood")){
parametersNumber[1,4] <- 1;
}
parametersNumber[1,5] <- sum(parametersNumber[1,1:4]);
parametersNumber[2,5] <- sum(parametersNumber[2,1:4]);
adamSelected$results[[i]]$parametersNumber <- parametersNumber;
}
}
#### Use the provided model ####
else if(modelDo=="use"){
# If the distribution is default, change it according to the error term
if(distribution=="default"){
distributionNew <- switch(loss,
"likelihood"= switch(Etype, "A"= "dnorm", "M"= "dgamma"),
"MAEh"=, "MACE"=, "MAE"= "dlaplace",
"HAMh"=, "CHAM"=, "HAM"= "ds",
"MSEh"=, "MSCE"=, "MSE"=, "GPL"=, "dnorm");
}
else{
distributionNew <- distribution;
}
# Create the basic variables
adamArchitect <- architector(etsModel, Etype, Ttype, Stype, lags, lagsModelSeasonal,
xregNumber, obsInSample, initialType,
arimaModel, lagsModelARIMA, xregModel, constantRequired,
profilesRecentTable, profilesRecentProvided);
list2env(adamArchitect, environment());
# Create the matrices for the specific ETS model
adamCreated <- creator(etsModel, Etype, Ttype, Stype, modelIsTrendy, modelIsSeasonal,
lags, lagsModel, lagsModelARIMA, lagsModelAll, lagsModelMax,
profilesRecentTable, profilesRecentProvided,
obsStates, obsInSample, obsAll, componentsNumberETS, componentsNumberETSSeasonal,
componentsNamesETS, otLogical, yInSample,
persistence, persistenceEstimate,
persistenceLevel, persistenceLevelEstimate, persistenceTrend, persistenceTrendEstimate,
persistenceSeasonal, persistenceSeasonalEstimate,
persistenceXreg, persistenceXregEstimate, persistenceXregProvided,
phi,
initialType, initialEstimate,
initialLevel, initialLevelEstimate, initialTrend, initialTrendEstimate,
initialSeasonal, initialSeasonalEstimate,
initialArima, initialArimaEstimate, initialArimaNumber,
initialXregEstimate, initialXregProvided,
arimaModel, arRequired, iRequired, maRequired, armaParameters,
arOrders, iOrders, maOrders,
componentsNumberARIMA, componentsNamesARIMA,
xregModel, xregModelInitials, xregData, xregNumber, xregNames,
xregParametersPersistence,
constantRequired, constantEstimate, constantValue, constantName);
list2env(adamCreated, environment());
# Prepare the denominator needed for the shrinkage of explanatory variables in LASSO / RIDGE
if(xregNumber>0 && any(loss==c("LASSO","RIDGE"))){
denominator <- apply(matWt, 2, sd);
denominator[is.infinite(denominator)] <- 1;
yDenominator <- max(sd(diff(yInSample)),1);
}
else{
denominator <- NULL;
yDenominator <- NULL;
}
CFValue <- CF(B=0, etsModel=etsModel, Etype=Etype, Ttype=Ttype, Stype=Stype, modelIsTrendy=modelIsTrendy,
modelIsSeasonal=modelIsSeasonal, yInSample=yInSample,
ot=ot, otLogical=otLogical, occurrenceModel=occurrenceModel, obsInSample=obsInSample,
componentsNumberETS=componentsNumberETS, componentsNumberETSSeasonal=componentsNumberETSSeasonal,
componentsNumberETSNonSeasonal=componentsNumberETSNonSeasonal,
componentsNumberARIMA=componentsNumberARIMA,
lags=lags, lagsModel=lagsModel, lagsModelAll=lagsModelAll, lagsModelMax=lagsModelMax,
indexLookupTable=indexLookupTable, profilesRecentTable=profilesRecentTable,
matVt=matVt, matWt=matWt, matF=matF, vecG=vecG,
persistenceEstimate=persistenceEstimate,
persistenceLevelEstimate=persistenceLevelEstimate,
persistenceTrendEstimate=persistenceTrendEstimate,
persistenceSeasonalEstimate=persistenceSeasonalEstimate,
persistenceXregEstimate=persistenceXregEstimate,
phiEstimate=phiEstimate, initialType=initialType,
initialEstimate=initialEstimate, initialLevelEstimate=initialLevelEstimate,
initialTrendEstimate=initialTrendEstimate, initialSeasonalEstimate=initialSeasonalEstimate,
initialArimaEstimate=initialArimaEstimate, initialXregEstimate=initialXregEstimate,
arimaModel=arimaModel, nonZeroARI=nonZeroARI, nonZeroMA=nonZeroMA,
arimaPolynomials=arimaPolynomials,
arEstimate=arEstimate, maEstimate=maEstimate,
arOrders=arOrders, iOrders=iOrders, maOrders=maOrders,
arRequired=arRequired, maRequired=maRequired, armaParameters=armaParameters,
xregModel=xregModel, xregNumber=xregNumber,
xregParametersMissing=xregParametersMissing,
xregParametersIncluded=xregParametersIncluded,
xregParametersEstimated=xregParametersEstimated,
xregParametersPersistence=xregParametersPersistence,
constantRequired=constantRequired, constantEstimate=constantEstimate,
bounds=bounds, loss=loss, lossFunction=lossFunction, distribution=distributionNew,
horizon=horizon, multisteps=multisteps,
denominator=denominator, yDenominator=yDenominator,
other=other, otherParameterEstimate=otherParameterEstimate, lambda=lambda,
arPolynomialMatrix=NULL, maPolynomialMatrix=NULL);
parametersNumber[1,1] <- parametersNumber[1,5] <- 1;
logLikADAMValue <- structure(logLikADAM(B=0,
etsModel, Etype, Ttype, Stype, modelIsTrendy, modelIsSeasonal, yInSample,
ot, otLogical, occurrenceModel, pFitted, obsInSample,
componentsNumberETS, componentsNumberETSSeasonal, componentsNumberETSNonSeasonal,
componentsNumberARIMA,
lags, lagsModel, lagsModelAll, lagsModelMax,
indexLookupTable, profilesRecentTable,
matVt, matWt, matF, vecG,
persistenceEstimate, persistenceLevelEstimate, persistenceTrendEstimate,
persistenceSeasonalEstimate, persistenceXregEstimate,
phiEstimate, initialType, initialEstimate,
initialLevelEstimate, initialTrendEstimate, initialSeasonalEstimate,
initialArimaEstimate, initialXregEstimate,
arimaModel, nonZeroARI, nonZeroMA, arEstimate, maEstimate, arimaPolynomials,
arOrders, iOrders, maOrders, arRequired, maRequired, armaParameters,
xregModel, xregNumber,
xregParametersMissing, xregParametersIncluded,
xregParametersEstimated, xregParametersPersistence,
constantRequired, constantEstimate,
bounds, loss, lossFunction, distributionNew, horizon,
multisteps, denominator, yDenominator, other, otherParameterEstimate, lambda,
arPolynomialMatrix=NULL, maPolynomialMatrix=NULL)
,nobs=obsInSample,df=parametersNumber[1,5],class="logLik")
icSelection <- icFunction(logLikADAMValue);
# If Fisher Information is required, do that analytically
if(FI){
# If B is not provided, then use the standard thing
if(is.null(B)){
BValues <- initialiser(etsModel, Etype, Ttype, Stype, modelIsTrendy, modelIsSeasonal,
componentsNumberETSNonSeasonal, componentsNumberETSSeasonal, componentsNumberETS,
lags, lagsModel, lagsModelSeasonal, lagsModelARIMA, lagsModelMax,
matVt,
TRUE, TRUE, modelIsTrendy, rep(modelIsSeasonal,componentsNumberETSSeasonal), FALSE,
damped, "optimal", TRUE,
TRUE, TRUE, rep(modelIsSeasonal,componentsNumberETSSeasonal),
arimaModel, xregModel,
arimaModel, arRequired, maRequired, arRequired, maRequired, arOrders, maOrders,
componentsNumberARIMA, componentsNamesARIMA, initialArimaNumber,
xregModel, xregNumber,
xregParametersEstimated, xregParametersPersistence,
constantRequired, constantName, FALSE);
# Create the vector of initials for the optimisation
B <- BValues$B;
}
# Reset persistence, just to make sure that there are no duplicates
vecG[] <- 0;
initialTypeFI <- switch(initialType,
"complete"=,
"backcasting"="provided",
initialType);
initialEstimateFI <- FALSE;
# Define parameters just for FI calculation
if(initialTypeFI=="provided"){
initialLevelEstimateFI <- any(names(B)=="level");
initialTrendEstimateFI <- any(names(B)=="trend");
if(any(substr(names(B),1,8)=="seasonal")){
initialSeasonalEstimateFI <- vector("logical", componentsNumberETSSeasonal);
seasonalNames <- names(B)[substr(names(B),1,8)=="seasonal"];
# If there is only one seasonality
if(any(substr(seasonalNames,1,9)=="seasonal_")){
initialSeasonalEstimateFI[] <- TRUE;
}
# If there are several
else{
initialSeasonalEstimateFI[unique(as.numeric(substr(seasonalNames,9,9)))] <- TRUE;
}
}
else{
initialSeasonalEstimateFI <- FALSE;
}
if(arimaModel){
initialArimaEstimateFI <- any(substr(names(B),1,10)=="ARIMAState");
}
else{
initialArimaEstimateFI <- FALSE;
}
if(xregModel){
initialXregEstimateFI <- any(colnames(xregData) %in% names(B));
}
else{
initialXregEstimateFI <- FALSE;
}
initialTypeFI <- "optimal";
initialEstimateFI <- any(c(initialLevelEstimateFI,initialTrendEstimateFI,initialSeasonalEstimateFI,
initialArimaEstimateFI, initialXregEstimateFI));
}
# If smoothing parameters were estimated, then alpha should be in the list
persistenceLevelEstimateFI <- any(names(B)=="alpha");
persistenceTrendEstimateFI <- any(names(B)=="beta");
if(any(substr(names(B),1,5)=="gamma")){
gammas <- (substr(names(B),1,5)=="gamma");
if(sum(gammas)==1){
persistenceSeasonalEstimateFI <- TRUE;
}
else{
persistenceSeasonalEstimateFI <- vector("logical",componentsNumberETSSeasonal);
persistenceSeasonalEstimateFI[as.numeric(substr(names(B),6,6)[gammas])] <- TRUE;
}
}
else{
persistenceSeasonalEstimateFI <- FALSE;
}
persistenceXregEstimateFI <- any(substr(names(B),1,5)=="delta");
persistenceEstimateFI <- any(c(persistenceLevelEstimateFI,persistenceTrendEstimateFI,
persistenceSeasonalEstimateFI,persistenceXregEstimateFI));
phiEstimateFI <- any(names(B)=="phi");
otherParameterEstimateFI <- any(names(B)=="other");
# Stuff for the ARIMA elements
if(arimaModel){
maEstimateFI <- maRequired;
arEstimateFI <- arRequired;
maPolynomialMatrix <- arPolynomialMatrix <- NULL;
}
# This is needed in order to avoid the 1e+300 in the CF
boundsFI <- "none";
FI <- -hessian(logLikADAM, B, etsModel=etsModel, Etype=Etype, Ttype=Ttype, Stype=Stype, modelIsTrendy=modelIsTrendy,
modelIsSeasonal=modelIsSeasonal, yInSample=yInSample,
ot=ot, otLogical=otLogical, occurrenceModel=occurrenceModel, pFitted=pFitted, obsInSample=obsInSample,
componentsNumberETS=componentsNumberETS, componentsNumberETSSeasonal=componentsNumberETSSeasonal,
componentsNumberETSNonSeasonal=componentsNumberETSNonSeasonal,
componentsNumberARIMA=componentsNumberARIMA,
lags=lags, lagsModel=lagsModel, lagsModelAll=lagsModelAll, lagsModelMax=lagsModelMax,
indexLookupTable=indexLookupTable, profilesRecentTable=profilesRecentTable,
matVt=matVt, matWt=matWt, matF=matF, vecG=vecG,
persistenceEstimate=persistenceEstimateFI, persistenceLevelEstimate=persistenceLevelEstimateFI,
persistenceTrendEstimate=persistenceTrendEstimateFI,
persistenceSeasonalEstimate=persistenceSeasonalEstimateFI,
persistenceXregEstimate=persistenceXregEstimateFI,
phiEstimate=phiEstimateFI, initialType=initialTypeFI,
initialEstimate=initialEstimateFI, initialLevelEstimate=initialLevelEstimateFI,
initialTrendEstimate=initialTrendEstimateFI, initialSeasonalEstimate=initialSeasonalEstimateFI,
initialArimaEstimate=initialArimaEstimateFI, initialXregEstimate=initialXregEstimateFI,
arimaModel=arimaModel, nonZeroARI=nonZeroARI, nonZeroMA=nonZeroMA,
arEstimate=arEstimateFI, maEstimate=maEstimateFI, arimaPolynomials=arimaPolynomials,
arOrders=arOrders, iOrders=iOrders, maOrders=maOrders,
arRequired=arRequired, maRequired=maRequired, armaParameters=armaParameters,
xregModel=xregModel, xregNumber=xregNumber,
xregParametersMissing=xregParametersMissing,
xregParametersIncluded=xregParametersIncluded,
xregParametersEstimated=xregParametersEstimated,
xregParametersPersistence=xregParametersPersistence,
constantRequired=constantRequired, constantEstimate=constantRequired,
bounds=boundsFI, loss=loss, lossFunction=lossFunction, distribution=distribution,
horizon=horizon, multisteps=multisteps,
denominator=denominator, yDenominator=yDenominator,
other=other, otherParameterEstimate=otherParameterEstimateFI, lambda=lambda,
arPolynomialMatrix=arPolynomialMatrix, maPolynomialMatrix=maPolynomialMatrix,
hessianCalculation=FALSE,h=stepSize);
colnames(FI) <- names(B);
rownames(FI) <- names(B);
}
else{
FI <- NULL;
}
res <- NULL;
}
# Transform everything into appropriate classes
if(any(yClasses=="ts")){
yInSample <- ts(yInSample,start=yStart, frequency=yFrequency);
if(holdout){
yHoldout <- ts(as.matrix(yHoldout), start=yForecastStart, frequency=yFrequency);
}
}
else{
yInSample <- zoo(yInSample, order.by=yInSampleIndex);
if(holdout){
yHoldout <- zoo(as.matrix(yHoldout), order.by=yForecastIndex);
}
}
#### Prepare the return if we didn't combine anything ####
if(modelDo!="combine"){
modelReturned <- preparator(B, etsModel, Etype, Ttype, Stype,
lagsModel, lagsModelMax, lagsModelAll,
componentsNumberETS, componentsNumberETSSeasonal,
xregNumber, distribution, loss,
persistenceEstimate, persistenceLevelEstimate, persistenceTrendEstimate,
persistenceSeasonalEstimate, persistenceXregEstimate,
phiEstimate, otherParameterEstimate,
initialType, initialEstimate,
initialLevelEstimate, initialTrendEstimate, initialSeasonalEstimate,
initialArimaEstimate, initialXregEstimate,
matVt, matWt, matF, vecG,
occurrenceModel, ot, oesModel,
parametersNumber, CFValue,
arimaModel, arRequired, maRequired,
arEstimate, maEstimate, arOrders, iOrders, maOrders,
nonZeroARI, nonZeroMA,
arimaPolynomials, armaParameters,
constantRequired, constantEstimate);
# Prepare the name of the model
modelName <- "";
if(etsModel){
if(model!="NNN"){
modelName[] <- "ETS";
if(xregModel){
modelName[] <- paste0(modelName,"X");
}
modelName[] <- paste0(modelName,"(",model,")");
if(componentsNumberETSSeasonal>1){
modelName[] <- paste0(modelName,"[",paste0(lags[lags!=1], collapse=", "),"]");
}
}
}
if(arimaModel){
if(etsModel){
modelName[] <- paste0(modelName,"+");
}
# Either the lags are non-seasonal, or there are no orders for seasonal lags
if(all(lags==1) || (all(arOrders[lags>1]==0) && all(iOrders[lags>1]==0) && all(maOrders[lags>1]==0))){
modelName[] <- paste0(modelName,"ARIMA");
if(!etsModel && xregModel){
modelName[] <- paste0(modelName,"X");
}
modelName[] <- paste0(modelName,"(",arOrders[1],",",iOrders[1],",",maOrders[1],")");
}
else{
modelName[] <- paste0(modelName,"SARIMA");
if(!etsModel && xregModel){
modelName[] <- paste0(modelName,"X");
}
for(i in 1:length(arOrders)){
if(all(arOrders[i]==0) && all(iOrders[i]==0) && all(maOrders[i]==0)){
next;
}
modelName[] <- paste0(modelName,"(",arOrders[i],",");
modelName[] <- paste0(modelName,iOrders[i],",");
modelName[] <- paste0(modelName,maOrders[i],")[",lags[i],"]");
}
}
}
if(regressors=="adapt"){
modelName[] <- paste0(modelName,"{D}");
}
if(!etsModel && !arimaModel){
if(model=="NNN"){
modelName[] <- "Constant level";
}
else if(regressors=="adapt"){
modelName[] <- paste0("Dynamic regression");
}
else{
modelName[] <- paste0("Regression");
}
}
else{
if(constantRequired){
modelName[] <- paste0(modelName," with ",constantName);
}
}
if(all(occurrence!=c("n","none"))){
modelName[] <- paste0("i",modelName,
switch(occurrence,
"f"=,"fixed"="[F]",
"d"=,"direct"="[D]",
"o"=,"odds-ratio"="[O]",
"i"=,"invese-odds-ratio"="[I]",
"g"=,"general"="[G]",
""));
}
modelReturned$model <- modelName;
modelReturned$timeElapsed <- Sys.time()-startTime;
if(!is.null(xregData) && !is.null(ncol(data))){
# Remove redundant columns from the data
modelReturned$data <- data[1:obsInSample,,drop=FALSE];
if(holdout){
modelReturned$holdout <- data[obsInSample+c(1:h),,drop=FALSE];
}
# Fix the ts class, which is destroyed during subsetting
if(all(yClasses!="zoo")){
if(is.data.frame(data)){
modelReturned$data[,responseName] <- ts(modelReturned$data[,responseName],
start=yStart, frequency=yFrequency);
if(holdout){
modelReturned$holdout[,responseName] <- ts(modelReturned$holdout[,responseName],
start=yForecastStart, frequency=yFrequency);
}
}
else{
modelReturned$data <- ts(modelReturned$data, start=yStart, frequency=yFrequency);
if(holdout){
modelReturned$holdout <- ts(modelReturned$holdout, start=yForecastStart, frequency=yFrequency);
}
}
}
}
else{
modelReturned$data <- yInSample;
modelReturned$holdout <- yHoldout;
}
if(any(yNAValues)){
modelReturned$data[yNAValues[1:obsInSample],responseName] <- NA;
if(holdout && length(yNAValues)==obsAll){
modelReturned$holdout[yNAValues[-c(1:obsInSample)],responseName] <- NA;
}
modelReturned$residuals[yNAValues[1:obsInSample]] <- NA;
}
class(modelReturned) <- c("adam","smooth");
}
#### Return the combined model ####
else{
modelReturned <- list(models=vector("list",length(adamSelected$results)));
yFittedCombined <- rep(0,obsInSample);
if(h>0){
yForecastCombined <- rep(0,h);
}
else{
yForecastCombined <- NA;
}
parametersNumberOverall <- parametersNumber;
for(i in 1:length(adamSelected$results)){
list2env(adamSelected$results[[i]], environment());
modelReturned$models[[i]] <- preparator(B, etsModel, Etype, Ttype, Stype,
lagsModel, lagsModelMax, lagsModelAll,
componentsNumberETS, componentsNumberETSSeasonal,
xregNumber, distribution, loss,
persistenceEstimate, persistenceLevelEstimate, persistenceTrendEstimate,
persistenceSeasonalEstimate, persistenceXregEstimate,
phiEstimate, otherParameterEstimate,
initialType, initialEstimate,
initialLevelEstimate, initialTrendEstimate, initialSeasonalEstimate,
initialArimaEstimate, initialXregEstimate,
matVt, matWt, matF, vecG,
occurrenceModel, ot, oesModel,
parametersNumber, CFValue,
arimaModel, arRequired, maRequired,
arEstimate, maEstimate, arOrders, iOrders, maOrders,
nonZeroARI, nonZeroMA,
arimaPolynomials, armaParameters,
constantRequired, constantEstimate);
modelReturned$models[[i]]$fitted[is.na(modelReturned$models[[i]]$fitted)] <- 0;
yFittedCombined[] <- yFittedCombined + modelReturned$models[[i]]$fitted * adamSelected$icWeights[i];
if(h>0){
modelReturned$models[[i]]$forecast[is.na(modelReturned$models[[i]]$forecast)] <- 0;
yForecastCombined[] <- yForecastCombined + modelReturned$models[[i]]$forecast * adamSelected$icWeights[i];
}
# Prepare the name of the model
modelName <- "";
if(xregModel){
modelName[] <- "ETSX";
}
else{
modelName[] <- "ETS";
}
modelName[] <- paste0(modelName,"(",model,")");
if(all(occurrence!=c("n","none"))){
modelName[] <- paste0("i",modelName);
}
if(componentsNumberETSSeasonal>1){
modelName[] <- paste0(modelName,"[",paste0(lags[lags!=1], collapse=", "),"]");
}
if(arimaModel){
# Either the lags are non-seasonal, or there are no orders for seasonal lags
if(all(lags==1) || (all(arOrders[lags>1]==0) && all(iOrders[lags>1]==0) && all(maOrders[lags>1]==0))){
modelName[] <- paste0(modelName,"+ARIMA(",arOrders[1],",",iOrders[1],",",maOrders[1],")");
}
else{
modelName[] <- paste0(modelName,"+SARIMA");
for(i in 1:length(arOrders)){
if(all(arOrders[i]==0) && all(iOrders[i]==0) && all(maOrders[i]==0)){
next;
}
modelName[] <- paste0(modelName,"(",arOrders[i],",");
modelName[] <- paste0(modelName,iOrders[i],",");
modelName[] <- paste0(modelName,maOrders[i],")[",lags[i],"]");
}
}
}
if(!etsModel && !arimaModel){
if(model=="NNN"){
modelName[] <- "Constant level";
}
else if(regressors=="adapt"){
modelName[] <- paste0("Dynamic regression");
}
else{
modelName[] <- paste0("Regression");
}
}
else{
if(constantRequired){
modelName[] <- paste0(modelName," with ",constantName);
}
}
if(all(occurrence!=c("n","none"))){
modelName[] <- paste0("i",modelName);
}
modelReturned$models[[i]]$model <- modelName;
modelReturned$models[[i]]$timeElapsed <- Sys.time()-startTime;
parametersNumberOverall[1,1] <- parametersNumber[1,1] + parametersNumber[1,1] * adamSelected$icWeights[i];
if(!is.null(xregData) && !is.null(ncol(data))){
modelReturned$models[[i]]$data <- data[1:obsInSample,,drop=FALSE];
if(holdout){
modelReturned$models[[i]]$holdout <- data[obsInSample+c(1:h),,drop=FALSE];
}
# Fix the ts class, which is destroyed during subsetting
if(all(yClasses!="zoo")){
if(is.data.frame(data)){
modelReturned$models[[i]]$data[,responseName] <- ts(modelReturned$models[[i]]$data[,responseName],
start=yStart, frequency=yFrequency);
if(holdout){
modelReturned$models[[i]]$holdout[,responseName] <- ts(modelReturned$models[[i]]$holdout[,responseName],
start=yForecastStart, frequency=yFrequency);
}
}
else{
modelReturned$models[[i]]$data <- ts(modelReturned$models[[i]]$data, start=yStart, frequency=yFrequency);
if(holdout){
modelReturned$models[[i]]$holdout <- ts(modelReturned$models[[i]]$holdout, start=yForecastStart, frequency=yFrequency);
}
}
}
}
else{
modelReturned$models[[i]]$data <- yInSample;
modelReturned$models[[i]]$holdout <- yHoldout;
}
if(any(yNAValues)){
modelReturned$models[[i]]$data[yNAValues[1:obsInSample],responseName] <- NA;
if(holdout && length(yNAValues)==obsAll){
modelReturned$models[[i]]$holdout[yNAValues[-c(1:obsInSample)],responseName] <- NA;
}
modelReturned$models[[i]]$residuals[yNAValues[1:obsInSample]] <- NA;
}
modelReturned$models[[i]]$call <- cl;
# Amend the call so that each sub-model can be used separately
modelReturned$models[[i]]$call$model <- model;
modelReturned$models[[i]]$bounds <- bounds;
class(modelReturned$models[[i]]) <- c("adam","smooth");
}
names(modelReturned$models) <- names(adamSelected$icWeights);
# Record the original name of the model.
model[] <- modelOriginal;
# Prepare the name of the model
modelName <- "";
if(xregModel){
modelName[] <- "ETSX";
}
else{
modelName[] <- "ETS";
}
modelName[] <- paste0(modelName,"(",model,")");
if(all(occurrence!=c("n","none"))){
modelName[] <- paste0("i",modelName);
}
if(componentsNumberETSSeasonal>1){
modelName[] <- paste0(modelName,"[",paste0(lags[lags!=1], collapse=", "),"]");
}
if(arimaModel){
# Either the lags are non-seasonal, or there are no orders for seasonal lags
if(all(lags==1) || (all(arOrders[lags>1]==0) && all(iOrders[lags>1]==0) && all(maOrders[lags>1]==0))){
modelName[] <- paste0(modelName,"+ARIMA(",arOrders[1],",",iOrders[1],",",maOrders[1],")");
}
else{
modelName[] <- paste0(modelName,"+SARIMA");
for(i in 1:length(arOrders)){
if(all(arOrders[i]==0) && all(iOrders[i]==0) && all(maOrders[i]==0)){
next;
}
modelName[] <- paste0(modelName,"(",arOrders[i],",");
modelName[] <- paste0(modelName,iOrders[i],",");
modelName[] <- paste0(modelName,maOrders[i],")[",lags[i],"]");
}
}
}
if(all(occurrence!=c("n","none"))){
modelName[] <- paste0("i",modelName);
}
modelReturned$model <- modelName;
modelReturned$formula <- as.formula(paste0(responseName,"~."));
modelReturned$timeElapsed <- Sys.time()-startTime;
if(!is.null(xregDataOriginal)){
modelReturned$data <- data[1:obsInSample,,drop=FALSE];
if(holdout){
modelReturned$holdout <- data[obsInSample+c(1:h),,drop=FALSE];
}
# Fix the ts class, which is destroyed during subsetting
if(all(yClasses!="zoo")){
if(is.data.frame(data)){
modelReturned$data[,responseName] <- ts(modelReturned$data[,responseName],
start=yStart, frequency=yFrequency);
if(holdout){
modelReturned$holdout[,responseName] <- ts(modelReturned$holdout[,responseName],
start=yForecastStart, frequency=yFrequency);
}
}
else{
modelReturned$data <- ts(modelReturned$data, start=yStart, frequency=yFrequency);
if(holdout){
modelReturned$holdout <- ts(modelReturned$holdout, start=yForecastStart, frequency=yFrequency);
}
}
}
}
else{
modelReturned$data <- yInSample;
modelReturned$holdout <- yHoldout;
}
modelReturned$fitted <- ts(yFittedCombined,start=yStart, frequency=yFrequency);
modelReturned$residuals <- yInSample - yFittedCombined;
if(any(yNAValues)){
modelReturned$data[yNAValues[1:obsInSample],responseName] <- NA;
if(holdout && length(yNAValues)==obsAll){
modelReturned$holdout[yNAValues[-c(1:obsInSample)],responseName] <- NA;
}
modelReturned$residuals[yNAValues[1:obsInSample]] <- NA;
}
modelReturned$forecast <- ts(yForecastCombined,start=yForecastStart, frequency=yFrequency);
parametersNumberOverall[1,5] <- sum(parametersNumberOverall[1,1:4]);
parametersNumberOverall[2,5] <- sum(parametersNumberOverall[2,1:4]);
modelReturned$nParam <- parametersNumberOverall;
modelReturned$ICw <- adamSelected$icWeights;
# These two are needed just to make basic methods work
modelReturned$distribution <- distribution;
modelReturned$scale <- sqrt(mean(modelReturned$residuals^2,na.rm=TRUE));
class(modelReturned) <- c("adamCombined","adam","smooth");
}
modelReturned$ICs <- icSelection;
modelReturned$lossFunction <- lossFunction;
modelReturned$call <- cl;
modelReturned$bounds <- bounds;
# Error measures if there is a holdout
if(holdout){
modelReturned$accuracy <- measures(yHoldout,modelReturned$forecast,yInSample);
}
if(!silent){
plot(modelReturned, 7);
}
return(modelReturned);
}
#### Small useful ADAM functions ####
# These functions are faster than which() and tail() for vectors are.
# The main gain is in polinomialiser()
# whichFast <- function(x){
# return(c(1:length(x))[x]);
# }
# tailFast <- function(x,...){
# return(x[length(x)]) ;
# }
# This function creates recent profile and the lookup table for adam
#' @importFrom greybox detectdst
adamProfileCreator <- function(lagsModelAll, lagsModelMax, obsAll,
lags=NULL, yIndex=NULL, yClasses=NULL){
# lagsModelAll - all lags used in the model for ETS + ARIMA + xreg
# lagsModelMax - the maximum lag used in the model
# obsAll - number of observations to create
# lags - the original lags provided by user (no lags for ARIMA etc). Needed in order to see
# if weird frequencies are used.
# yIndex - the indices needed in order to get the weird dates.
# yClass - the class used for the actuals. If zoo, magic will happen here.
# Create the matrix with profiles, based on provided lags
profilesRecentTable <- matrix(0,length(lagsModelAll),lagsModelMax,
dimnames=list(lagsModelAll,NULL));
# Create the lookup table
indexLookupTable <- matrix(1,length(lagsModelAll),obsAll+lagsModelMax,
dimnames=list(lagsModelAll,NULL));
# Modify the lookup table in order to get proper indices in C++
profileIndices <- matrix(c(1:(lagsModelMax*length(lagsModelAll))),length(lagsModelAll));
for(i in 1:length(lagsModelAll)){
profilesRecentTable[i,1:lagsModelAll[i]] <- 1:lagsModelAll[i];
# -1 is needed to align this with C++ code
indexLookupTable[i,lagsModelMax+c(1:obsAll)] <- rep(profileIndices[i,1:lagsModelAll[i]],
ceiling(obsAll/lagsModelAll[i]))[1:obsAll] -1;
# Fix the head of the data, before the sample starts
indexLookupTable[i,1:lagsModelMax] <- tail(rep(unique(indexLookupTable[i,lagsModelMax+c(1:obsAll)]),lagsModelMax),
lagsModelMax);
}
# Do shifts for proper lags only:
# Check lags variable for 24 / 24*7 / 24*365 / 48 / 48*7 / 48*365 / 365 / 52
# If they are there, find the DST / Leap moments
# Then amend respective lookup values of profile, shifting them around
if(any(yClasses=="zoo") && !is.null(yIndex) && !is.numeric(yIndex)){
# If this is weekly data, duplicate 52, when 53 is used
if(any(lags==52) && any(strftime(yIndex,format="%W")=="53")){
shiftRows <- lagsModelAll==52;
# If the data does not start with 1, proceed
if(all(which(strftime(yIndex,format="%W")=="53")!=1)){
indexLookupTable[shiftRows,which(strftime(yIndex,format="%W")=="53")] <-
indexLookupTable[shiftRows,which(strftime(yIndex,format="%W")=="53")-1];
}
}
#### If this is daily and we have 365 days of year, locate 29th February and use 28th instead
if(any(c(365,365*48,365*24) %in% lags) && any(strftime(yIndex,format="%d/%m")=="29/02")){
shiftValue <- c(365,365*48,365*24)[c(365,365*48,365*24) %in% lags]/365;
shiftRows <- lagsModelAll %in% c(365,365*48,365*24);
# If the data does not start with 1/24/48, proceed (otherwise we refer to negative numbers)
if(!any(which(strftime(yIndex,format="%d/%m")=="29/02") %in% shiftValue)){
indexLookupTable[shiftRows,which(strftime(yIndex,format="%d/%m")=="29/02")] <-
indexLookupTable[shiftRows,which(strftime(yIndex,format="%d/%m")=="29/02")-shiftValue];
}
}
#### If this is hourly; Locate DST and do shifts for specific observations
if(any(c(24,24*7,24*365,48,48*7,48*365) %in% lags)){
shiftRows <- lagsModelAll %in% c(24,48,24*7,48*7,24*365,48*365);
# If this is hourly data, then shift 1 hour. If it is halfhourly, shift 2 hours
shiftValue <- 1;
if(any(c(48,48*7,48*365) %in% lags)){
shiftValue[] <- 2;
}
# Get the start and the end of DST
dstValues <- detectdst(yIndex);
# If there are DST issues, do something
doShifts <- !is.null(dstValues) && ((nrow(dstValues$start)!=0) | (nrow(dstValues$end)!=0))
if(doShifts){
# If the start date is not positioned before the end, introduce the artificial one
if(nrow(dstValues$start)==0 ||
(nrow(dstValues$end)>0 && dstValues$start$id[1]>dstValues$end$id[1])){
dstValues$start <- rbind(data.frame(id=1,date=yIndex[1]),dstValues$start);
}
# If the end date is not present or the length of the end is not the same as the start,
# set the end of series as one
if(nrow(dstValues$end)==0 ||
nrow(dstValues$end)<nrow(dstValues$start)){
dstValues$end <- rbind(dstValues$end,data.frame(id=obsAll,date=tail(yIndex,1)));
}
# Shift everything from start to end dates by 1 obs forward.
for(i in 1:nrow(dstValues$start)){
# If the end date is natural, just shift
if(dstValues$end$id[i]+shiftValue<=obsAll){
indexLookupTable[shiftRows,dstValues$start$id[i]:dstValues$end$id[i]] <-
indexLookupTable[shiftRows,dstValues$start$id[i]:dstValues$end$id[i]+shiftValue];
}
# If it isn't, we need to come up with the values for the end of sample
else{
indexLookupTable[shiftRows,dstValues$start$id[i]:dstValues$end$id[i]] <-
indexLookupTable[shiftRows,dstValues$start$id[i]:dstValues$end$id[i]-lagsModelMax+shiftValue];
}
}
}
}
}
return(list(recent=profilesRecentTable,lookup=indexLookupTable));
}
#### ARI and MA polynomials function ####
# polynomialiser <- function(B, arOrders, iOrders, maOrders,
# arRequired, maRequired, arEstimate, maEstimate, armaParameters, lags){
#
# # Number of parameters that we have
# nParamAR <- sum(arOrders);
# nParamMA <- sum(maOrders);
#
# # Matrices with parameters
# arParameters <- matrix(0, max(arOrders * lags) + 1, length(arOrders));
# iParameters <- matrix(0, max(iOrders * lags) + 1, length(iOrders));
# maParameters <- matrix(0, max(maOrders * lags) + 1, length(maOrders));
# # The first element is always 1
# arParameters[1,] <- iParameters[1,] <- maParameters[1,] <- 1;
#
# # nParam is used for B
# nParam <- 1;
# # armanParam is used for the provided arma parameters
# armanParam <- 1;
# # Fill in the matrices with the provided parameters
# for(i in 1:length(lags)){
# if(arOrders[i]*lags[i]!=0){
# if(arEstimate){
# arParameters[1+(1:arOrders[i])*lags[i],i] <- -B[nParam+c(1:arOrders[i])-1];
# nParam[] <- nParam + arOrders[i];
# }
# else if(!arEstimate && arRequired){
# arParameters[1+(1:arOrders[i])*lags[i],i] <- -armaParameters[armanParam+c(1:arOrders[i])-1];
# armanParam[] <- armanParam + arOrders[i];
# }
# }
#
# if(iOrders[i]*lags[i] != 0){
# iParameters[1+lags[i],i] <- -1;
# }
#
# if(maOrders[i]*lags[i]!=0){
# if(maEstimate){
# maParameters[1+(1:maOrders[i])*lags[i],i] <- B[nParam+c(1:maOrders[i])-1];
# nParam[] <- nParam + maOrders[i];
# }
# else if(!maEstimate && maRequired){
# maParameters[1+(1:maOrders[i])*lags[i],i] <- armaParameters[armanParam+c(1:maOrders[i])-1];
# armanParam[] <- armanParam + maOrders[i];
# }
# }
# }
#
# # Vectors of polynomials for the ARIMA
# arPolynomial <- vector("numeric", sum(arOrders * lags) + 1);
# iPolynomial <- vector("numeric", sum(iOrders * lags) + 1);
# maPolynomial <- vector("numeric", sum(maOrders * lags) + 1);
# ariPolynomial <- vector("numeric", sum(arOrders * lags) + sum(iOrders * lags) + 1);
#
# # Fill in the first polynomials
# arPolynomial[0:(arOrders[1]*lags[1])+1] <- arParameters[0:(arOrders[1]*lags[1])+1,1];
# iPolynomial[0:(iOrders[1]*lags[1])+1] <- iParameters[0:(iOrders[1]*lags[1])+1,1];
# maPolynomial[0:(maOrders[1]*lags[1])+1] <- maParameters[0:(maOrders[1]*lags[1])+1,1];
#
# index1 <- 0;
# index2 <- 0;
# # Fill in all the other polynomials
# for(i in 1:length(lags)){
# if(i!=1){
# if(arOrders[i]>0){
# index1[] <- tailFast(whichFast(arPolynomial!=0));
# index2[] <- tailFast(whichFast(arParameters[,i]!=0));
# arPolynomial[1:(index1+index2-1)] <- polyprod(arPolynomial[1:index1], arParameters[1:index2,i]);
# }
#
# if(maOrders[i]>0){
# index1[] <- tailFast(whichFast(maPolynomial!=0));
# index2[] <- tailFast(whichFast(maParameters[,i]!=0));
# maPolynomial[1:(index1+index2-1)] <- polyprod(maPolynomial[1:index1], maParameters[1:index2,i]);
# }
#
# if(iOrders[i]>0){
# index1[] <- tailFast(whichFast(iPolynomial!=0));
# index2[] <- tailFast(whichFast(iParameters[,i]!=0));
# iPolynomial[1:(index1+index2-1)] <- polyprod(iPolynomial[1:index1], iParameters[1:index2,i]);
# }
# }
# # This part takes the power of (1-B)^D
# if(iOrders[i]>1){
# for(j in 2:iOrders[i]){
# index1[] <- tailFast(whichFast(iPolynomial!=0));
# index2[] <- tailFast(whichFast(iParameters[,i]!=0));
# iPolynomial[1:(index1+index2-1)] = polyprod(iPolynomial[1:index1], iParameters[1:index2,i]);
# }
# }
# }
# # ARI polynomials
# ariPolynomial[] <- polyprod(arPolynomial, iPolynomial);
#
# return(list(arPolynomial=arPolynomial,iPolynomial=iPolynomial,
# ariPolynomial=ariPolynomial,maPolynomial=maPolynomial));
# }
#### Technical methods ####
#' @export
lags.adam <- function(object, ...){
return(object$lags);
}
#' @rdname plot.smooth
#' @export
plot.adam <- function(x, which=c(1,2,4,6), level=0.95, legend=FALSE,
ask=prod(par("mfcol")) < length(which) && dev.interactive(),
lowess=TRUE, ...){
ellipsis <- list(...);
# Define, whether to wait for the hit of "Enter"
if(ask){
devAskNewPage(TRUE);
on.exit(devAskNewPage(FALSE));
}
# Warn if the diagnostis will be done for scale
if(is.scale(x$scale) && any(which %in% c(2:6,8,9,13,14))){
message("Note that residuals diagnostics plots are produced for scale model");
}
# 1. Fitted vs Actuals values
plot1 <- function(x, ...){
ellipsis <- list(...);
# Get the actuals and the fitted values
ellipsis$y <- as.vector(actuals(x));
if(is.occurrence(x)){
if(any(x$distribution==c("plogis","pnorm")) || x$occurrence!="none"){
ellipsis$y <- (ellipsis$y!=0)*1;
}
}
ellipsis$x <- as.vector(fitted(x));
# If this is a mixture model, remove zeroes
if(is.occurrence(x$occurrence)){
ellipsis$x <- ellipsis$x[ellipsis$y!=0];
ellipsis$y <- ellipsis$y[ellipsis$y!=0];
}
# Remove NAs
if(any(is.na(ellipsis$x))){
ellipsis$y <- ellipsis$y[!is.na(ellipsis$x)];
ellipsis$x <- ellipsis$x[!is.na(ellipsis$x)];
}
if(any(is.na(ellipsis$y))){
ellipsis$x <- ellipsis$x[!is.na(ellipsis$y)];
ellipsis$y <- ellipsis$y[!is.na(ellipsis$y)];
}
# Title
if(!any(names(ellipsis)=="main")){
ellipsis$main <- "Actuals vs Fitted";
}
# If type and ylab are not provided, set them...
if(!any(names(ellipsis)=="type")){
ellipsis$type <- "p";
}
if(!any(names(ellipsis)=="ylab")){
ellipsis$ylab <- "Actuals";
}
if(!any(names(ellipsis)=="xlab")){
ellipsis$xlab <- "Fitted";
}
# xlim and ylim
if(!any(names(ellipsis)=="xlim")){
ellipsis$xlim <- range(c(ellipsis$x,ellipsis$y));
}
if(!any(names(ellipsis)=="ylim")){
ellipsis$ylim <- range(c(ellipsis$x,ellipsis$y));
}
# Start plotting
do.call(plot,ellipsis);
abline(a=0,b=1,col="grey",lwd=2,lty=2)
if(lowess){
lines(lowess(ellipsis$x, ellipsis$y), col="red");
}
}
# 2 and 3: Standardised / studentised residuals vs Fitted
plot2 <- function(x, type="rstandard", ...){
ellipsis <- list(...);
# Amend to do analysis of residuals of scale model
if(is.scale(x$scale)){
x <- x$scale;
}
ellipsis$x <- as.vector(fitted(x));
if(type=="rstandard"){
ellipsis$y <- as.vector(rstandard(x));
yName <- "Standardised";
}
else{
ellipsis$y <- as.vector(rstudent(x));
yName <- "Studentised";
}
if(is.occurrence(x$occurrence)){
ellipsis$x <- ellipsis$x[actuals(x$occurrence)!=0];
ellipsis$y <- ellipsis$y[actuals(x$occurrence)!=0];
}
# Remove NAs
if(any(is.na(ellipsis$x))){
ellipsis$x <- ellipsis$x[!is.na(ellipsis$x)];
ellipsis$y <- ellipsis$y[!is.na(ellipsis$y)];
}
# Main, labs etc
if(!any(names(ellipsis)=="main")){
if(any(x$distribution==c("dinvgauss","dgamma","dlnorm","dllaplace","dls","dlgnorm"))){
ellipsis$main <- paste0("log(",yName," Residuals) vs Fitted");
}
else{
ellipsis$main <- paste0(yName," Residuals vs Fitted");
}
}
if(!any(names(ellipsis)=="xlab")){
ellipsis$xlab <- "Fitted";
}
if(!any(names(ellipsis)=="ylab")){
ellipsis$ylab <- paste0(yName," Residuals");
}
if(legend){
if(ellipsis$x[length(ellipsis$x)]>mean(ellipsis$x)){
legendPosition <- "bottomright";
}
else{
legendPosition <- "topright";
}
}
# Get the IDs of outliers and statistic
outliers <- outlierdummy(x, level=level, type=type);
statistic <- outliers$statistic;
# Analyse stuff in logarithms if the error is multiplicative
if(any(x$distribution==c("dinvgauss","dgamma"))){
ellipsis$y[] <- log(ellipsis$y);
statistic <- log(statistic);
}
else if(any(x$distribution==c("dlnorm","dllaplace","dls","dlgnorm"))){
ellipsis$y[] <- log(ellipsis$y);
}
outliers <- which(ellipsis$y >statistic[2] | ellipsis$y <statistic[1]);
# cat(paste0(round(length(outliers)/length(ellipsis$y),3)*100,"% of values are outside the bounds\n"));
if(!any(names(ellipsis)=="ylim")){
ellipsis$ylim <- range(c(ellipsis$y,statistic), na.rm=TRUE)*1.2;
if(legend){
if(legendPosition=="bottomright"){
ellipsis$ylim[1] <- ellipsis$ylim[1] - 0.2*diff(ellipsis$ylim);
}
else{
ellipsis$ylim[2] <- ellipsis$ylim[2] + 0.2*diff(ellipsis$ylim);
}
}
}
xRange <- range(ellipsis$x, na.rm=TRUE);
xRange[1] <- xRange[1] - sd(ellipsis$x, na.rm=TRUE);
xRange[2] <- xRange[2] + sd(ellipsis$x, na.rm=TRUE);
do.call(plot,ellipsis);
abline(h=0, col="grey", lty=2);
polygon(c(xRange,rev(xRange)),c(statistic[1],statistic[1],statistic[2],statistic[2]),
col="lightgrey", border=NA, density=10);
abline(h=statistic, col="red", lty=2);
if(length(outliers)>0){
points(ellipsis$x[outliers], ellipsis$y[outliers], pch=16);
text(ellipsis$x[outliers], ellipsis$y[outliers], labels=outliers, pos=(ellipsis$y[outliers]>0)*2+1);
}
if(lowess){
lines(lowess(ellipsis$x[!is.na(ellipsis$y)], ellipsis$y[!is.na(ellipsis$y)]), col="red");
}
if(legend){
if(lowess){
legend(legendPosition,
legend=c(paste0(round(level,3)*100,"% bounds"),"outside the bounds","LOWESS line"),
col=c("red", "black","red"), lwd=c(1,NA,1), lty=c(2,1,1), pch=c(NA,16,NA));
}
else{
legend(legendPosition,
legend=c(paste0(round(level,3)*100,"% bounds"),"outside the bounds"),
col=c("red", "black"), lwd=c(1,NA), lty=c(2,1), pch=c(NA,16));
}
}
}
# 4 and 5. Fitted vs |Residuals| or Fitted vs Residuals^2
plot3 <- function(x, type="abs", ...){
ellipsis <- list(...);
# Amend to do analysis of residuals of scale model
if(is.scale(x$scale)){
x <- x$scale;
}
ellipsis$x <- as.vector(fitted(x));
ellipsis$y <- as.vector(residuals(x));
if(any(x$distribution==c("dinvgauss","dlnorm","dllaplace","dls","dlgnorm"))){
ellipsis$y[] <- log(ellipsis$y);
}
if(type=="abs"){
ellipsis$y[] <- abs(ellipsis$y);
}
else{
ellipsis$y[] <- as.vector(ellipsis$y)^2;
}
if(is.occurrence(x$occurrence)){
ellipsis$x <- ellipsis$x[ellipsis$y!=0];
ellipsis$y <- ellipsis$y[ellipsis$y!=0];
}
# Remove NAs
if(any(is.na(ellipsis$x))){
ellipsis$x <- ellipsis$x[!is.na(ellipsis$x)];
ellipsis$y <- ellipsis$y[!is.na(ellipsis$y)];
}
if(!any(names(ellipsis)=="main")){
if(type=="abs"){
if(any(x$distribution==c("dinvgauss","dlnorm","dllaplace","dls","dlgnorm"))){
ellipsis$main <- "|log(Residuals)| vs Fitted";
}
else{
ellipsis$main <- "|Residuals| vs Fitted";
}
}
else{
if(any(x$distribution==c("dinvgauss","dlnorm","dllaplace","dls","dlgnorm"))){
ellipsis$main <- "log(Residuals)^2 vs Fitted";
}
else{
ellipsis$main <- "Residuals^2 vs Fitted";
}
}
}
if(!any(names(ellipsis)=="xlab")){
ellipsis$xlab <- "Fitted";
}
if(!any(names(ellipsis)=="ylab")){
if(type=="abs"){
ellipsis$ylab <- "|Residuals|";
}
else{
ellipsis$ylab <- "Residuals^2";
}
}
do.call(plot,ellipsis);
abline(h=0, col="grey", lty=2);
if(lowess){
lines(lowess(ellipsis$x[!is.na(ellipsis$y)], ellipsis$y[!is.na(ellipsis$y)]), col="red");
}
}
# 6. Q-Q with the specified distribution
plot4 <- function(x, ...){
ellipsis <- list(...);
# Amend to do analysis of residuals of scale model
if(is.scale(x$scale)){
x <- x$scale;
}
ellipsis$y <- as.vector(residuals(x));
if(is.occurrence(x$occurrence)){
ellipsis$y <- ellipsis$y[actuals(x$occurrence)!=0];
}
if(!any(names(ellipsis)=="xlab")){
ellipsis$xlab <- "Theoretical Quantile";
}
if(!any(names(ellipsis)=="ylab")){
ellipsis$ylab <- "Actual Quantile";
}
if(any(x$distribution=="dnorm")){
if(!any(names(ellipsis)=="main")){
ellipsis$main <- "QQ plot of Normal distribution";
}
do.call(qqnorm, ellipsis);
qqline(ellipsis$y);
}
else if(any(x$distribution=="dlnorm")){
if(!any(names(ellipsis)=="main")){
ellipsis$main <- "QQ plot of Log-Normal distribution";
}
ellipsis$x <- qlnorm(ppoints(500), meanlog=-extractScale(x)^2/2, sdlog=extractScale(x));
do.call(qqplot, ellipsis);
qqline(ellipsis$y, distribution=function(p) qlnorm(p, meanlog=-extractScale(x)^2/2, sdlog=extractScale(x)));
}
else if(x$distribution=="dlaplace"){
if(!any(names(ellipsis)=="main")){
ellipsis$main <- "QQ-plot of Laplace distribution";
}
ellipsis$x <- qlaplace(ppoints(500), mu=0, scale=extractScale(x));
do.call(qqplot, ellipsis);
qqline(ellipsis$y, distribution=function(p) qlaplace(p, mu=0, scale=extractScale(x)));
}
else if(x$distribution=="dllaplace"){
if(!any(names(ellipsis)=="main")){
ellipsis$main <- "QQ-plot of Log-Laplace distribution";
}
ellipsis$x <- exp(qlaplace(ppoints(500), mu=0, scale=extractScale(x)));
do.call(qqplot, ellipsis);
qqline(ellipsis$y, distribution=function(p) exp(qlaplace(p, mu=0, scale=extractScale(x))));
}
else if(x$distribution=="ds"){
if(!any(names(ellipsis)=="main")){
ellipsis$main <- "QQ-plot of S distribution";
}
ellipsis$x <- qs(ppoints(500), mu=0, scale=extractScale(x));
do.call(qqplot, ellipsis);
qqline(ellipsis$y, distribution=function(p) qs(p, mu=0, scale=extractScale(x)));
}
else if(x$distribution=="dls"){
if(!any(names(ellipsis)=="main")){
ellipsis$main <- "QQ-plot of Log-S distribution";
}
ellipsis$x <- exp(qs(ppoints(500), mu=0, scale=extractScale(x)));
do.call(qqplot, ellipsis);
qqline(ellipsis$y, distribution=function(p) exp(qs(p, mu=0, scale=extractScale(x))));
}
else if(x$distribution=="dgnorm"){
if(!any(names(ellipsis)=="main")){
ellipsis$main <- paste0("QQ-plot of Generalised Normal distribution with shape=",round(x$other$shape,3));
}
ellipsis$x <- qgnorm(ppoints(500), mu=0, scale=extractScale(x), shape=x$other$shape);
do.call(qqplot, ellipsis);
qqline(ellipsis$y, distribution=function(p) qgnorm(p, mu=0, scale=extractScale(x), shape=x$other$shape));
}
else if(x$distribution=="dlgnorm"){
if(!any(names(ellipsis)=="main")){
ellipsis$main <- paste0("QQ-plot of Log-Generalised Normal distribution with shape=",round(x$other$shape,3));
}
ellipsis$x <- exp(qgnorm(ppoints(500), mu=0, scale=extractScale(x), shape=x$other$shape));
do.call(qqplot, ellipsis);
qqline(ellipsis$y, distribution=function(p) exp(qgnorm(p, mu=0, scale=extractScale(x), shape=x$other$shape)));
}
else if(x$distribution=="dlogis"){
if(!any(names(ellipsis)=="main")){
ellipsis$main <- "QQ-plot of Logistic distribution";
}
ellipsis$x <- qlogis(ppoints(500), location=0, scale=extractScale(x));
do.call(qqplot, ellipsis);
qqline(ellipsis$y, distribution=function(p) qlogis(p, location=0, scale=extractScale(x)));
}
else if(x$distribution=="dt"){
# Standardise residuals
ellipsis$y[] <- ellipsis$y / sd(ellipsis$y);
if(!any(names(ellipsis)=="main")){
ellipsis$main <- "QQ-plot of Student's distribution";
}
ellipsis$x <- qt(ppoints(500), df=x$other$nu);
do.call(qqplot, ellipsis);
qqline(ellipsis$y, distribution=function(p) qt(p, df=x$other$nu));
}
else if(x$distribution=="dalaplace"){
if(!any(names(ellipsis)=="main")){
ellipsis$main <- paste0("QQ-plot of Asymmetric Laplace with alpha=",round(x$other$alpha,3));
}
ellipsis$x <- qalaplace(ppoints(500), mu=0, scale=extractScale(x), alpha=x$other$alpha);
do.call(qqplot, ellipsis);
qqline(ellipsis$y, distribution=function(p) qalaplace(p, mu=0, scale=extractScale(x), alpha=x$other$alpha));
}
else if(x$distribution=="dinvgauss"){
if(is.scale(x)){
# Transform residuals for something meaningful
# This is not 100% accurate, because the dispersion should change as well as mean...
if(!any(names(ellipsis)=="main")){
ellipsis$main <- "QQ-plot of Chi-Squared distribution";
}
ellipsis$x <- qchisq(ppoints(500), df=1);
do.call(qqplot, ellipsis);
qqline(ellipsis$y, distribution=function(p) qchisq(p, df=1));
}
else{
# Transform residuals for something meaningful
# This is not 100% accurate, because the dispersion should change as well as mean...
if(!any(names(ellipsis)=="main")){
ellipsis$main <- "QQ-plot of Inverse Gaussian distribution";
}
ellipsis$x <- qinvgauss(ppoints(500), mean=1, dispersion=extractScale(x));
do.call(qqplot, ellipsis);
qqline(ellipsis$y, distribution=function(p) qinvgauss(p, mean=1, dispersion=extractScale(x)));
}
}
else if(x$distribution=="dgamma"){
# Transform residuals for something meaningful
# This is not 100% accurate, because the dispersion should change as well as mean...
if(!any(names(ellipsis)=="main")){
ellipsis$main <- "QQ-plot of Gamma distribution";
}
ellipsis$x <- qgamma(ppoints(500), shape=1/extractScale(x), scale=extractScale(x));
do.call(qqplot, ellipsis);
qqline(ellipsis$y, distribution=function(p) qgamma(p, shape=1/extractScale(x), scale=extractScale(x)));
}
}
# 7. Basic plot over time
plot5 <- function(x, ...){
ellipsis <- list(...);
ellipsis$fitted <- fitted(x);
ellipsis$actuals <- actuals(x);
if(!is.null(x$holdout)){
responseName <- all.vars(formula(x))[1];
yHoldout <- x$holdout[,responseName]
if(inherits(yHoldout,"tbl_df") || inherits(yHoldout,"tbl")){
yHoldout <- yHoldout[[1]];
}
if(is.zoo(ellipsis$fitted)){
ellipsis$actuals <- zoo(c(as.vector(ellipsis$actuals),as.vector(yHoldout)),
order.by=c(time(ellipsis$fitted),time(yHoldout)));
}
else{
ellipsis$actuals <- ts(c(as.vector(ellipsis$actuals),as.vector(yHoldout)),
start=start(ellipsis$fitted),
frequency=frequency(ellipsis$fitted));
}
}
# Reclass the actuals just in case
else{
if(is.zoo(ellipsis$fitted)){
ellipsis$actuals <- zoo(as.vector(ellipsis$actuals),
order.by=time(ellipsis$fitted));
}
else{
ellipsis$actuals <- ts(ellipsis$actuals,
start=start(ellipsis$fitted),
frequency=frequency(ellipsis$fitted));
}
}
if(is.null(ellipsis$main)){
ellipsis$main <- x$model;
}
ellipsis$forecast <- x$forecast;
ellipsis$legend <- FALSE;
ellipsis$parReset <- FALSE;
do.call(graphmaker, ellipsis);
}
# 8 and 9. Standardised / Studentised residuals vs time
plot6 <- function(x, type="rstandard", ...){
# Amend to do analysis of residuals of scale model
if(is.scale(x$scale)){
x <- x$scale;
}
ellipsis <- list(...);
if(type=="rstandard"){
ellipsis$x <- rstandard(x);
yName <- "Standardised";
}
else{
ellipsis$x <- rstudent(x);
yName <- "Studentised";
}
# If there is occurrence part, substitute zeroes with NAs
if(is.occurrence(x$occurrence)){
ellipsis$x[actuals(x$occurrence)==0] <- NA;
}
# Main, labs etc
if(!any(names(ellipsis)=="main")){
if(any(x$distribution==c("dinvgauss","dgamma","dlnorm","dllaplace","dls","dlgnorm"))){
ellipsis$main <- paste0("log(",yName," Residuals) vs Time");
}
else{
ellipsis$main <- paste0(yName," Residuals vs Time");
}
}
if(!any(names(ellipsis)=="xlab")){
ellipsis$xlab <- "Time";
}
if(!any(names(ellipsis)=="ylab")){
ellipsis$ylab <- paste0(yName," Residuals");
}
# If type and ylab are not provided, set them...
if(!any(names(ellipsis)=="type")){
ellipsis$type <- "l";
}
# Get the IDs of outliers and statistic
outliers <- outlierdummy(x, level=level, type=type);
statistic <- outliers$statistic;
# Analyse stuff in logarithms if the error is multiplicative
if(any(x$distribution==c("dinvgauss","dgamma"))){
ellipsis$x[] <- log(ellipsis$x);
statistic <- log(statistic);
}
else if(any(x$distribution==c("dlnorm","dllaplace","dls","dlgnorm"))){
ellipsis$x[] <- log(ellipsis$x);
}
outliers <- which(ellipsis$x >statistic[2] | ellipsis$x <statistic[1]);
if(!any(names(ellipsis)=="ylim")){
ellipsis$ylim <- c(-max(abs(ellipsis$x),na.rm=TRUE),
max(abs(ellipsis$x),na.rm=TRUE))*1.2;
}
if(legend){
legendPosition <- "topright";
ellipsis$ylim[2] <- ellipsis$ylim[2] + 0.2*diff(ellipsis$ylim);
ellipsis$ylim[1] <- ellipsis$ylim[1] - 0.2*diff(ellipsis$ylim);
}
# Start plotting
do.call(plot,ellipsis);
if(length(outliers)>0){
points(time(ellipsis$x)[outliers], ellipsis$x[outliers], pch=16);
text(time(ellipsis$x)[outliers], ellipsis$x[outliers], labels=outliers, pos=(ellipsis$x[outliers]>0)*2+1);
}
# If there is occurrence model, plot points to fill in breaks
if(is.occurrence(x$occurrence)){
points(time(ellipsis$x), ellipsis$x);
}
if(lowess){
# Substitute NAs with the mean
if(any(is.na(ellipsis$x))){
ellipsis$x[is.na(ellipsis$x)] <- mean(ellipsis$x, na.rm=TRUE);
}
lines(lowess(c(1:length(ellipsis$x)),ellipsis$x), col="red");
}
abline(h=0, col="grey", lty=2);
abline(h=statistic[1], col="red", lty=2);
abline(h=statistic[2], col="red", lty=2);
polygon(c(1:nobs(x), c(nobs(x):1)),
c(rep(statistic[1],nobs(x)), rep(statistic[2],nobs(x))),
col="lightgrey", border=NA, density=10);
if(legend){
legend(legendPosition,legend=c("Residuals",paste0(level*100,"% prediction interval")),
col=c("black","red"), lwd=rep(1,3), lty=c(1,1,2));
}
}
# 10 and 11. ACF and PACF
plot7 <- function(x, type="acf", squared=FALSE, ...){
ellipsis <- list(...);
if(!any(names(ellipsis)=="main")){
if(type=="acf"){
if(squared){
ellipsis$main <- "Autocorrelation Function of Squared Residuals";
}
else{
ellipsis$main <- "Autocorrelation Function of Residuals";
}
}
else{
if(squared){
ellipsis$main <- "Partial Autocorrelation Function of Squared Residuals";
}
else{
ellipsis$main <- "Partial Autocorrelation Function of Residuals";
}
}
}
if(!any(names(ellipsis)=="xlab")){
ellipsis$xlab <- "Lags";
}
if(!any(names(ellipsis)=="ylab")){
if(type=="acf"){
ellipsis$ylab <- "ACF";
}
else{
ellipsis$ylab <- "PACF";
}
}
if(!any(names(ellipsis)=="ylim")){
ellipsis$ylim <- c(-1,1);
}
if(squared){
if(type=="acf"){
theValues <- acf(as.vector(residuals(x)^2), plot=FALSE, na.action=na.pass);
}
else{
theValues <- pacf(as.vector(residuals(x)^2), plot=FALSE, na.action=na.pass);
}
}
else{
if(type=="acf"){
theValues <- acf(as.vector(residuals(x)), plot=FALSE, na.action=na.pass);
}
else{
theValues <- pacf(as.vector(residuals(x)), plot=FALSE, na.action=na.pass);
}
}
ellipsis$x <- switch(type,
"acf"=theValues$acf[-1],
"pacf"=theValues$acf);
statistic <- qnorm(c((1-level)/2, (1+level)/2),0,sqrt(1/nobs(x)));
ellipsis$type <- "h"
do.call(plot,ellipsis);
abline(h=0, col="black", lty=1);
abline(h=statistic, col="red", lty=2);
if(any(ellipsis$x>statistic[2] | ellipsis$x<statistic[1])){
outliers <- which(ellipsis$x >statistic[2] | ellipsis$x <statistic[1]);
points(outliers, ellipsis$x[outliers], pch=16);
text(outliers, ellipsis$x[outliers], labels=outliers, pos=(ellipsis$x[outliers]>0)*2+1);
}
}
# 12. Plot of states
plot8 <- function(x, ...){
parDefault <- par(no.readonly=TRUE);
on.exit(par(parDefault), add=TRUE);
if(any(unlist(gregexpr("C",x$model))==-1)){
statesNames <- c("actuals",colnames(x$states),"residuals");
x$states <- cbind(actuals(x),x$states,residuals(x));
colnames(x$states) <- statesNames;
if(ncol(x$states)>10){
message("Too many states. Plotting them one by one on several plots.");
if(is.null(ellipsis$main)){
ellipsisMain <- NULL;
}
else{
ellipsisMain <- ellipsis$main;
}
nPlots <- ceiling(ncol(x$states)/10);
for(i in 1:nPlots){
if(is.null(ellipsisMain)){
ellipsis$main <- paste0("States of ",x$model,", part ",i);
}
ellipsis$x <- x$states[,(1+(i-1)*10):min(i*10,ncol(x$states)),drop=FALSE];
do.call(plot, ellipsis);
}
}
else{
if(ncol(x$states)<=5){
ellipsis$nc <- 1;
}
if(is.null(ellipsis$main)){
ellipsis$main <- paste0("States of ",x$model);
}
ellipsis$x <- x$states;
do.call(plot, ellipsis);
}
}
else{
# If we did combinations, we cannot return anything
message("Combination of models was done. Sorry, but there is nothing to plot.");
}
}
# 13 and 14. Fitted vs (std. Residuals)^2 or Fitted vs |std. Residuals|
plot9 <- function(x, type="abs", ...){
ellipsis <- list(...);
# Amend to do analysis of residuals of scale model
if(is.scale(x$scale)){
x <- x$scale;
}
ellipsis$x <- as.vector(fitted(x));
ellipsis$y <- as.vector(rstandard(x));
if(any(x$distribution==c("dinvgauss","dgamma"))){
ellipsis$y[] <- log(ellipsis$y);
}
if(type=="abs"){
ellipsis$y[] <- abs(ellipsis$y);
}
else{
ellipsis$y[] <- ellipsis$y^2;
}
if(is.occurrence(x$occurrence)){
ellipsis$x <- ellipsis$x[actuals(x$occurrence)!=0];
ellipsis$y <- ellipsis$y[actuals(x$occurrence)!=0];
}
# Remove NAs
if(any(is.na(ellipsis$x))){
ellipsis$x <- ellipsis$x[!is.na(ellipsis$x)];
ellipsis$y <- ellipsis$y[!is.na(ellipsis$y)];
}
if(!any(names(ellipsis)=="main")){
if(type=="abs"){
if(any(x$distribution==c("dinvgauss","dgamma","dlnorm","dllaplace","dls","dlgnorm"))){
ellipsis$main <- paste0("|log(Standardised Residuals)| vs Fitted");
}
else{
ellipsis$main <- "|Standardised Residuals| vs Fitted";
}
}
else{
if(any(x$distribution==c("dinvgauss","dgamma","dlnorm","dllaplace","dls","dlgnorm"))){
ellipsis$main <- paste0("log(Standardised Residuals)^2 vs Fitted");
}
else{
ellipsis$main <- "Standardised Residuals^2 vs Fitted";
}
}
}
if(!any(names(ellipsis)=="xlab")){
ellipsis$xlab <- "Fitted";
}
if(!any(names(ellipsis)=="ylab")){
if(type=="abs"){
if(any(x$distribution==c("dinvgauss","dgamma","dlnorm","dllaplace","dls","dlgnorm"))){
ellipsis$ylab <- "|log(Standardised Residuals)|";
}
else{
ellipsis$ylab <- "|Standardised Residuals|";
}
}
else{
if(any(x$distribution==c("dinvgauss","dgamma","dlnorm","dllaplace","dls","dlgnorm"))){
ellipsis$ylab <- "log(Standardised Residuals)^2";
}
else{
ellipsis$ylab <- "Standardised Residuals^2";
}
}
}
do.call(plot,ellipsis);
abline(h=0, col="grey", lty=2);
if(lowess){
lines(lowess(ellipsis$x[!is.na(ellipsis$y)], ellipsis$y[!is.na(ellipsis$y)]), col="red");
}
}
# Do plots
for(i in which){
if(any(i==1)){
plot1(x, ...);
}
else if(any(i==2)){
plot2(x, ...);
}
else if(any(i==3)){
plot2(x, "rstudent", ...);
}
else if(any(i==4)){
plot3(x, ...);
}
else if(any(i==5)){
plot3(x, type="squared", ...);
}
else if(any(i==6)){
plot4(x, ...);
}
else if(any(i==7)){
plot5(x, ...);
}
else if(any(i==8)){
plot6(x, ...);
}
else if(any(i==9)){
plot6(x, "rstudent", ...);
}
else if(any(i==10)){
plot7(x, type="acf", ...);
}
else if(any(i==11)){
plot7(x, type="pacf", ...);
}
else if(any(i==12)){
plot8(x, ...);
}
else if(any(i==13)){
plot9(x, ...);
}
else if(any(i==14)){
plot9(x, type="squared", ...);
}
else if(any(i==15)){
plot7(x, type="acf", squared=TRUE, ...);
}
else if(any(i==16)){
plot7(x, type="pacf", squared=TRUE, ...);
}
}
}
#' @export
print.adam <- function(x, digits=4, ...){
if(is.scale(x)){
cat("**Scale Model**\n");
}
etsModel <- any(unlist(gregexpr("ETS",x$model))!=-1);
arimaModel <- any(unlist(gregexpr("ARIMA",x$model))!=-1);
cat("Time elapsed:",round(as.numeric(x$timeElapsed,units="secs"),2),"seconds");
# tail all.vars is needed in case smooth::adam() was used
cat(paste0("\nModel estimated using ",tail(all.vars(x$call[[1]]),1),
"() function: ",x$model));
cat(paste0("\nWith ", x$initialType, " initialisation"));
if(is.scale(x$scale)){
cat("\nScale model estimated with sm():",x$scale$model);
}
if(is.occurrence(x$occurrence)){
occurrence <- switch(x$occurrence$occurrence,
"f"=,
"fixed"="Fixed probability",
"o"=,
"odds-ratio"="Odds ratio",
"i"=,
"inverse-odds-ratio"="Inverse odds ratio",
"d"=,
"direct"="Direct",
"g"=,
"general"="General",
"p"=,
"provided"="Provided by user");
cat("\nOccurrence model type:",occurrence);
}
distrib <- switch(x$distribution,
"dnorm" = "Normal",
"dlaplace" = "Laplace",
"ds" = "S",
"dgnorm" = paste0("Generalised Normal with shape=",round(x$other$shape, digits)),
"dlogis" = "Logistic",
"dt" = paste0("Student t with df=",round(x$other$nu, digits)),
"dalaplace" = paste0("Asymmetric Laplace with alpha=",round(x$other$alpha,digits)),
"dlnorm" = "Log-Normal",
"dllaplace" = "Log-Laplace",
"dls" = "Log-S",
"dlgnorm" = paste0("Log-Generalised Normal with shape=",round(x$other$shape, digits)),
# "dbcnorm" = paste0("Box-Cox Normal with lambda=",round(x$other$lambda,2)),
"dinvgauss" = "Inverse Gaussian",
"dgamma" = "Gamma"
);
if(is.occurrence(x$occurrence)){
distrib <- paste0("Mixture of Bernoulli and ", distrib);
}
cat("\nDistribution assumed in the model:", distrib);
cat("\nLoss function type:",x$loss);
if(!is.null(x$lossValue)){
cat("; Loss function value:",round(x$lossValue,digits));
if(any(x$loss==c("LASSO","RIDGE"))){
cat("; lambda=",x$other$lambda);
}
}
# If there is a Intercept/drift
if(!is.null(x$constant)){
cat("\nIntercept/Drift value:", round(x$constant, digits));
}
if(etsModel){
if(!is.null(x$persistence)){
cat("\nPersistence vector g");
if(ncol(x$data)>1){
cat(" (excluding xreg):\n");
}
else{
cat(":\n");
}
persistence <- x$persistence[substr(names(x$persistence),1,5)!="delta"];
if(arimaModel){
persistence <- persistence[substr(names(persistence),1,3)!="psi"];
}
# If there is constant, don't include the stuff
if(!is.null(x$constant)){
persistence <- persistence[substr(names(persistence),1,8)!="constant"];
persistence <- persistence[substr(names(persistence),1,8)!="drift"];
}
print(round(persistence,digits));
}
if(!is.null(x$phi)){
if(gregexpr("d",modelType(x))!=-1){
cat("Damping parameter:", round(x$phi,digits));
}
}
}
# If this is ARIMA model
if(!is.null(x$arma) && (!is.null(x$arma$ar) || !is.null(x$arma$ma))){
ordersModel <- orders(x);
# If the order was just a vector
if(!is.list(ordersModel)){
ordersModel <- list(ar=ordersModel[1], i=ordersModel[2], ma=ordersModel[3]);
}
lagsModel <- lags(x);
cat("\nARMA parameters of the model:\n");
if(!is.null(x$arma$ar)){
# cat("AR:\n")
arMatrix <- matrix(NA,max(ordersModel$ar),length(lagsModel[ordersModel$ar!=0]),
dimnames=list(paste0("AR(",1:max(ordersModel$ar),")"),
paste0("Lag ",lagsModel[ordersModel$ar!=0],"")));
arNumber <- 0;
# Remove zero lags
ordersModel$ar <- ordersModel$ar[ordersModel$ar!=0];
for(i in 1:length(ordersModel$ar)){
arMatrix[(1:ordersModel$ar[i]),i] <- x$arma$ar[arNumber+(1:ordersModel$ar[i])];
arNumber <- arNumber + ordersModel$ar[i];
}
print(round(arMatrix, digits));
}
if(!is.null(x$arma$ma)){
# cat("MA:\n")
# print(round(x$arma$ma,digits));
maMatrix <- matrix(NA,max(ordersModel$ma),length(lagsModel[ordersModel$ma!=0]),
dimnames=list(paste0("MA(",1:max(ordersModel$ma),")"),
paste0("Lag ",lagsModel[ordersModel$ma!=0],"")))
maNumber <- 0;
ordersModel$ma <- ordersModel$ma[ordersModel$ma!=0];
for(i in 1:length(ordersModel$ma)){
maMatrix[(1:ordersModel$ma[i]),i] <- x$arma$ma[maNumber+(1:ordersModel$ma[i])];
maNumber <- maNumber + ordersModel$ma[i];
}
print(round(maMatrix, digits));
}
}
cat("\nSample size:", nobs(x));
cat("\nNumber of estimated parameters:", nparam(x));
cat("\nNumber of degrees of freedom:", nobs(x)-nparam(x));
if(x$nParam[2,4]>0){
cat("\nNumber of provided parameters:", x$nParam[2,4]);
}
if(x$loss=="likelihood" ||
(any(x$loss==c("MSE","MSEh","MSCE","GPL")) & (x$distribution=="dnorm")) ||
(any(x$loss==c("aMSE","aMSEh","aMSCE","aGPL")) & (x$distribution=="dnorm")) ||
(any(x$loss==c("MAE","MAEh","MACE")) & (x$distribution=="dlaplace")) ||
(any(x$loss==c("HAM","HAMh","CHAM")) & (x$distribution=="ds"))){
ICs <- c(AIC(x),AICc(x),BIC(x),BICc(x));
names(ICs) <- c("AIC","AICc","BIC","BICc");
cat("\nInformation criteria:\n");
print(round(ICs,digits));
}
else{
cat("\nInformation criteria are unavailable for the chosen loss & distribution.\n");
}
# If there are accuracy measures, print them out
if(!is.null(x$accuracy)){
cat("\nForecast errors:\n");
if(is.null(x$occurrence)){
cat(paste(paste0("ME: ",round(x$accuracy["ME"],3)),
paste0("MAE: ",round(x$accuracy["MAE"],3)),
paste0("RMSE: ",round(sqrt(x$accuracy["MSE"]),3),"\n")
,sep="; "));
cat(paste(paste0("sCE: ",round(x$accuracy["sCE"],5)*100,"%"),
paste0("Asymmetry: ",round(x$accuracy["asymmetry"],3)*100,"%"),
paste0("sMAE: ",round(x$accuracy["sMAE"],5)*100,"%"),
paste0("sMSE: ",round(x$accuracy["sMSE"],5)*100,"%\n")
,sep="; "));
cat(paste(paste0("MASE: ",round(x$accuracy["MASE"],3)),
paste0("RMSSE: ",round(x$accuracy["RMSSE"],3)),
paste0("rMAE: ",round(x$accuracy["rMAE"],3)),
paste0("rRMSE: ",round(x$accuracy["rRMSE"],3),"\n")
,sep="; "));
}
else{
cat(paste(paste0("Asymmetry: ",round(x$accuracy["asymmetry"],5)*100,"%"),
paste0("sMSE: ",round(x$accuracy["sMSE"],5)*100,"%"),
paste0("rRMSE: ",round(x$accuracy["rRMSE"],3)),
paste0("sPIS: ",round(x$accuracy["sPIS"],5)*100,"%"),
paste0("sCE: ",round(x$accuracy["sCE"],5)*100,"%\n"),sep="; "));
}
}
}
#' @export
print.adamCombined <- function(x, digits=4, ...){
cat("Time elapsed:",round(as.numeric(x$timeElapsed,units="secs"),2),"seconds");
cat("\nModel estimated:",x$model);
cat("\nLoss function type:",x$models[[1]]$loss);
cat("\n\nNumber of models combined:", length(x$ICw));
cat("\nSample size: "); cat(nobs(x));
cat("\nAverage number of estimated parameters:", round(nparam(x),digits=digits));
cat("\nAverage number of degrees of freedom:", round(nobs(x)-nparam(x),digits=digits));
if(!is.null(x$accuracy)){
cat("\n\nForecast errors:\n");
if(is.null(x$occurrence)){
cat(paste(paste0("ME: ",round(x$accuracy["ME"],3)),
paste0("MAE: ",round(x$accuracy["MAE"],3)),
paste0("RMSE: ",round(sqrt(x$accuracy["MSE"]),3),"\n")
# paste0("Bias: ",round(x$accuracy["cbias"],3)*100,"%"),
,sep="; "));
cat(paste(paste0("sCE: ",round(x$accuracy["sCE"],5)*100,"%"),
paste0("sMAE: ",round(x$accuracy["sMAE"],5)*100,"%"),
paste0("sMSE: ",round(x$accuracy["sMSE"],5)*100,"%\n")
,sep="; "));
cat(paste(paste0("MASE: ",round(x$accuracy["MASE"],3)),
paste0("RMSSE: ",round(x$accuracy["RMSSE"],3)),
paste0("rMAE: ",round(x$accuracy["rMAE"],3)),
paste0("rRMSE: ",round(x$accuracy["rRMSE"],3),"\n")
,sep="; "));
}
else{
cat(paste(paste0("Bias: ",round(x$accuracy["cbias"],5)*100,"%"),
paste0("sMSE: ",round(x$accuracy["sMSE"],5)*100,"%"),
paste0("rRMSE: ",round(x$accuracy["rRMSE"],3)),
paste0("sPIS: ",round(x$accuracy["sPIS"],5)*100,"%"),
paste0("sCE: ",round(x$accuracy["sCE"],5)*100,"%\n"),sep="; "));
}
}
}
#### Coefficients ####
#### The functions needed for confint and reapply
# The function inverts the measurement matrix, setting infinite values to zero
# This is needed for the stability check for xreg models with regressors="adapt"
measurementInverter <- function(measurement){
measurement[] <- 1/measurement;
measurement[is.infinite(measurement)] <- 0;
return(measurement);
}
# The function that returns the eigen values for specified parameters
# The function returns TRUE if the condition is violated
eigenValues <- function(object, persistence){
#### !!!! Eigen values checks do not work for xreg. So move to (0, 1) region
if(ncol(object$data)>1 && any(substr(names(object$persistence),1,5)=="delta")){
# We check the condition on average
return(any(abs(eigen((object$transition -
diag(as.vector(persistence)) %*%
t(measurementInverter(object$measurement[1:nobs(object),,drop=FALSE])) %*%
object$measurement[1:nobs(object),,drop=FALSE] / nobs(object)),
symmetric=FALSE, only.values=TRUE)$values)>1+1E-10));
}
else{
return(any(abs(eigen(object$transition -
persistence %*% object$measurement[nobs(object),,drop=FALSE],
symmetric=FALSE, only.values=TRUE)$values)>1+1E-10));
}
}
# The function that returns the bounds for persistence parameters, based on eigen values
eigenBounds <- function(object, persistence, variableNumber=1){
# The lower bound
persistence[variableNumber,] <- -5;
eigenValuesTested <- eigenValues(object, persistence);
while(eigenValuesTested){
persistence[variableNumber,] <- persistence[variableNumber,] + 0.01;
eigenValuesTested[] <- eigenValues(object, persistence);
if(persistence[variableNumber,]>5){
persistence[variableNumber,] <- -5;
break;
}
}
lowerBound <- persistence[variableNumber,]-0.01;
# The upper bound
persistence[variableNumber,] <- 5;
eigenValuesTested <- eigenValues(object, persistence);
while(eigenValuesTested){
persistence[variableNumber,] <- persistence[variableNumber,] - 0.01;
eigenValuesTested[] <- eigenValues(object, persistence);
if(persistence[variableNumber,]<-5){
persistence[variableNumber,] <- 5;
break;
}
}
upperBound <- persistence[variableNumber,]+0.01;
return(c(lowerBound, upperBound));
}
# Function for the bounds of the AR parameters
arPolinomialsBounds <- function(arPolynomialMatrix,arPolynomial,variableNumber){
# The lower bound
arPolynomial[variableNumber] <- -5;
arPolynomialMatrix[,1] <- -arPolynomial[-1];
arPolyroots <- any(abs(eigen(arPolynomialMatrix, symmetric=FALSE, only.values=TRUE)$values)>1);
stoppingCriteria <- 20;
i <- 1;
while(arPolyroots){
arPolynomial[variableNumber] <- arPolynomial[variableNumber] +0.01;
arPolynomialMatrix[,1] <- -arPolynomial[-1];
arPolyroots[] <- any(abs(eigen(arPolynomialMatrix, symmetric=FALSE, only.values=TRUE)$values)>1);
i[] <- i+1;
if(i>=stoppingCriteria){
break;
}
}
lowerBound <- arPolynomial[variableNumber]-0.01;
# The upper bound
arPolynomial[variableNumber] <- 5;
arPolynomialMatrix[,1] <- -arPolynomial[-1];
arPolyroots <- any(abs(eigen(arPolynomialMatrix, symmetric=FALSE, only.values=TRUE)$values)>1);
i[] <- 1;
while(arPolyroots){
arPolynomial[variableNumber] <- arPolynomial[variableNumber] -0.01;
arPolynomialMatrix[,1] <- -arPolynomial[-1];
arPolyroots[] <- any(abs(eigen(arPolynomialMatrix, symmetric=FALSE, only.values=TRUE)$values)>1);
i[] <- i+1;
if(i>=stoppingCriteria){
break;
}
}
upperBound <- arPolynomial[variableNumber]+0.01;
return(c(lowerBound, upperBound));
}
# Confidence intervals
#' @export
confint.adam <- function(object, parm, level=0.95, bootstrap=FALSE, ...){
parameters <- coef(object);
confintNames <- c(paste0((1-level)/2*100,"%"),
paste0((1+level)/2*100,"%"));
if(bootstrap){
coefValues <- coefbootstrap(object, ...);
adamReturn <- cbind(sqrt(diag(coefValues$vcov)),
apply(coefValues$coefficients,2,quantile,probs=(1-level)/2),
apply(coefValues$coefficients,2,quantile,probs=(1+level)/2));
colnames(adamReturn) <- c("S.E.",confintNames);
}
else{
adamVcov <- vcov(object, ...);
adamSD <- sqrt(abs(diag(adamVcov)));
parametersNames <- names(adamSD);
nParam <- length(adamSD);
etsModel <- any(unlist(gregexpr("ETS",object$model))!=-1);
arimaModel <- any(unlist(gregexpr("ARIMA",object$model))!=-1);
adamCoefBounds <- matrix(0,nParam,2,
dimnames=list(parametersNames,NULL));
# Fill in the values with normal bounds
adamCoefBounds[,1] <- qt((1-level)/2, df=nobs(object)-nparam(object))*adamSD;
adamCoefBounds[,2] <- qt((1+level)/2, df=nobs(object)+nparam(object))*adamSD;
persistence <- as.matrix(object$persistence);
# If there is xreg, but no deltas, increase persistence by including zeroes
# This can be considered as a failsafe mechanism
if(ncol(object$data)>1 && !any(substr(names(object$persistence),1,5)=="delta")){
persistence <- rbind(persistence,matrix(rep(0,sum(object$nParam[,2])),ncol=1));
}
# Correct the bounds for the ETS model
if(etsModel){
#### The usual bounds ####
if(object$bounds=="usual"){
# Check, if there is alpha
if(any(parametersNames=="alpha")){
adamCoefBounds["alpha",1] <- max(-parameters["alpha"],adamCoefBounds["alpha",1]);
adamCoefBounds["alpha",2] <- min(1-parameters["alpha"],adamCoefBounds["alpha",2]);
}
# Check, if there is beta
if(any(parametersNames=="beta")){
adamCoefBounds["beta",1] <- max(-parameters["beta"],adamCoefBounds["beta",1]);
if(any(parametersNames=="alpha")){
adamCoefBounds["beta",2] <- min(parameters["alpha"]-parameters["beta"],adamCoefBounds["beta",2]);
}
else{
adamCoefBounds["beta",2] <- min(object$persistence["alpha"]-parameters["beta"],adamCoefBounds["beta",2]);
}
}
# Check, if there are gammas
if(any(substr(parametersNames,1,5)=="gamma")){
gammas <- which(substr(parametersNames,1,5)=="gamma");
adamCoefBounds[gammas,1] <- apply(cbind(adamCoefBounds[gammas,1],-parameters[gammas]),1,max);
if(any(parametersNames=="alpha")){
adamCoefBounds[gammas,2] <- apply(cbind(adamCoefBounds[gammas,2],
(1-parameters["alpha"])-parameters[gammas]),1,min);
}
else{
adamCoefBounds[gammas,2] <- apply(cbind(adamCoefBounds[gammas,2],
(1-object$persistence["alpha"])-parameters[gammas]),1,min);
}
}
# Check, if there are deltas (for xreg)
if(any(substr(parametersNames,1,5)=="delta")){
deltas <- which(substr(parametersNames,1,5)=="delta");
adamCoefBounds[deltas,1] <- apply(cbind(adamCoefBounds[deltas,1],-parameters[deltas]),1,max);
adamCoefBounds[deltas,2] <- apply(cbind(adamCoefBounds[deltas,2],1-parameters[deltas]),1,min);
}
# These are "usual" bounds for phi. We don't care about other bounds
if(any(parametersNames=="phi")){
adamCoefBounds["phi",1] <- max(-parameters["phi"],adamCoefBounds["phi",1]);
adamCoefBounds["phi",2] <- min(1-parameters["phi"],adamCoefBounds["phi",2]);
}
}
#### Admissible bounds ####
else if(object$bounds=="admissible"){
# Check, if there is alpha
if(any(parametersNames=="alpha")){
alphaBounds <- eigenBounds(object, persistence,
variableNumber=which(names(object$persistence)=="alpha"));
adamCoefBounds["alpha",1] <- max(alphaBounds[1]-parameters["alpha"],adamCoefBounds["alpha",1]);
adamCoefBounds["alpha",2] <- min(alphaBounds[2]-parameters["alpha"],adamCoefBounds["alpha",2]);
}
# Check, if there is beta
if(any(parametersNames=="beta")){
betaBounds <- eigenBounds(object, persistence,
variableNumber=which(names(object$persistence)=="beta"));
adamCoefBounds["beta",1] <- max(betaBounds[1]-parameters["beta"],adamCoefBounds["beta",1]);
adamCoefBounds["beta",2] <- min(betaBounds[2]-parameters["beta"],adamCoefBounds["beta",2]);
}
# Check, if there are gammas
if(any(substr(parametersNames,1,5)=="gamma")){
gammas <- which(substr(parametersNames,1,5)=="gamma");
for(i in 1:length(gammas)){
gammaBounds <- eigenBounds(object, persistence,
variableNumber=which(substr(names(object$persistence),1,5)=="gamma")[i]);
adamCoefBounds[gammas[i],1] <- max(gammaBounds[1]-parameters[gammas[i]],adamCoefBounds[gammas[i],1]);
adamCoefBounds[gammas[i],2] <- min(gammaBounds[2]-parameters[gammas[i]],adamCoefBounds[gammas[i],2]);
}
}
# Check, if there are deltas (for xreg)
if(any(substr(parametersNames,1,5)=="delta")){
deltas <- which(substr(parametersNames,1,5)=="delta");
for(i in 1:length(deltas)){
deltaBounds <- eigenBounds(object, persistence,
variableNumber=which(substr(names(object$persistence),1,5)=="delta")[i]);
adamCoefBounds[deltas[i],1] <- max(deltaBounds[1]-parameters[deltas[i]],adamCoefBounds[deltas[i],1]);
adamCoefBounds[deltas[i],2] <- min(deltaBounds[2]-parameters[deltas[i]],adamCoefBounds[deltas[i],2]);
}
}
}
# Restrictions on the initials for the multiplicative models (greater than zero)
# Level
# if(errorType(object)=="M" && any(parametersNames=="level")){
# adamCoefBounds["level",1] <- max(-parameters["level"],adamCoefBounds["level",1]);
# adamCoefBounds["level",2] <- max(-parameters["level"],adamCoefBounds["level",2]);
# }
adamModelType <- modelType(object);
# Trend
if(substr(adamModelType,2,2)=="M" && any(parametersNames=="trend")){
adamCoefBounds["trend",1] <- max(-parameters["trend"],adamCoefBounds["trend",1]);
adamCoefBounds["trend",2] <- max(-parameters["trend"],adamCoefBounds["trend",2]);
}
# Seasonality
if(substr(adamModelType,nchar(adamModelType),nchar(adamModelType))=="M" &&
any(substr(parametersNames,1,8)=="seasonal")){
seasonals <- which(substr(parametersNames,1,8)=="seasonal");
adamCoefBounds[seasonals,1] <- max(-parameters[seasonals],adamCoefBounds[seasonals,1]);
adamCoefBounds[seasonals,2] <- max(-parameters[seasonals],adamCoefBounds[seasonals,2]);
}
}
# Correct the bounds for the ARIMA model
if(arimaModel){
#### Deal with ARIMA parameters ####
ariPolynomial <- object$other$polynomial$ariPolynomial;
arPolynomial <- object$other$polynomial$arPolynomial;
maPolynomial <- object$other$polynomial$maPolynomial;
nonZeroARI <- object$other$ARIMAIndices$nonZeroARI;
nonZeroMA <- object$other$ARIMAIndices$nonZeroMA;
arPolynomialMatrix <- object$other$arPolynomialMatrix;
# Locate all thetas for ARIMA
thetas <- which(substr(parametersNames,1,5)=="theta");
# Locate phi for ARIMA (they are always phi1, phi2 etc)
phis <- which((substr(parametersNames,1,3)=="phi") & (nchar(parametersNames)>3));
# Do loop for thetas
if(length(thetas)>0){
# MA parameters
for(i in 1:length(thetas)){
# In this case, we check, where the standard condition is violated for an element of persistence,
# and then substitute the ARI part from that.
psiBounds <- eigenBounds(object, persistence,
variableNumber=which(substr(names(object$persistence),1,3)=="psi")[nonZeroMA[i,2]]);
# If there are ARI elements in persistence, subtract (-(-x)) them to get proper bounds
if(any(nonZeroARI[,2]==i)){
ariIndex <- which(nonZeroARI[,2]==i);
adamCoefBounds[thetas[i],1] <- max(psiBounds[1]-parameters[thetas[i]]+ariPolynomial[nonZeroARI[ariIndex,1]],
adamCoefBounds[thetas[i],1]);
adamCoefBounds[thetas[i],2] <- min(psiBounds[2]-parameters[thetas[i]]+ariPolynomial[nonZeroARI[ariIndex,1]],
adamCoefBounds[thetas[i],2]);
}
else{
adamCoefBounds[thetas[i],1] <- max(psiBounds[1]-parameters[thetas[i]], adamCoefBounds[thetas[i],1]);
adamCoefBounds[thetas[i],2] <- min(psiBounds[2]-parameters[thetas[i]], adamCoefBounds[thetas[i],2]);
}
}
}
# Locate phi for ARIMA (they are always phi1, phi2 etc)
if(length(phis)>0){
# AR parameters
for(i in 1:length(phis)){
# Get bounds for AR based on stationarity condition
phiBounds <- arPolinomialsBounds(arPolynomialMatrix, arPolynomial,
which(arPolynomial==arPolynomial[arPolynomial!=0][-1][i]));
adamCoefBounds[phis[i],1] <- max(phiBounds[1]-parameters[phis[i]], adamCoefBounds[phis[i],1]);
adamCoefBounds[phis[i],2] <- min(phiBounds[2]-parameters[phis[i]], adamCoefBounds[phis[i],2]);
}
}
}
adamCoefBounds[] <- adamCoefBounds+parameters;
adamReturn <- cbind(adamSD,adamCoefBounds);
colnames(adamReturn) <- c("S.E.", confintNames);
}
# If parm was not provided, return everything.
if(!exists("parm",inherits=FALSE)){
parm <- names(adamSD);
}
return(adamReturn[parm,,drop=FALSE]);
}
#' @export
coef.adam <- function(object, ...){
return(object$B);
}
#' @importFrom stats sigma
#' @export
sigma.adam <- function(object, ...){
df <- (nobs(object, all=FALSE)-nparam(object));
# If the sample is too small, then use biased estimator
if(df<=0){
df[] <- nobs(object);
}
return(sqrt(switch(object$distribution,
"dnorm"=,
"dlaplace"=,
"ds"=,
"dgnorm"=,
"dt"=,
"dlogis"=,
"dalaplace"=sum(residuals(object)^2,na.rm=TRUE),
"dlnorm"=,
"dllaplace"=,
"dls"=sum(log(residuals(object))^2,na.rm=TRUE),
"dlgnorm"=sum(log(residuals(object)-extractScale(object)^2/2)^2,na.rm=TRUE),
"dinvgauss"=,
"dgamma"=sum((residuals(object)-1)^2,na.rm=TRUE)
)
/df));
}
#' @export
summary.adam <- function(object, level=0.95, bootstrap=FALSE, ...){
ourReturn <- list(model=object$model,responseName=all.vars(formula(object))[1]);
occurrence <- NULL;
if(is.occurrence(object$occurrence)){
occurrence <- switch(object$occurrence$occurrence,
"f"=,
"fixed"="Fixed probability",
"o"=,
"odds-ratio"="Odds ratio",
"i"=,
"inverse-odds-ratio"="Inverse odds ratio",
"d"=,
"direct"="Direct",
"g"=,
"general"="General");
}
ourReturn$occurrence <- occurrence;
ourReturn$distribution <- object$distribution;
# Collect parameters and their standard errors
parametersValues <- coef(object);
if(!is.null(parametersValues)){
parametersConfint <- confint(object, level=level, bootstrap=bootstrap, ...);
if(is.null(parametersValues)){
if(ncol(object$data)>1 && all(object$persistenceXreg!=0)){
parametersValues <- c(object$persistence,object$persistenceXreg,object$initial,object$initialXreg);
}
else{
parametersValues <- c(object$persistence,object$initial);
}
warning(paste0("Parameters are not available. You have probably provided them in the model, ",
"so there was nothing to estimate. I extracted smoothing parameters and initials."),
call.=FALSE);
}
parametersTable <- cbind(parametersValues,parametersConfint);
rownames(parametersTable) <- rownames(parametersConfint);
colnames(parametersTable) <- c("Estimate","Std. Error",
paste0("Lower ",(1-level)/2*100,"%"),
paste0("Upper ",(1+level)/2*100,"%"));
ourReturn$coefficients <- parametersTable;
# Mark those that are significant on the selected level
ourReturn$significance <- !(parametersTable[,3]<=0 & parametersTable[,4]>=0);
}
ourReturn$loss <- object$loss;
ourReturn$lossValue <- object$lossValue;
ourReturn$nobs <- nobs(object);
ourReturn$nparam <- nparam(object);
ourReturn$nParam <- object$nParam;
ourReturn$call <- object$call;
ourReturn$other <- object$other;
ourReturn$sigma <- sigma(object);
if(object$loss=="likelihood" ||
(any(object$loss==c("MSE","MSEh","MSCE")) & (object$distribution=="dnorm")) ||
(any(object$loss==c("MAE","MAEh","MACE")) & (object$distribution=="dlaplace")) ||
(any(object$loss==c("HAM","HAMh","CHAM")) & (object$distribution=="ds"))){
ICs <- c(AIC(object),AICc(object),BIC(object),BICc(object));
names(ICs) <- c("AIC","AICc","BIC","BICc");
ourReturn$ICs <- ICs;
}
ourReturn$bootstrap <- bootstrap;
return(structure(ourReturn, class="summary.adam"));
}
#' @export
as.data.frame.summary.adam <- function(x, ...){
return(as.data.frame(x$coefficients, ...));
}
#' @export
summary.adamCombined <- function(object, ...){
return(print.adamCombined(object, ...));
}
#' @export
print.summary.adam <- function(x, ...){
ellipsis <- list(...);
if(!any(names(ellipsis)=="digits")){
digits <- 4;
}
else{
digits <- ellipsis$digits;
}
cat(paste0("\nModel estimated using ",tail(all.vars(x$call[[1]]),1),
"() function: ",x$model));
cat("\nResponse variable:", paste0(x$responseName,collapse=""));
if(!is.null(x$occurrence)){
cat("\nOccurrence model type:",x$occurrence);
}
distrib <- switch(x$distribution,
"dnorm" = "Normal",
"dlaplace" = "Laplace",
"ds" = "S",
"dgnorm" = paste0("Generalised Normal with shape=",round(x$other$shape,digits)),
"dlogis" = "Logistic",
"dt" = paste0("Student t with df=",round(x$other$nu, digits)),
"dalaplace" = paste0("Asymmetric Laplace with alpha=",round(x$other$alpha,digits)),
"dlnorm" = "Log-Normal",
"dllaplace" = "Log-Laplace",
"dls" = "Log-S",
"dlgnorm" = paste0("Log-Generalised Normal with shape=",round(x$other$shape,digits)),
# "dbcnorm" = paste0("Box-Cox Normal with lambda=",round(x$other$lambda,2)),
"dinvgauss" = "Inverse Gaussian",
"dgamma" = "Gamma"
);
if(!is.null(x$occurrence)){
distrib <- paste0("\nMixture of Bernoulli and ", distrib);
}
cat("\nDistribution used in the estimation:", distrib);
cat("\nLoss function type:",x$loss);
if(!is.null(x$lossValue)){
cat("; Loss function value:",round(x$lossValue,digits));
if(any(x$loss==c("LASSO","RIDGE"))){
cat("; lambda=",x$other$lambda);
}
}
if(x$bootstrap){
cat("\nBootstrap was used for the estimation of uncertainty of parameters");
}
if(!is.null(x$coefficients)){
cat("\nCoefficients:\n");
stars <- setNames(vector("character",length(x$significance)),
names(x$significance));
stars[x$significance] <- "*";
print(data.frame(round(x$coefficients,digits),stars,
check.names=FALSE,fix.empty.names=FALSE));
}
else{
cat("\nAll coefficients were provided");
}
cat("\nError standard deviation:", round(x$sigma,digits));
cat("\nSample size:", x$nobs);
cat("\nNumber of estimated parameters:", x$nparam);
cat("\nNumber of degrees of freedom:", x$nobs-x$nparam);
if(x$nParam[2,4]>0){
cat("\nNumber of provided parameters:", x$nParam[2,4]);
}
if(x$loss=="likelihood" ||
(any(x$loss==c("MSE","MSEh","MSCE")) & (x$distribution=="dnorm")) ||
(any(x$loss==c("MAE","MAEh","MACE")) & (x$distribution=="dlaplace")) ||
(any(x$loss==c("HAM","HAMh","CHAM")) & (x$distribution=="ds"))){
cat("\nInformation criteria:\n");
print(round(x$ICs,digits));
}
else{
cat("\nInformation criteria are unavailable for the chosen loss & distribution.\n");
}
}
#' @export
xtable::xtable
#' @importFrom xtable xtable
#' @export
xtable.adam <- function(x, caption = NULL, label = NULL, align = NULL, digits = NULL,
display = NULL, auto = FALSE, ...){
adamSummary <- summary(x);
return(do.call("xtable", list(x=adamSummary,
caption=caption, label=label, align=align, digits=digits,
display=display, auto=auto, ...)));
}
#' @export
xtable.summary.adam <- function(x, caption = NULL, label = NULL, align = NULL, digits = NULL,
display = NULL, auto = FALSE, ...){
# Substitute class with lm
class(x) <- "summary.lm";
return(do.call("xtable", list(x=x,
caption=caption, label=label, align=align, digits=digits,
display=display, auto=auto, ...)));
}
#' @importFrom greybox coefbootstrap dsrboot
#' @export
coefbootstrap.adam <- function(object, nsim=1000, size=floor(0.75*nobs(object)),
replace=FALSE, prob=NULL, parallel=FALSE,
method=c("dsr","cr"), ...){
startTime <- Sys.time();
cl <- match.call();
method <- match.arg(method);
if(method=="cr"){
warning("Only dsr is supported as the bootstrap method for adam().",
call.=FALSE);
}
if(is.numeric(parallel)){
nCores <- parallel;
parallel <- TRUE;
}
else if(is.logical(parallel) && parallel){
# Detect number of cores for parallel calculations
nCores <- min(parallel::detectCores() - 1, nsim);
}
# If they asked for parallel, make checks and try to do that
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)){
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)){
cluster <- parallel::makeCluster(nCores);
doParallel::registerDoParallel(cluster);
}
else{
stop("Sorry, but in order to run the function in parallel, you need either 'doMC' (prefered) or 'doParallel' packages.",
call. = FALSE);
}
}
}
# Coefficients of the model
coefficientsOriginal <- coef(object);
nVariables <- length(coefficientsOriginal);
variablesNames <- names(coefficientsOriginal);
# interceptIsNeeded <- any(variablesNames=="(Intercept)");
# variablesNamesMade <- make.names(variablesNames);
# if(interceptIsNeeded){
# variablesNamesMade[1] <- variablesNames[1];
# }
obsInsample <- nobs(object);
# The matrix with coefficients
coefBootstrap <- matrix(0, nsim, nVariables, dimnames=list(NULL, variablesNames));
# Indices for the observations to use and the vector of subsets
indices <- c(1:obsInsample);
# Form the call for alm
newCall <- object$call;
# Switch off this, just in case.
newCall$silent <- TRUE;
# If this was auto.adam, use just adam
if(newCall[[1]]=="auto.adam"){
newCall[[1]] <- as.symbol("adam");
}
newCall$formula <- formula(object);
if(!is.null(newCall$regressors)){
newCall$regressors <- switch(newCall$regressors,"select"="use",newCall$regressors);
}
# This is based on the split data, so no need to do holdout
newCall$holdout <- FALSE;
newCall$distribution <- object$distribution;
if(object$loss=="custom"){
newCall$loss <- object$lossFunction;
}
else{
newCall$loss <- object$loss;
}
# If ETS was selected
if(any(object$call!=modelType(object))){
newCall$model <- modelType(object);
}
# If ARIMA was selected
if(!is.null(object$call$orders$select)){
newCall$orders <- orders(object);
newCall$orders$select <- FALSE;
}
newCall$constant <- !is.null(object$constant);
newCall$outliers <- "ignore";
# Get lags and the minimum possible sample (2 seasons)
lags <- lags(object);
# This is needed for cases, when lags changed in the function
newCall$lags <- lags;
# Number of variables + 2 (for security) or 2 seasonal cycles + 2
obsMinimum <- max(c(lags*2,nVariables))+2;
# If this is ARIMA, and the size wasn't specified, make it changable
if(substr(object$model,1,10)=="Regression"){
regressionPure <- TRUE;
}
else{
regressionPure <- FALSE;
}
if(any(object$distribution==c("dchisq","dt"))){
newCall$nu <- object$other$nu;
}
else if(object$distribution=="dalaplace"){
newCall$alpha <- object$other$alpha;
}
else if(object$distribution=="dbcnorm"){
newCall$lambdaBC <- object$other$lambdaBC;
}
else if(any(object$distribution==c("dgnorm","dlgnorm"))){
newCall$shape <- object$other$shape;
}
newCall$occurrence <- object$occurrence;
# If this is backcasting, do sampling with moving origin
changeOrigin <- FALSE;
if(object$initialType=="complete"){
changeOrigin[] <- TRUE;
}
# Use the available parameters as starting point
newCall$B <- object$B;
newCall$lb <- rep(-Inf, length(object$B));
newCall$ub <- rep(Inf, length(object$B));
newCall$data <- object$data;
# Function creates a random sample. Needed for dynamic models
# sampler <- function(indices,size,replace,prob,regressionPure=FALSE,changeOrigin=FALSE){
# if(regressionPure){
# return(sample(indices,size=size,replace=replace,prob=prob));
# }
# else{
# indices <- c(1:ceiling(runif(1,obsMinimum,obsInsample)));
# startingIndex <- 0
# if(changeOrigin){
# startingIndex <- floor(runif(1,0,obsInsample-max(indices)));
# }
# # This way we return the continuous sample, starting from the first observation
# return(startingIndex+indices);
# }
# }
responseName <- all.vars(formula(object))[1];
# Create a new dataset
newData <- replicate(nsim, newCall$data, simplify=FALSE);
newCall$formula <- as.formula(paste0(responseName,"~."));
# Bootstrap the data
dataBoot <- suppressWarnings(apply(newCall$data, 2, dsrboot,
nsim=nsim, intermittent=FALSE));
nLevels <- length(dataBoot);
# Fill in the list of data
for(i in 1:nsim){
for(j in 1:nLevels){
newData[[i]][,j] <- dataBoot[[j]]$boot[,i];
}
}
if(!parallel){
for(i in 1:nsim){
# subsetValues <- sampler(indices,size,replace,prob,regressionPure,changeOrigin);
# newCall$data <- object$data[subsetValues,,drop=FALSE];
newCall$data[] <- newData[[i]];
testModel <- suppressWarnings(eval(newCall));
coefBootstrap[i,variablesNames %in% names(coef(testModel))] <- coef(testModel);
}
}
else{
# We don't do rbind for security reasons - in order to deal with skipped variables
coefBootstrapParallel <- foreach::`%dopar%`(foreach::foreach(i=1:nsim),{
# subsetValues <- sampler(indices,size,replace,prob,regressionPure,changeOrigin);
# newCall$data <- object$data[subsetValues,,drop=FALSE];
newCall$data[] <- newData[[i]];
testModel <- eval(newCall);
return(coef(testModel));
})
# Prepare the matrix with parameters
for(i in 1:nsim){
coefBootstrap[i,variablesNames %in% names(coefBootstrapParallel[[i]])] <- coefBootstrapParallel[[i]];
}
}
# Get rid of NAs. They mean "zero"
coefBootstrap[is.na(coefBootstrap)] <- 0;
# Rename the variables to the originals
colnames(coefBootstrap) <- names(coefficientsOriginal);
# Centre the coefficients for the calculation of the vcov
coefvcov <- coefBootstrap - matrix(coefficientsOriginal, nsim, nVariables, byrow=TRUE);
return(structure(list(vcov=(t(coefvcov) %*% coefvcov)/nsim,
coefficients=coefBootstrap, method=method,
nsim=nsim, size=NA, replace=NA, prob=NA,
parallel=parallel, model=object$call[[1]], timeElapsed=Sys.time()-startTime),
class="bootstrap"));
}
#' @export
vcov.adam <- function(object, bootstrap=FALSE, heuristics=NULL, ...){
ellipsis <- list(...);
# Heuristics is to set variance equal to sqrt(heuristics)% of values
if(!is.null(heuristics)){
if(is.numeric(heuristics)){
return(diag(abs(coef(object))*heuristics));
}
}
if(bootstrap){
return(coefbootstrap(object, ...)$vcov);
}
else{
# If the forecast is in numbers, then use its length as a horizon
if(any(!is.na(object$forecast))){
h <- length(object$forecast)
}
else{
h <- 0;
}
if(substr(object$model,1,10)=="Regression"){
modelFormula <- formula(object);
testModel <- structure(list(call=object$call,
data=as.matrix(model.matrix(modelFormula,
data=model.frame(modelFormula,
data=as.data.frame(object$data)))),
distribution=object$distribution, occurrence=object$occurrence,
coefficients=coef(object), logLik=logLik(object),
residuals=residuals(object), df=nparam(object), loss=object$loss,
other=object$other),
class=c("alm","greybox"));
testModel$call$formula <- modelFormula;
testModel$data[,1] <- object$data[,1];
colnames(testModel$data)[1] <- all.vars(modelFormula)[1];
return(vcov(testModel));
}
else{
modelReturn <- suppressWarnings(adam(object$data, h=0, model=object, formula=formula(object),
FI=TRUE, stepSize=ellipsis$stepSize));
# If any row contains all zeroes, then it means that the variable does not impact the likelihood. Invert the matrix without it.
brokenVariables <- apply(modelReturn$FI==0,1,all) | apply(is.nan(modelReturn$FI),1,any);
# If there are issues, try the same stuff, but with a different step size for hessian
if(any(brokenVariables)){
modelReturn <- suppressWarnings(adam(object$data, h=0, model=object, formula=formula(object),
FI=TRUE, stepSize=.Machine$double.eps^(1/6)));
brokenVariables <- apply(modelReturn$FI==0,1,all);
}
# If there are NaNs, then this has not been estimated well
if(any(is.nan(modelReturn$FI))){
stop("The Fisher Information cannot be calculated numerically with provided parameters - it contains NaNs.",
"Try setting stepSize for the hessian to something like stepSize=1e-6 or using the bootstrap.", call.=FALSE);
}
if(any(eigen(modelReturn$FI,only.values=TRUE)$values<0)){
warning(paste0("Observed Fisher Information is not positive semi-definite, ",
"which means that the likelihood was not maximised properly. ",
"Consider reestimating the model, tuning the optimiser or ",
"using bootstrap via bootstrap=TRUE."), call.=FALSE);
}
FIMatrix <- modelReturn$FI[!brokenVariables,!brokenVariables,drop=FALSE];
vcovMatrix <- try(chol2inv(chol(FIMatrix)), silent=TRUE);
if(inherits(vcovMatrix,"try-error")){
vcovMatrix <- try(solve(FIMatrix, diag(ncol(FIMatrix)), tol=1e-20), silent=TRUE);
if(inherits(vcovMatrix,"try-error")){
warning(paste0("Sorry, but the hessian is singular, so I could not invert it.\n",
"I failed to produce the covariance matrix of parameters. Shame on me!"),
call.=FALSE);
vcovMatrix <- diag(1e+100,ncol(FIMatrix));
}
}
# If there were broken variables, reproduce the zero elements.
# Reuse FI object in order to preserve memory. The names of cols / rows should be fine.
modelReturn$FI[!brokenVariables,!brokenVariables] <- vcovMatrix;
modelReturn$FI[brokenVariables,] <- modelReturn$FI[,brokenVariables] <- Inf;
# Just in case, take absolute values for the diagonal (in order to avoid possible issues with FI)
diag(modelReturn$FI) <- abs(diag(modelReturn$FI));
return(modelReturn$FI);
}
}
}
#### Residuals and actuals functions ####
#' @importFrom greybox actuals
#' @export
actuals.adam <- function(object, all=TRUE, ...){
responseName <- all.vars(formula(object))[1];
if(all){
response <- object$data[,responseName];
}
else{
response <- object$data[object$data[,responseName]!=0,responseName];
}
if(inherits(response,"tbl")){
response <- response[[1]];
}
return(response);
}
#' @export
nobs.adam <- function(object, ...){
return(length(actuals(object, ...)));
}
#' @export
residuals.adam <- function(object, ...){
return(switch(object$distribution,
"dlnorm"=,
"dllaplace"=,
"dls"=,
"dlgnorm"=,
"dgamma"=,
"dinvgauss"=switch(errorType(object),
# abs() is needed in order to avoid the weird cases
"A"=abs(1+object$residuals/fitted(object)),
"M"=1+object$residuals),
"dnorm"=,
"dlaplace"=,
"ds"=,
"dgnorm"=,
"dlogis"=,
"dt"=,
"dalaplace"=,
object$residuals));
}
#' Multiple steps ahead forecast errors
#'
#' The function extracts 1 to h steps ahead forecast errors from the model.
#'
#' The errors correspond to the error term epsilon_t in the ETS models. Don't forget
#' that different models make different assumptions about epsilon_t and / or 1+epsilon_t.
#'
#' @template ssAuthor
#' @template ssKeywords
#'
#' @param object Model estimated using one of the forecasting functions.
#' @param h The forecasting horizon to use.
#' @param ... Currently nothing is accepted via ellipsis.
#' @return The matrix with observations in rows and h steps ahead values in columns.
#' So, the first row corresponds to the forecast produced from the 0th observation
#' from 1 to h steps ahead.
#' @seealso \link[stats]{residuals},
#' @examples
#'
#' x <- rnorm(100,0,1)
#' ourModel <- adam(x)
#' rmultistep(ourModel, h=13)
#'
#' @export rmultistep
rmultistep <- function(object, h=10, ...) UseMethod("rmultistep")
#' @export
rmultistep.default <- function(object, h=10, ...){
return(NULL);
}
#' @export
rmultistep.adam <- function(object, h=10, ...){
yClasses <- class(actuals(object));
# Model type
model <- modelType(object);
Etype <- errorType(object);
Ttype <- substr(model,2,2);
Stype <- substr(model,nchar(model),nchar(model));
# Technical parameters
lagsModelAll <- modelLags(object);
lagsModelMax <- max(lagsModelAll);
lagsOriginal <- lags(object);
if(Ttype!="N"){
lagsOriginal <- c(1,lagsOriginal);
}
if(!is.null(object$initial$seasonal)){
if(is.list(object$initial$seasonal)){
componentsNumberETSSeasonal <- length(object$initial$seasonal);
}
else{
componentsNumberETSSeasonal <- 1;
}
}
else{
componentsNumberETSSeasonal <- 0;
}
componentsNumberETS <- length(object$initial$level) + length(object$initial$trend) + componentsNumberETSSeasonal;
componentsNumberARIMA <- sum(substr(colnames(object$states),1,10)=="ARIMAState");
if(ncol(object$data)>1){
xregNumber <- ncol(object$data)-1;
}
else{
xregNumber <- 0;
}
obsInSample <- nobs(object);
constantRequired <- !is.null(object$constant);
# Function returns the matrix with multi-step errors
if(is.occurrence(object$occurrence)){
ot <- matrix(actuals(object$occurrence),obsInSample,1);
}
else{
ot <- matrix(1,obsInSample,1);
}
adamProfiles <- adamProfileCreator(lagsModelAll, lagsModelMax, obsInSample,
lagsOriginal, time(actuals(object)), yClasses);
profilesRecentTable <- adamProfiles$recent;
indexLookupTable <- adamProfiles$lookup;
# Fill in the profile. This is done in Errorer as well, but this is just in case
profilesRecentTable[] <- t(object$states[1:lagsModelMax,,drop=FALSE]);
# Return multi-step errors matrix
if(any(yClasses=="ts")){
return(ts(adamErrorerWrap(t(object$states), object$measurement, object$transition,
lagsModelAll, indexLookupTable, profilesRecentTable,
Etype, Ttype, Stype,
componentsNumberETS, componentsNumberETSSeasonal,
componentsNumberARIMA, xregNumber, constantRequired, h,
matrix(actuals(object),obsInSample,1), ot),
start=start(actuals(object)), frequency=frequency(actuals(object))));
}
else{
return(zoo(adamErrorerWrap(t(object$states), object$measurement, object$transition,
lagsModelAll, indexLookupTable, profilesRecentTable,
Etype, Ttype, Stype,
componentsNumberETS, componentsNumberETSSeasonal,
componentsNumberARIMA, xregNumber, constantRequired, h,
matrix(actuals(object),obsInSample,1), ot),
order.by=time(actuals(object))));
}
}
#' @importFrom stats rstandard
#' @export
rstandard.adam <- function(model, ...){
obs <- nobs(model);
df <- obs - nparam(model);
errors <- residuals(model);
# If this is an occurrence model, then only modify the non-zero obs
# Also, if there are NAs in actuals, consider them as occurrence
if(is.occurrence(model$occurrence)){
residsToGo <- which(actuals(model$occurrence)!=0 & !is.na(actuals(model)));
}
else{
residsToGo <- c(1:obs);
}
if(any(model$distribution==c("dt","dnorm"))){
return((errors - mean(errors[residsToGo])) / sqrt(extractScale(model)^2 * obs / df));
}
else if(model$distribution=="ds"){
return((errors - mean(errors[residsToGo])) / (extractScale(model) * obs / df)^2);
}
else if(model$distribution=="dls"){
errors[] <- log(errors);
return(exp((errors - mean(errors[residsToGo])) / (extractScale(model) * obs / df)^2));
}
else if(model$distribution=="dgnorm"){
return((errors - mean(errors[residsToGo])) / (extractScale(model)^model$other$shape * obs / df)^{1/model$other$shape});
}
else if(model$distribution=="dlgnorm"){
errors[] <- log(errors);
return(exp((errors - mean(errors[residsToGo])) / (extractScale(model)^model$other$shape * obs / df)^{1/model$other$shape}));
}
else if(any(model$distribution==c("dinvgauss","dgamma"))){
return(errors / mean(errors[residsToGo]));
}
else if(model$distribution=="dlnorm"){
# Debias the residuals
errors[] <- log(errors) + extractScale(model)^2/2;
return(exp((errors - mean(errors[residsToGo])) / sqrt(extractScale(model)^2 * obs / df)));
}
else if(model$distribution=="dllaplace"){
errors[] <- log(errors);
return(exp((errors - mean(errors[residsToGo])) / extractScale(model) * obs / df));
}
else{
return(errors / extractScale(model) * obs / df);
}
}
#' @importFrom stats rstudent
#' @export
rstudent.adam <- function(model, ...){
obs <- nobs(model);
df <- obs - nparam(model) - 1;
rstudentised <- errors <- residuals(model);
# If this is an occurrence model, then only modify the non-zero obs
# Also, if there are NAs in actuals, consider them as occurrence
if(is.occurrence(model$occurrence)){
residsToGo <- which(actuals(model$occurrence)!=0 & !is.na(actuals(model)));
}
else{
residsToGo <- c(1:obs);
}
if(any(model$distribution==c("dt","dnorm"))){
errors[] <- errors - mean(errors);
for(i in residsToGo){
rstudentised[i] <- errors[i] / sqrt(sum(errors[-i]^2,na.rm=TRUE) / df);
}
}
else if(model$distribution=="dlaplace"){
errors[] <- errors - mean(errors);
for(i in residsToGo){
rstudentised[i] <- errors[i] / (sum(abs(errors[-i]),na.rm=TRUE) / df);
}
}
else if(model$distribution=="dlnorm"){
errors[] <- log(errors) - mean(log(errors)) - extractScale(model)^2/2;
for(i in residsToGo){
rstudentised[i] <- exp(errors[i] / sqrt(sum(errors[-i]^2,na.rm=TRUE) / df));
}
}
else if(model$distribution=="dllaplace"){
errors[] <- log(errors) - mean(log(errors));
for(i in residsToGo){
rstudentised[i] <- exp(errors[i] / (sum(abs(errors[-i]),na.rm=TRUE) / df));
}
}
else if(model$distribution=="ds"){
errors[] <- errors - mean(errors);
for(i in residsToGo){
rstudentised[i] <- errors[i] / (sum(sqrt(abs(errors[-i])),na.rm=TRUE) / (2*df))^2;
}
}
else if(model$distribution=="dls"){
errors[] <- log(errors) - mean(log(errors));
for(i in residsToGo){
rstudentised[i] <- exp(errors[i] / (sum(sqrt(abs(errors[-i])),na.rm=TRUE) / (2*df))^2);
}
}
else if(model$distribution=="dgnorm"){
errors[] <- errors - mean(errors);
for(i in residsToGo){
rstudentised[i] <- errors[i] / (sum(abs(errors[-i])^model$other$shape) * (model$other$shape/df))^{1/model$other$shape};
}
}
else if(model$distribution=="dlgnorm"){
errors[] <- log(errors) - mean(log(errors));
for(i in residsToGo){
rstudentised[i] <- errors[i] / (sum(abs(errors[-i])^model$other$shape) * (model$other$shape/df))^{1/model$other$shape};
}
}
else if(model$distribution=="dalaplace"){
for(i in residsToGo){
rstudentised[i] <- errors[i] / (sum(errors[-i] * (model$other$alpha - (errors[-i]<=0)*1),na.rm=TRUE) / df);
}
}
else if(model$distribution=="dlogis"){
errors[] <- errors - mean(errors);
for(i in residsToGo){
rstudentised[i] <- errors[i] / (sqrt(sum(errors[-i]^2,na.rm=TRUE) / df) * sqrt(3) / pi);
}
}
else if(any(model$distribution==c("dinvgauss","dgamma"))){
for(i in residsToGo){
rstudentised[i] <- errors[i] / mean(errors[residsToGo][-i],na.rm=TRUE);
}
}
else{
for(i in residsToGo){
rstudentised[i] <- errors[i] / sqrt(sum(errors[-i]^2,na.rm=TRUE) / df);
}
}
return(rstudentised);
}
#' @importFrom greybox outlierdummy
#' @importFrom stats qchisq
#' @export
outlierdummy.adam <- function(object, level=0.999, type=c("rstandard","rstudent"), ...){
# Function returns the matrix of dummies with outliers
type <- match.arg(type);
errors <- switch(type,"rstandard"=rstandard(object),"rstudent"=rstudent(object));
statistic <- switch(object$distribution,
"dlaplace"=,
"dllaplace"=qlaplace(c((1-level)/2, (1+level)/2), 0, 1),
"dalaplace"=qalaplace(c((1-level)/2, (1+level)/2), 0, 1, object$other$alpha),
"dlogis"=qlogis(c((1-level)/2, (1+level)/2), 0, 1),
"dt"=qt(c((1-level)/2, (1+level)/2), nobs(object)-nparam(object)),
"dgnorm"=,
"dlgnorm"=qgnorm(c((1-level)/2, (1+level)/2), 0, 1, object$other$shape),
"ds"=,
"dls"=qs(c((1-level)/2, (1+level)/2), 0, 1),
# In the next one, the scale is debiased, taking n-k into account
"dinvgauss"=qinvgauss(c((1-level)/2, (1+level)/2), mean=1,
dispersion=mean(extractScale(object)) * nobs(object) /
(nobs(object)-nparam(object))),
"dgamma"=qgamma(c((1-level)/2, (1+level)/2), shape=1/extractScale(object), scale=extractScale(object)),
qnorm(c((1-level)/2, (1+level)/2), 0, 1));
# Fix for IG in case of scale - it should be chi-squared
if(is.scale(object) && object$distribution=="dinvgauss"){
statistic <- qchisq(c((1-level)/2, (1+level)/2), 1);
}
if(any(object$distribution==c("dlnorm","dllaplace","dls","dlgnorm"))){
errors[] <- log(errors);
}
outliersID <- which(errors>statistic[2] | errors<statistic[1]);
outliersNumber <- length(outliersID);
if(outliersNumber>0){
outliers <- matrix(0, nobs(object), outliersNumber,
dimnames=list(rownames(actuals(object)),
paste0("outlier",c(1:outliersNumber))));
outliers[cbind(outliersID,c(1:outliersNumber))] <- 1;
}
else{
outliers <- NULL;
}
return(structure(list(outliers=outliers, statistic=statistic, id=outliersID,
level=level, type=type),
class="outlierdummy"));
}
#### Predict and forecast functions ####
#' @export
predict.adam <- function(object, newdata=NULL, interval=c("none", "confidence", "prediction"),
level=0.95, side=c("both","upper","lower"), ...){
interval <- match.arg(interval);
obsInSample <- nobs(object);
# Indices and classes of the original data
yIndex <- time(actuals(object));
yClasses <- class(actuals(object));
if(any(yClasses=="ts")){
# ts structure
yStart <- yIndex[1];
yFrequency <- frequency(actuals(object));
}
# Check if newdata is provided
if(!is.null(newdata)){
# If this is not a matrix / data.frame, then convert to one
if(!is.data.frame(newdata) && !is.matrix(newdata)){
newdata <- as.data.frame(newdata);
colnames(newdata) <- "xreg";
}
h <- nrow(newdata);
# If the newdata is provided, then just do forecasts for that part
if(any(interval==c("none","prediction","confidence"))){
if(interval==c("prediction")){
interval[] <- "simulated";
}
return(forecast(object, h=h, newdata=newdata,
interval=interval,
level=level, side=side, ...));
}
}
else{
# If there are no newdata, then we need to produce fitted with / without interval
if(interval=="none"){
return(structure(list(mean=fitted(object), lower=NA, upper=NA, model=object,
level=level, interval=interval, side=side),
class=c("adam.predict","adam.forecast")));
}
# Otherwise we do one-step-ahead prediction / confidence interval
else{
yForecast <- fitted(object);
}
}
##### Prediction interval for in sample only! #####
side <- match.arg(side);
if(length(level)>1){
warning(paste0("Sorry, but I only support scalar for the level, ",
"when constructing in-sample 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;
}
# Basic parameters
model <- modelType(object);
Etype <- errorType(object);
# Extract variance and amend it in case of confidence interval
s2 <- sigma(object)^2;
# If this is a mixture model, produce forecasts for the occurrence
if(!is.null(object$occurrence)){
occurrenceModel <- TRUE;
pForecast <- fitted(object$occurrence);
}
else{
occurrenceModel <- FALSE;
pForecast <- rep(1, obsInSample);
}
# If this is an occurrence model, then take probability into account in the level.
if(occurrenceModel && (interval=="prediction")){
levelNew <- (level-(1-pForecast))/pForecast;
levelNew[levelNew<0] <- 0;
}
else{
levelNew <- level;
}
levelLow <- levelUp <- vector("numeric",obsInSample);
if(side=="both"){
levelLow[] <- (1-levelNew)/2;
levelUp[] <- (1+levelNew)/2;
}
else if(side=="upper"){
levelLow[] <- rep(0,length(levelNew));
levelUp[] <- levelNew;
}
else{
levelLow[] <- 1-levelNew;
levelUp[] <- rep(1,length(levelNew));
}
levelLow[levelLow<0] <- 0;
levelUp[levelUp<0] <- 0;
nLevels <- 1;
# Matrices for levels
# Create necessary matrices for the forecasts
if(any(yClasses=="ts")){
yUpper <- yLower <- ts(matrix(0,obsInSample,nLevels), start=yStart, frequency=yFrequency);
}
else{
yUpper <- yLower <- zoo(matrix(0,obsInSample,nLevels), order.by=yIndex);
}
colnames(yLower) <- switch(side,
"both"=paste0("Lower bound (",(1-level)/2*100,"%)"),
"lower"=paste0("Lower bound (",(1-level)*100,"%)"),
"upper"=rep("Lower 0%",nLevels));
colnames(yUpper) <- switch(side,
"both"=paste0("Upper bound (",(1+level)/2*100,"%)"),
"lower"=rep("Upper 100%",nLevels),
"upper"=paste0("Upper bound (",level*100,"%)"));
#### Call reapply if this is confidence ####
if(interval=="confidence"){
yFittedMatrix <- reapply(object, ...);
for(i in 1:obsInSample){
yUpper[i] <- quantile(yFittedMatrix$refitted[i,], levelLow[i], na.rm=TRUE);
yLower[i] <- quantile(yFittedMatrix$refitted[i,], levelUp[i], na.rm=TRUE);
}
return(structure(list(mean=yForecast, lower=yLower, upper=yUpper, model=object,
level=level, interval=interval, side=side),
class=c("adam.predict","adam.forecast")))
}
#### Produce the prediction intervals ####
if(object$distribution=="dnorm"){
if(Etype=="A"){
yLower[] <- qnorm(levelLow, 0, sqrt(s2));
yUpper[] <- qnorm(levelUp, 0, sqrt(s2));
}
else{
yLower[] <- qnorm(levelLow, 1, sqrt(s2));
yUpper[] <- qnorm(levelUp, 1, sqrt(s2));
}
}
else if(object$distribution=="dlaplace"){
if(Etype=="A"){
yLower[] <- qlaplace(levelLow, 0, sqrt(s2/2));
yUpper[] <- qlaplace(levelUp, 0, sqrt(s2/2));
}
else{
yLower[] <- qlaplace(levelLow, 1, sqrt(s2/2));
yUpper[] <- qlaplace(levelUp, 1, sqrt(s2/2));
}
}
else if(object$distribution=="ds"){
if(Etype=="A"){
yLower[] <- qs(levelLow, 0, (s2/120)^0.25);
yUpper[] <- qs(levelUp, 0, (s2/120)^0.25);
}
else{
yLower[] <- qs(levelLow, 1, (s2/120)^0.25);
yUpper[] <- qs(levelUp, 1, (s2/120)^0.25);
}
}
else if(object$distribution=="dgnorm"){
scale <- sqrt(s2*(gamma(1/object$other$shape)/gamma(3/object$other$shape)));
if(Etype=="A"){
yLower[] <- suppressWarnings(qgnorm(levelLow, 0, scale, object$other$shape));
yUpper[] <- suppressWarnings(qgnorm(levelUp, 0, scale, object$other$shape));
}
else{
yLower[] <- suppressWarnings(qgnorm(levelLow, 1, scale, object$other$shape));
yUpper[] <- suppressWarnings(qgnorm(levelUp, 1, scale, object$other$shape));
}
}
else if(object$distribution=="dlogis"){
if(Etype=="A"){
yLower[] <- qlogis(levelLow, 0, sqrt(s2*3)/pi);
yUpper[] <- qlogis(levelUp, 0, sqrt(s2*3)/pi);
}
else{
yLower[] <- qlogis(levelLow, 1, sqrt(s2*3)/pi);
yUpper[] <- qlogis(levelUp, 1, sqrt(s2*3)/pi);
}
}
else if(object$distribution=="dt"){
df <- nobs(object) - nparam(object);
if(Etype=="A"){
yLower[] <- sqrt(s2)*qt(levelLow, df);
yUpper[] <- sqrt(s2)*qt(levelUp, df);
}
else{
yLower[] <- (1 + sqrt(s2)*qt(levelLow, df));
yUpper[] <- (1 + sqrt(s2)*qt(levelUp, df));
}
}
else if(object$distribution=="dalaplace"){
alpha <- object$other$alpha;
if(Etype=="A"){
yLower[] <- qalaplace(levelLow, 0,
sqrt(s2*alpha^2*(1-alpha)^2/(alpha^2+(1-alpha)^2)), alpha);
yUpper[] <- qalaplace(levelUp, 0,
sqrt(s2*alpha^2*(1-alpha)^2/(alpha^2+(1-alpha)^2)), alpha);
}
else{
yLower[] <- qalaplace(levelLow, 1,
sqrt(s2*alpha^2*(1-alpha)^2/(alpha^2+(1-alpha)^2)), alpha);
yUpper[] <- qalaplace(levelUp, 1,
sqrt(s2*alpha^2*(1-alpha)^2/(alpha^2+(1-alpha)^2)), alpha);
}
}
else if(object$distribution=="dlnorm"){
# Take into account the logN restrictions
yLower[] <- qlnorm(levelLow, -(1-sqrt(abs(1-s2)))^2, sqrt(2*(1-sqrt(abs(1-s2)))));
yUpper[] <- qlnorm(levelUp, -(1-sqrt(abs(1-s2)))^2, sqrt(2*(1-sqrt(abs(1-s2)))));
}
else if(object$distribution=="dllaplace"){
yLower[] <- exp(qlaplace(levelLow, 0, sqrt(s2/2)));
yUpper[] <- exp(qlaplace(levelUp, 0, sqrt(s2/2)));
}
else if(object$distribution=="dls"){
yLower[] <- exp(qs(levelLow, 0, (s2/120)^0.25));
yUpper[] <- exp(qs(levelUp, 0, (s2/120)^0.25));
}
else if(object$distribution=="dlgnorm"){
scale <- sqrt(s2*(gamma(1/object$other$shape)/gamma(3/object$other$shape)));
yLower[] <- suppressWarnings(exp(qgnorm(levelLow, 0, scale, object$other$shape)));
yUpper[] <- suppressWarnings(exp(qgnorm(levelUp, 0, scale, object$other$shape)));
}
else if(object$distribution=="dinvgauss"){
yLower[] <- qinvgauss(levelLow, 1, dispersion=s2);
yUpper[] <- qinvgauss(levelUp, 1, dispersion=s2);
}
else if(object$distribution=="dgamma"){
yLower[] <- qgamma(levelLow, shape=1/s2, scale=s2);
yUpper[] <- qgamma(levelUp, shape=1/s2, scale=s2);
}
#### Clean up the produced values for the interval ####
# Make sensible values out of those weird quantiles
if(Etype=="A"){
yLower[levelLow==0] <- -Inf;
}
else{
yLower[levelLow==0] <- 0;
}
yUpper[levelUp==1] <- Inf;
# Substitute NAs and NaNs with zeroes
if(any(is.nan(yLower)) || any(is.na(yLower))){
yLower[is.nan(yLower)] <- 0;
yLower[is.na(yLower)] <- 0;
}
if(any(is.nan(yUpper)) || any(is.na(yUpper))){
yUpper[is.nan(yUpper)] <- 0;
yUpper[is.na(yUpper)] <- 0;
}
if(Etype=="A"){
yLower[] <- yForecast + yLower;
yUpper[] <- yForecast + yUpper;
}
else{
yLower[] <- yForecast * yLower;
yUpper[] <- yForecast * yUpper;
}
return(structure(list(mean=yForecast, lower=yLower, upper=yUpper, model=object,
level=level, interval=interval, side=side),
class=c("adam.predict","adam.forecast")));
}
#' @export
plot.adam.predict <- function(x, ...){
ellipsis <- list(...);
if(is.null(ellipsis$ylim)){
ellipsis$ylim <- range(c(actuals(x$model),x$mean,x$lower,x$upper),na.rm=TRUE);
}
ellipsis$x <- actuals(x$model);
do.call(plot, ellipsis);
lines(x$mean,col="purple",lwd=2,lty=2);
if(x$interval!="none"){
lines(x$lower,col="grey",lwd=3,lty=2);
lines(x$upper,col="grey",lwd=3,lty=2);
}
}
#' @param newdata The new data needed in order to produce forecasts.
#' @param nsim Number of iterations to do in cases of \code{interval="simulated"},
#' \code{interval="prediction"} (for mixed and multiplicative model),
#' \code{interval="confidence"} and \code{interval="complete"}.
#' The default value for the prediction / simulated interval is 1000. In case of
#' confidence or complete intervals, this is set to 100.
#' @param interval What type of mechanism to use for interval construction.
#' the recommended option is \code{interval="prediction"}, which will use analytical
#' solutions for pure additive models and simulations for the others.
#' \code{interval="simulated"} is the slowest method, but is robust to the type of
#' model. \code{interval="approximate"} (aka \code{interval="parametric"}) uses
#' analytical formulae for conditional h-steps ahead variance, but is approximate
#' for the non-additive error models. \code{interval="semiparametric"} relies on the
#' multiple steps ahead forecast error (extracted via \code{rmultistep} method) and on
#' the assumed distribution of the error term. \code{interval="nonparametric"} uses
#' Taylor & Bunn (1999) approach with quantile regressions. \code{interval="empirical"}
#' constructs intervals based on empirical quantiles of multistep forecast errors.
#' \code{interval="complete"} will call for \code{reforecast()} function and produce
#' interval based on the uncertainty around the parameters of the model.
#' Finally, \code{interval="confidence"} tries to generate the confidence intervals
#' for the point forecast based on the \code{reforecast} method.
#' @param cumulative If \code{TRUE}, then the cumulative forecast and prediction
#' interval are produced instead of the normal ones. This is useful for
#' inventory control systems.
#' @param occurrence The vector containing the future occurrence variable
#' (values in [0,1]), if it is known.
#' @param scenarios Binary, defining whether to return scenarios produced via
#' simulations or not. Only works if \code{interval="simulated"}. If \code{TRUE}
#' the object will contain \code{scenarios} variable.
#' @rdname forecast.smooth
#' @importFrom stats rnorm rlogis rt rlnorm rgamma
#' @importFrom stats qnorm qlogis qt qlnorm qgamma
#' @importFrom statmod rinvgauss qinvgauss
#' @importFrom greybox rlaplace rs ralaplace rgnorm
#' @importFrom greybox qlaplace qs qalaplace qgnorm
#' @export
forecast.adam <- function(object, h=10, newdata=NULL, occurrence=NULL,
interval=c("none", "prediction", "confidence", "simulated",
"approximate", "semiparametric", "nonparametric",
"empirical","complete"),
level=0.95, side=c("both","upper","lower"), cumulative=FALSE, nsim=NULL,
scenarios=FALSE, ...){
ellipsis <- list(...);
interval <- match.arg(interval[1],c("none", "simulated", "approximate", "semiparametric",
"nonparametric", "confidence", "parametric","prediction",
"empirical","complete"));
# If the horizon is zero, just construct fitted and potentially confidence interval thingy
if(h<=0){
if(all(interval!=c("none","confidence"))){
interval[] <- "prediction";
}
return(predict(object, newdata=newdata,
interval=interval,
level=level, side=side, ...));
}
else{
if(interval=="confidence"){
if(is.null(nsim)){
nsim <- 100;
}
return(reforecast(object, h=h, newdata=newdata, occurrence=occurrence,
interval=interval, level=level, side=side, cumulative=cumulative,
nsim=nsim, ...));
}
}
if(interval=="parametric"){
interval <- "prediction";
}
else if(interval=="complete"){
if(is.null(nsim)){
nsim <- 100;
}
return(reforecast(object, h=h, newdata=newdata, occurrence=occurrence,
interval="prediction", level=level, side=side, cumulative=cumulative,
nsim=nsim, ...));
}
side <- match.arg(side);
# If nsim is null, set it to 10000
if(is.null(nsim)){
nsim <- 10000;
}
# Model type
model <- modelType(object);
Etype <- errorType(object);
Ttype <- substr(model,2,2);
damped <- substr(model,3,3)=="d";
Stype <- substr(model,nchar(model),nchar(model));
etsModel <- any(unlist(gregexpr("ETS",object$model))!=-1);
arimaModel <- any(unlist(gregexpr("ARIMA",object$model))!=-1);
# Technical parameters
lagsModelAll <- modelLags(object);
lagsModelMax <- max(lagsModelAll);
# This is needed in order to see, whether h>m or not in seasonal models
lagsModelMin <- lagsModelAll[lagsModelAll!=1];
if(length(lagsModelMin)==0){
lagsModelMin <- Inf;
}
else{
lagsModelMin <- min(lagsModelMin);
}
profilesRecentTable <- object$profile;
if(!is.null(object$initial$seasonal)){
if(is.list(object$initial$seasonal)){
componentsNumberETSSeasonal <- length(object$initial$seasonal);
}
else{
componentsNumberETSSeasonal <- 1;
}
}
else{
componentsNumberETSSeasonal <- 0;
}
componentsNumberETS <- length(object$initial$level) + length(object$initial$trend) + componentsNumberETSSeasonal;
componentsNumberARIMA <- sum(substr(colnames(object$states),1,10)=="ARIMAState");
obsStates <- nrow(object$states);
obsInSample <- nobs(object);
yIndex <- time(actuals(object));
yClasses <- class(actuals(object));
# Create indices for the future
if(any(yClasses=="ts")){
# ts structure
yForecastStart <- time(actuals(object))[obsInSample]+deltat(actuals(object));
yFrequency <- frequency(actuals(object));
yForecastIndex <- yIndex[obsInSample]+as.numeric(diff(tail(yIndex,2)))*c(1:h);
}
else{
# zoo
yIndex <- time(actuals(object));
yForecastIndex <- yIndex[obsInSample]+diff(tail(yIndex,2))*c(1:h);
}
# Get the lookup table
indexLookupTable <- adamProfileCreator(lagsModelAll, lagsModelMax, obsInSample+h,
lags(object), c(yIndex,yForecastIndex),
yClasses)$lookup[,-c(1:(obsInSample+lagsModelMax)),drop=FALSE];
# All the important matrices
matVt <- t(object$states[obsStates-(lagsModelMax:1)+1,,drop=FALSE]);
matWt <- tail(object$measurement,h);
# If the forecast horizon is higher than the in-sample, duplicate the last value in matWt
if(nrow(matWt)<h){
matWt <- matrix(tail(matWt,1), nrow=h, ncol=ncol(matWt), dimnames=list(NULL,colnames(matWt)), byrow=TRUE);
}
vecG <- matrix(object$persistence, ncol=1);
# Deal with explanatory variables
if(ncol(object$data)>1){
xregNumber <- length(object$initial$xreg);
xregNames <- names(object$initial$xreg);
# The newdata is not provided
if(is.null(newdata) && ((!is.null(object$holdout) && nrow(object$holdout)<h) ||
is.null(object$holdout))){
# Salvage what data we can (if there is something)
if(!is.null(object$holdout)){
hNeeded <- h-nrow(object$holdout);
xreg <- tail(object$data,h);
xreg[1:nrow(object$holdout),] <- object$holdout;
}
else{
hNeeded <- h;
xreg <- tail(object$data,h);
}
if(is.matrix(xreg)){
warning("The newdata is not provided.",
"Predicting the explanatory variables based on what I have in-sample.",
call.=FALSE);
for(i in 1:xregNumber){
xreg[,i] <- adam(object$data[,i+1],h=hNeeded,silent=TRUE)$forecast;
}
}
else{
warning("The newdata is not provided. Using last h in-sample observations instead.",
call.=FALSE);
}
}
# The newdata is not provided, but we have holdout
else if(is.null(newdata) && !is.null(object$holdout) && nrow(object$holdout)>=h){
xreg <- object$holdout[1:h,,drop=FALSE];
}
# The newdata is provided
else{
# If this is not a matrix / data.frame, then convert to one
if(!is.data.frame(newdata) && !is.matrix(newdata)){
newdata <- as.data.frame(newdata);
colnames(newdata) <- "xreg";
}
if(nrow(newdata)<h){
warning(paste0("The newdata has ",nrow(newdata)," observations, while ",h," are needed. ",
"Using the last available values as future ones."),
call.=FALSE);
newnRows <- h-nrow(newdata);
xreg <- newdata[c(1:nrow(newdata),rep(nrow(newdata)),each=newnRows),];
# xreg <- rbind(newdata,
# data.frame(matrix(rep(tail(newdata,1),each=newnRows),
# newnRows,ncol(newdata),
# dimnames=list(NULL,colnames(newdata))))
# );
}
else if(nrow(newdata)>h){
warning(paste0("The newdata has ",nrow(newdata)," observations, while only ",h," are needed. ",
"Using the last ",h," of them."),
call.=FALSE);
xreg <- tail(newdata,h);
}
else{
xreg <- newdata;
}
if(any(is.na(xreg))){
warning("The newdata has NAs. This might cause some issues.",
call.=FALSE);
}
}
# If the user asked for trend, but it's not in the data, add it
if(any(all.vars(formula(object))=="trend") && all(colnames(object$data)!="trend")){
xreg <- cbind(xreg,trend=nobs(object)+c(1:h));
}
# If the names are wrong, transform to data frame and expand
if(!all(xregNames %in% colnames(xreg)) && !is.data.frame(xreg)){
xreg <- as.data.frame(xreg);
}
# Expand the xreg if it is data frame to get the proper matrix
if(is.data.frame(xreg)){
testFormula <- formula(object);
# Remove response variable
testFormula[[2]] <- NULL;
colnames(xreg) <- make.names(colnames(xreg));
# Expand the variables. We cannot use alm, because it is based on obsInSample
xregData <- model.frame(testFormula,data=xreg);
# Binary, flagging factors in the data
# Expanded stuff with all levels for factors
if(any((attr(terms(xregData),"dataClasses")=="factor"))){
xregModelMatrix <- model.matrix(xregData,xregData,
contrasts.arg=lapply(xregData[attr(terms(xregData),"dataClasses")=="factor"],
contrasts, contrasts=FALSE));
}
else{
xregModelMatrix <- model.matrix(xregData,data=xregData);
}
xregNames[] <- make.names(xregNames, unique=TRUE);
colnames(xregModelMatrix) <- make.names(colnames(xregModelMatrix), unique=TRUE);
newdata <- as.matrix(xregModelMatrix)[,xregNames,drop=FALSE];
rm(xregData,xregModelMatrix);
}
else{
colnames(xreg) <- make.names(colnames(xreg));
newdata <- xreg[,xregNames,drop=FALSE];
}
rm(xreg);
# From 1 to nrow to address potential missing values
matWt[1:nrow(newdata),componentsNumberETS+componentsNumberARIMA+c(1:xregNumber)] <- newdata;
}
else{
xregNumber <- 0;
# If the user asked for trend, but it's not in the data, add it
if(any(all.vars(formula(object))=="trend") && all(colnames(object$data)!="trend")){
xreg <- matrix(nobs(object)+c(1:h),h,1);
xregNumber <- 1;
}
}
matF <- object$transition;
# If this is "prediction", do simulations for multiplicative components
if(interval=="prediction"){
# Simulate stuff for the ETS only
if((etsModel || xregNumber>0) &&
(Ttype=="M" || (Stype=="M" & h>lagsModelMin))){
interval <- "simulated";
}
else{
interval <- "approximate";
}
}
# See if constant is required
constantRequired <- !is.null(object$constant);
# Produce point forecasts for non-multiplicative trend / seasonality
# Do this for cases, when h<=m as well and prediction /confidence / simulated interval
if(Ttype!="M" && (Stype!="M" | (Stype=="M" & h<=lagsModelMin)) ||
any(interval==c("nonparametric","semiparametric","empirical","approximate"))){
adamForecast <- adamForecasterWrap(matWt, matF,
lagsModelAll, indexLookupTable, profilesRecentTable,
Etype, Ttype, Stype,
componentsNumberETS, componentsNumberETSSeasonal,
componentsNumberARIMA, xregNumber, constantRequired,
h);
}
else{
# If we do simulations, leave it for later
if(interval=="simulated"){
adamForecast <- rep(0, h);
}
# If we don't, do simulations to get mean
else{
adamForecast <- forecast(object, h=h, newdata=newdata, occurrence=occurrence,
interval="simulated",
level=level, side="both", cumulative=cumulative, nsim=nsim, ...)$mean;
}
}
#### Make safety checks
# If there are NaN values
if(any(is.nan(adamForecast))){
adamForecast[is.nan(adamForecast)] <- 0;
}
# Make a warning about the potential explosive trend
if(Ttype=="M" && !damped && profilesRecentTable[2,1]>1 && h>10){
warning("Your model has a potentially explosive multiplicative trend. ",
"I cannot do anything about it, so please just be careful.",
call.=FALSE);
}
occurrenceModel <- FALSE;
# If the occurrence values are provided for the holdout
if(!is.null(occurrence) && is.logical(occurrence)){
pForecast <- occurrence*1;
}
else if(!is.null(occurrence) && is.numeric(occurrence)){
pForecast <- occurrence;
}
else{
# If this is a mixture model, produce forecasts for the occurrence
if(is.occurrence(object$occurrence)){
occurrenceModel[] <- TRUE;
if(object$occurrence$occurrence=="provided"){
pForecast <- rep(1,h);
}
else{
pForecast <- forecast(object$occurrence, h=h, newdata=newdata)$mean;
}
}
else{
occurrenceModel[] <- FALSE;
# If this was provided occurrence, then use provided values
if(!is.null(object$occurrence) && !is.null(object$occurrence$occurrence) &&
(object$occurrence$occurrence=="provided") && !is.na(object$occurrence$forecast)){
pForecast <- object$occurrence$forecast;
}
else{
pForecast <- rep(1, h);
}
}
}
# Make sure that the values are of the correct length
if(h<length(pForecast)){
pForecast <- pForecast[1:h];
}
else if(h>length(pForecast)){
pForecast <- c(pForecast, rep(tail(pForecast,1), h-length(pForecast)));
}
# How many levels did user asked to produce
nLevels <- length(level);
# Cumulative forecasts have only one observation
if(cumulative){
# hFinal is the number of elements we will have in the final forecast
hFinal <- 1;
# In case of occurrence model use simulations - the cumulative probability is a bitch
if(occurrenceModel){
interval[] <- "simulated";
}
}
else{
hFinal <- h;
}
# Create necessary matrices for the forecasts
if(any(yClasses=="ts")){
yForecast <- ts(vector("numeric", hFinal), start=yForecastStart, frequency=yFrequency);
yUpper <- yLower <- ts(matrix(0,hFinal,nLevels), start=yForecastStart, frequency=yFrequency);
}
else{
if(cumulative){
yForecast <- zoo(vector("numeric", hFinal), order.by=yForecastIndex[1]);
yUpper <- yLower <- zoo(matrix(0,hFinal,nLevels), order.by=yForecastIndex[1]);
}
else{
yForecast <- zoo(vector("numeric", hFinal), order.by=yForecastIndex);
yUpper <- yLower <- zoo(matrix(0,hFinal,nLevels), order.by=yForecastIndex);
}
}
# Fill in the point forecasts
if(cumulative){
yForecast[] <- sum(as.vector(adamForecast) * as.vector(pForecast));
}
else{
yForecast[] <- as.vector(adamForecast) * as.vector(pForecast);
}
if(interval!="none"){
# Fix just in case a silly user used 95 etc instead of 0.95
if(any(level>1)){
level[] <- level / 100;
}
levelLow <- levelUp <- matrix(0,nrow=hFinal,ncol=nLevels);
levelNew <- matrix(level,nrow=hFinal,ncol=nLevels,byrow=TRUE);
# If this is an occurrence model, then take probability into account in the level.
# This correction is only needed for approximate, because the others contain zeroes
if(occurrenceModel && interval=="approximate"){
levelNew[] <- (levelNew-(1-as.vector(pForecast)))/as.vector(pForecast);
levelNew[levelNew<0] <- 0;
}
if(side=="both"){
levelLow[] <- (1-levelNew)/2;
levelUp[] <- (1+levelNew)/2;
}
else if(side=="upper"){
levelLow[] <- 0;
levelUp[] <- levelNew;
}
else{
levelLow[] <- 1-levelNew;
levelUp[] <- 1;
}
levelLow[levelLow<0] <- 0;
levelUp[levelUp<0] <- 0;
}
#### Simulated interval ####
if(interval=="simulated"){
arrVt <- array(NA, c(componentsNumberETS+componentsNumberARIMA+xregNumber+constantRequired, h+lagsModelMax, nsim));
arrVt[,1:lagsModelMax,] <- rep(matVt,nsim);
# Number of degrees of freedom to de-bias scales
df <- (nobs(object, all=FALSE)-nparam(object));
# If the sample is too small, then use biased estimator
if(df<=0){
df[] <- nobs(object, all=FALSE);
}
# If scale model is included, produce forecasts
if(is.scale(object$scale)){
# as.vector is needed to declass the mean.
scaleValue <- as.vector(forecast(object$scale,h=h,newdata=newdata,interval="none")$mean);
# De-bias the scales and transform to the appropriate scale
# dnorm, dlnorm fit model on square residuals
# dgnorm needs to be done with ^beta to get to 1/T part
# The rest do not require transformations, only de-bias
scaleValue[] <- switch(object$distribution,
"dlnorm"=,
"dnorm"=(scaleValue*obsInSample/df)^0.5,
"dgnorm"=((scaleValue^object$other$shape)*obsInSample/df)^{1/object$other$shape},
scaleValue*obsInSample/df);
}
else{
scaleValue <- object$scale*obsInSample/df;
}
matErrors <- matrix(switch(object$distribution,
"dnorm"=rnorm(h*nsim, 0, scaleValue),
"dlaplace"=rlaplace(h*nsim, 0, scaleValue),
"ds"=rs(h*nsim, 0, scaleValue),
"dgnorm"=rgnorm(h*nsim, 0, scaleValue, object$other$shape),
"dlogis"=rlogis(h*nsim, 0, scaleValue),
"dt"=rt(h*nsim, obsInSample-nparam(object)),
"dalaplace"=ralaplace(h*nsim, 0, scaleValue, object$other$alpha),
"dlnorm"=rlnorm(h*nsim, -scaleValue^2/2, scaleValue)-1,
"dinvgauss"=rinvgauss(h*nsim, 1, dispersion=scaleValue)-1,
"dgamma"=rgamma(h*nsim, shape=scaleValue^{-1}, scale=scaleValue)-1,
"dllaplace"=exp(rlaplace(h*nsim, 0, scaleValue))-1,
"dls"=exp(rs(h*nsim, 0, scaleValue))-1,
"dlgnorm"=exp(rgnorm(h*nsim, 0, scaleValue, object$other$shape))-1
),
h,nsim);
# Normalise errors in order not to get ridiculous things on small nsim
if(nsim<=500){
if(Etype=="A"){
matErrors[] <- matErrors - array(apply(matErrors,1,mean),c(h,nsim));
}
else{
matErrors[] <- (1+matErrors) / array(apply(1+matErrors,1,mean),c(h,nsim))-1;
}
}
# This stuff is needed in order to produce adequate values for weird models
EtypeModified <- Etype;
if(Etype=="A" && any(object$distribution==c("dlnorm","dinvgauss","dgamma","dls","dllaplace"))){
EtypeModified[] <- "M";
}
# States, Errors, Ot, Transition, Measurement, Persistence
ySimulated <- adamSimulatorWrap(arrVt, matErrors,
matrix(rbinom(h*nsim, 1, pForecast), h, nsim),
array(matF,c(dim(matF),nsim)), matWt,
matrix(vecG, componentsNumberETS+componentsNumberARIMA+xregNumber+constantRequired, nsim),
EtypeModified, Ttype, Stype,
lagsModelAll, indexLookupTable, profilesRecentTable,
componentsNumberETSSeasonal, componentsNumberETS,
componentsNumberARIMA, xregNumber, constantRequired)$matrixYt;
#### Note that the cumulative doesn't work with oes at the moment!
if(cumulative){
yForecast[] <- mean(colSums(ySimulated,na.rm=TRUE));
yLower[] <- quantile(colSums(ySimulated,na.rm=TRUE),levelLow,type=7);
yUpper[] <- quantile(colSums(ySimulated,na.rm=TRUE),levelUp,type=7);
}
else{
for(i in 1:h){
if(Ttype=="M" || (Stype=="M" & h>lagsModelMin)){
# Trim 1% of values just to resolve some issues with outliers
yForecast[i] <- mean(ySimulated[i,],na.rm=TRUE,trim=0.01);
}
yLower[i,] <- quantile(ySimulated[i,],levelLow[i,],na.rm=TRUE,type=7);
yUpper[i,] <- quantile(ySimulated[i,],levelUp[i,],na.rm=TRUE,type=7);
}
}
# This step is needed in order to make intervals similar between the different methods
if(Etype=="A"){
yLower[] <- yLower - yForecast;
yUpper[] <- yUpper - yForecast;
}
else{
yLower[] <- yLower / yForecast;
yUpper[] <- yUpper / yForecast;
# Substitute NaNs with zeroes - it means that both values were originally zeroes
yLower[as.vector(is.nan(yLower))] <- 0;
yUpper[as.vector(is.nan(yUpper))] <- 0;
}
}
else{
#### Approximate and confidence interval ####
# Produce covariance matrix and use it
if(any(interval=="approximate")){
# The variance of the model
s2 <- sigma(object)^2;
# If scale model is included, produce forecasts
if(is.scale(object$scale)){
# Number of degrees of freedom to de-bias the variance
df <- (nobs(object, all=FALSE)-nparam(object));
# If the sample is too small, then use biased estimator
if(df<=0){
df[] <- nobs(object, all=FALSE);
}
s2Forecast <- forecast(object$scale,h=h,newdata=newdata,interval="none")$mean;
# Transform scales into the variances
# dnorm, dlnorm, dgamma and dinvgauss return scales that are equal to variances
s2Forecast[] <- switch(object$distribution,
"dlaplace"=2*s2Forecast^2,
"ds"=120*s2Forecast^4,
"dgnorm"=s2Forecast^2*gamma(3/object$other$shape)/gamma(1/object$other$shape),
"dalaplace"=s2Forecast^2/(object$other$alpha^2*(1-object$other$alpha)^2/
(object$other$alpha^2+(1-object$other$alpha)^2)),
s2Forecast)*obsInSample/df;
}
# IG and Lnorm can use approximations from the multiplications
if(etsModel && any(object$distribution==c("dinvgauss","dgamma","dlnorm","dllaplace","dls","dlgnorm")) && Etype=="M"){
vcovMulti <- adamVarAnal(lagsModelAll, h, matWt[1,,drop=FALSE], matF, vecG, s2);
if(is.scale(object$scale)){
# Fix the matrix with the time varying variance
vcovMulti[] <- vcovMulti / s2 * (sqrt(s2Forecast) %*% t(sqrt(s2Forecast)));
}
if(any(object$distribution==c("dlnorm","dls","dllaplace","dlgnorm"))){
vcovMulti[] <- log(1+vcovMulti);
}
# We don't do correct cumulatives in this case...
if(cumulative){
vcovMulti <- sum(vcovMulti);
}
}
else{
vcovMulti <- covarAnal(lagsModelAll, h, matWt[1,,drop=FALSE], matF, vecG, s2);
if(is.scale(object$scale)){
# Fix the matrix with the time varying variance
vcovMulti[] <- vcovMulti / s2 * (sqrt(s2Forecast) %*% t(sqrt(s2Forecast)));
}
# Do either the variance of sum, or a diagonal
if(cumulative){
vcovMulti <- sum(vcovMulti);
}
else{
vcovMulti <- diag(vcovMulti);
}
}
}
#### Semiparametric, nonparametric and empirical interval ####
# Extract multistep errors and calculate the covariance matrix
else if(any(interval==c("semiparametric","nonparametric","empirical"))){
if(h>1){
adamErrors <- as.matrix(rmultistep(object, h=h));
if(any(object$distribution==c("dinvgauss","dgamma","dlnorm","dls","dllaplace","dlgnorm")) && (Etype=="A")){
yFittedMatrix <- adamErrors;
for(i in 1:h){
yFittedMatrix[,i] <- fitted(object)[1:(obsInSample-h)+i];
}
adamErrors[] <- adamErrors/yFittedMatrix;
}
if(interval=="semiparametric"){
# Do either the variance of sum, or a diagonal
if(cumulative){
vcovMulti <- sum(t(adamErrors) %*% adamErrors / (obsInSample-h));
}
else{
vcovMulti <- diag(t(adamErrors) %*% adamErrors / (obsInSample-h));
}
}
# For nonparametric and cumulative...
else{
if(cumulative){
adamErrors <- matrix(apply(adamErrors, 2, sum),obsInSample-h,1);
}
}
}
else{
# If scale model is included, produce forecasts
if(is.scale(object$scale)){
# Number of degrees of freedom to de-bias the variance
df <- (nobs(object, all=FALSE)-nparam(object));
# If the sample is too small, then use biased estimator
if(df<=0){
df[] <- nobs(object, all=FALSE);
}
vcovMulti <- forecast(object$scale,h=h,newdata=newdata,interval="none")$mean;
# Transform scales into the variances
# dnorm, dlnorm, dgamma and dinvgauss return scales that are equal to variances
vcovMulti[] <- switch(object$distribution,
"dlaplace"=2*vcovMulti^2,
"ds"=120*vcovMulti^4,
"dgnorm"=vcovMulti^2*gamma(3/object$other$shape)/gamma(1/object$other$shape),
"dalaplace"=vcovMulti^2/(object$other$alpha^2*(1-object$other$alpha)^2/
(object$other$alpha^2+(1-object$other$alpha)^2)),
vcovMulti)*obsInSample/df;
}
else{
vcovMulti <- sigma(object)^2;
}
adamErrors <- as.matrix(residuals(object));
}
}
# Calculate interval for approximate and semiparametric
if(any(interval==c("approximate","semiparametric"))){
if(object$distribution=="dnorm"){
if(Etype=="A"){
yLower[] <- qnorm(levelLow, 0, sqrt(vcovMulti));
yUpper[] <- qnorm(levelUp, 0, sqrt(vcovMulti));
}
else{
yLower[] <- qnorm(levelLow, 1, sqrt(vcovMulti));
yUpper[] <- qnorm(levelUp, 1, sqrt(vcovMulti));
}
}
else if(object$distribution=="dlaplace"){
if(Etype=="A"){
yLower[] <- qlaplace(levelLow, 0, sqrt(vcovMulti/2));
yUpper[] <- qlaplace(levelUp, 0, sqrt(vcovMulti/2));
}
else{
yLower[] <- qlaplace(levelLow, 1, sqrt(vcovMulti/2));
yUpper[] <- qlaplace(levelUp, 1, sqrt(vcovMulti/2));
}
}
else if(object$distribution=="ds"){
if(Etype=="A"){
yLower[] <- qs(levelLow, 0, (vcovMulti/120)^0.25);
yUpper[] <- qs(levelUp, 0, (vcovMulti/120)^0.25);
}
else{
yLower[] <- qs(levelLow, 1, (vcovMulti/120)^0.25);
yUpper[] <- qs(levelUp, 1, (vcovMulti/120)^0.25);
}
}
else if(object$distribution=="dgnorm"){
scale <- sqrt(vcovMulti*(gamma(1/object$other$shape)/gamma(3/object$other$shape)));
if(Etype=="A"){
yLower[] <- suppressWarnings(qgnorm(levelLow, 0, scale, object$other$shape));
yUpper[] <- suppressWarnings(qgnorm(levelUp, 0, scale, object$other$shape));
}
else{
yLower[] <- suppressWarnings(qgnorm(levelLow, 1, scale, object$other$shape));
yUpper[] <- suppressWarnings(qgnorm(levelUp, 1, scale, object$other$shape));
}
}
else if(object$distribution=="dlogis"){
if(Etype=="A"){
yLower[] <- qlogis(levelLow, 0, sqrt(vcovMulti*3)/pi);
yUpper[] <- qlogis(levelUp, 0, sqrt(vcovMulti*3)/pi);
}
else{
yLower[] <- qlogis(levelLow, 1, sqrt(vcovMulti*3)/pi);
yUpper[] <- qlogis(levelUp, 1, sqrt(vcovMulti*3)/pi);
}
}
else if(object$distribution=="dt"){
df <- nobs(object) - nparam(object);
if(Etype=="A"){
yLower[] <- sqrt(vcovMulti)*qt(levelLow, df);
yUpper[] <- sqrt(vcovMulti)*qt(levelUp, df);
}
else{
yLower[] <- (1 + sqrt(vcovMulti)*qt(levelLow, df));
yUpper[] <- (1 + sqrt(vcovMulti)*qt(levelUp, df));
}
}
else if(object$distribution=="dalaplace"){
alpha <- object$other$alpha;
if(Etype=="A"){
yLower[] <- qalaplace(levelLow, 0,
sqrt(vcovMulti*alpha^2*(1-alpha)^2/(alpha^2+(1-alpha)^2)), alpha);
yUpper[] <- qalaplace(levelUp, 0,
sqrt(vcovMulti*alpha^2*(1-alpha)^2/(alpha^2+(1-alpha)^2)), alpha);
}
else{
yLower[] <- qalaplace(levelLow, 1,
sqrt(vcovMulti*alpha^2*(1-alpha)^2/(alpha^2+(1-alpha)^2)), alpha);
yUpper[] <- qalaplace(levelUp, 1,
sqrt(vcovMulti*alpha^2*(1-alpha)^2/(alpha^2+(1-alpha)^2)), alpha);
}
}
else if(object$distribution=="dlnorm"){
yLower[] <- qlnorm(levelLow, sqrt(abs(1-vcovMulti))-1, sqrt(vcovMulti));
yUpper[] <- qlnorm(levelUp, sqrt(abs(1-vcovMulti))-1, sqrt(vcovMulti));
if(Etype=="A"){
yLower[] <- (yLower-1)*yForecast;
yUpper[] <-(yUpper-1)*yForecast;
}
}
else if(object$distribution=="dllaplace"){
yLower[] <- exp(qlaplace(levelLow, 0, sqrt(vcovMulti/2)));
yUpper[] <- exp(qlaplace(levelUp, 0, sqrt(vcovMulti/2)));
if(Etype=="A"){
yLower[] <- (yLower-1)*yForecast;
yUpper[] <-(yUpper-1)*yForecast;
}
}
else if(object$distribution=="dls"){
yLower[] <- exp(qs(levelLow, 0, (vcovMulti/120)^0.25));
yUpper[] <- exp(qs(levelUp, 0, (vcovMulti/120)^0.25));
if(Etype=="A"){
yLower[] <- (yLower-1)*yForecast;
yUpper[] <-(yUpper-1)*yForecast;
}
}
else if(object$distribution=="dlgnorm"){
scale <- sqrt(vcovMulti*(gamma(1/object$other$shape)/gamma(3/object$other$shape)));
yLower[] <- suppressWarnings(exp(qgnorm(levelLow, 0, scale, object$other$shape)));
yUpper[] <- suppressWarnings(exp(qgnorm(levelUp, 0, scale, object$other$shape)));
if(Etype=="A"){
yLower[] <- (yLower-1)*yForecast;
yUpper[] <-(yUpper-1)*yForecast;
}
}
else if(object$distribution=="dinvgauss"){
yLower[] <- qinvgauss(levelLow, 1, dispersion=vcovMulti);
yUpper[] <- qinvgauss(levelUp, 1, dispersion=vcovMulti);
if(Etype=="A"){
yLower[] <- (yLower-1)*yForecast;
yUpper[] <-(yUpper-1)*yForecast;
}
}
else if(object$distribution=="dgamma"){
yLower[] <- qgamma(levelLow, shape=1/vcovMulti, scale=vcovMulti);
yUpper[] <- qgamma(levelUp, shape=1/vcovMulti, scale=vcovMulti);
if(Etype=="A"){
yLower[] <- (yLower-1)*yForecast;
yUpper[] <-(yUpper-1)*yForecast;
}
}
}
# Empirical, based on specific quantiles
else if(interval=="empirical"){
for(i in 1:h){
yLower[i,] <- quantile(adamErrors[,i],levelLow[i,],na.rm=TRUE,type=7);
yUpper[i,] <- quantile(adamErrors[,i],levelUp[i,],na.rm=TRUE,type=7);
}
if(Etype=="M"){
yLower[] <- 1+yLower;
yUpper[] <- 1+yUpper;
}
else if(Etype=="A" & any(object$distribution==c("dinvgauss","dgamma","dlnorm","dllaplace","dls","dlgnorm"))){
yLower[] <- yLower*yForecast;
yUpper[] <- yUpper*yForecast;
}
}
# Use Taylor & Bunn approach for the nonparametric ones
#### Nonparametric intervals, regression ####
else if(interval=="nonparametric"){
if(h>1){
# This is needed in order to see if quant regression can be used
if(all(levelLow==levelLow[1,])){
levelLow <- levelLow[1,,drop=FALSE];
}
if(all(levelUp==levelUp[1,])){
levelUp <- levelUp[1,,drop=FALSE];
}
# Do quantile regression for h>1 and scalars for the level (no change across h)
# transpose is needed in order to compare correctly
if(all(t(levelNew)==levelNew[1,])){
# Quantile regression function
intervalQuantile <- function(A, alpha){
ee[] <- adamErrors - (A[1]*xe^A[2]);
return((1-alpha)*sum(abs(ee[ee<0]))+alpha*sum(abs(ee[ee>=0])));
}
ee <- adamErrors;
xe <- matrix(c(1:h),nrow=nrow(ee),ncol=ncol(ee),byrow=TRUE);
for(i in 1:nLevels){
# lower quantiles
A <- nlminb(rep(1,2),intervalQuantile,alpha=levelLow[1,i])$par;
yLower[,i] <- A[1]*c(1:h)^A[2];
# upper quantiles
A[] <- nlminb(rep(1,2),intervalQuantile,alpha=levelUp[1,i])$par;
yUpper[,i] <- A[1]*c(1:h)^A[2];
}
}
# Otherwise just return quantiles of errors
else{
if(cumulative){
yLower[] <- quantile(adamErrors,levelLow,type=7);
yUpper[] <- quantile(adamErrors,levelUp,type=7);
}
else{
for(i in 1:h){
yLower[i] <- quantile(adamErrors[,i],levelLow[i],na.rm=TRUE,type=7);
yUpper[i] <- quantile(adamErrors[,i],levelUp[i],na.rm=TRUE,type=7);
}
}
}
}
else{
yLower[] <- quantile(adamErrors,levelLow,type=7);
yUpper[] <- quantile(adamErrors,levelUp,type=7);
}
if(Etype=="M"){
yLower[] <- 1+yLower;
yUpper[] <- 1+yUpper;
}
else if(Etype=="A" & any(object$distribution==c("dinvgauss","dgamma","dlnorm","dllaplace","dls","dlgnorm"))){
yLower[] <- yLower*yForecast;
yUpper[] <- yUpper*yForecast;
}
}
else{
yUpper[] <- yLower[] <- NA;
}
}
# Fix of prediction intervals depending on what has happened
if(interval!="none"){
# Make sensible values out of those weird quantiles
if(!cumulative){
if(any(levelLow==0)){
# zoo does not like, when you work with matrices of indices... silly thing
yBoundBuffer <- levelLow;
yBoundBuffer[] <- yLower
if(Etype=="A"){
yBoundBuffer[levelLow==0] <- -Inf;
yLower[] <- yBoundBuffer;
}
else{
yBoundBuffer[levelLow==0] <- 0;
yLower[] <- yBoundBuffer;
}
}
if(any(levelUp==1)){
# zoo does not like, when you work with matrices of indices... silly thing
yBoundBuffer <- levelUp;
yBoundBuffer[] <- yUpper
yBoundBuffer[levelUp==1] <- Inf;
yUpper[] <- yBoundBuffer;
}
}
else{
if(Etype=="A" && any(levelLow==0)){
yLower[] <- -Inf;
}
else if(Etype=="M" && any(levelLow==0)){
yLower[] <- 0;
}
if(any(levelUp==1)){
yUpper[] <- Inf;
}
}
# Substitute NAs and NaNs with zeroes
if(any(is.nan(yLower)) || any(is.na(yLower))){
yLower[is.nan(yLower)] <- switch(Etype,"A"=0,"M"=1);
yLower[is.na(yLower)] <- switch(Etype,"A"=0,"M"=1);
}
if(any(is.nan(yUpper)) || any(is.na(yUpper))){
yUpper[is.nan(yUpper)] <- switch(Etype,"A"=0,"M"=1);
yUpper[is.na(yUpper)] <- switch(Etype,"A"=0,"M"=1);
}
# Do intervals around the forecasts...
if(Etype=="A"){
yLower[] <- yForecast + yLower;
yUpper[] <- yForecast + yUpper;
}
else{
yLower[] <- yForecast*yLower;
yUpper[] <- yForecast*yUpper;
}
# Check what we have from the occurrence model
if(occurrenceModel){
# If there are NAs, then there's no variability and no intervals.
if(any(is.na(yUpper))){
yUpper[is.na(yUpper)] <- (yForecast/pForecast)[is.na(yUpper)];
}
if(any(is.na(yLower))){
yLower[is.na(yLower)] <- 0;
}
}
colnames(yLower) <- switch(side,
"both"=paste0("Lower bound (",(1-level)/2*100,"%)"),
"lower"=paste0("Lower bound (",(1-level)*100,"%)"),
"upper"=rep("Lower 0%",nLevels));
colnames(yUpper) <- switch(side,
"both"=paste0("Upper bound (",(1+level)/2*100,"%)"),
"lower"=rep("Upper 100%",nLevels),
"upper"=paste0("Upper bound (",level*100,"%)"));
}
# If this was a model in logarithms (e.g. ARIMA for sm), then take exponent
if(any(unlist(gregexpr("in logs",object$model))!=-1)){
yForecast[] <- exp(yForecast);
yLower[] <- exp(yLower);
yUpper[] <- exp(yUpper);
}
if(!scenarios){
ySimulated <- scenarios;
}
else{
if(interval=="simulated"){
colnames(ySimulated) <- paste0("nsim",1:nsim);
rownames(ySimulated) <- paste0("h",1:h);
}
else{
warning("Scenarios are only available when interval=\"simulated\".",
call.=FALSE);
ySimulated <- FALSE;
}
}
return(structure(list(mean=yForecast, lower=yLower, upper=yUpper, model=object,
level=level, interval=interval, side=side, cumulative=cumulative, h=h,
scenarios=ySimulated),
class=c("adam.forecast","smooth.forecast","forecast")));
}
#' @export
forecast.adamCombined <- function(object, h=10, newdata=NULL,
interval=c("none", "prediction", "confidence", "simulated",
"approximate", "semiparametric", "nonparametric",
"empirical","complete"),
level=0.95, side=c("both","upper","lower"), cumulative=FALSE, nsim=NULL, ...){
interval <- match.arg(interval[1],c("none", "simulated", "approximate", "semiparametric",
"nonparametric", "confidence", "parametric","prediction",
"empirical","complete"));
side <- match.arg(side);
yClasses <- class(actuals(object));
obsInSample <- nobs(object);
if(any(yClasses=="ts")){
# ts structure
yForecastStart <- time(actuals(object))[obsInSample]+deltat(actuals(object));
yFrequency <- frequency(actuals(object));
}
else{
# zoo thingy
yIndex <- time(actuals(object));
yForecastIndex <- yIndex[obsInSample]+diff(tail(yIndex,2))*c(1:h);
}
# How many levels did user asked to produce
nLevels <- length(level);
# Cumulative forecasts have only one observation
if(cumulative){
# hFinal is the number of elements we will have in the final forecast
hFinal <- 1;
}
else{
hFinal <- h;
}
# Create necessary matrices for the forecasts
if(any(yClasses=="ts")){
yForecast <- ts(vector("numeric", hFinal), start=yForecastStart, frequency=yFrequency);
yUpper <- yLower <- ts(matrix(0,hFinal,nLevels), start=yForecastStart, frequency=yFrequency);
}
else{
yForecast <- zoo(vector("numeric", hFinal), order.by=yForecastIndex);
yUpper <- yLower <- zoo(matrix(0,hFinal,nLevels), order.by=yForecastIndex);
}
# Remove ICw, which are lower than 0.001
object$ICw[object$ICw<1e-2] <- 0;
object$ICw[] <- object$ICw / sum(object$ICw);
# The list contains 10 elements
adamForecasts <- vector("list", 10);
names(adamForecasts)[c(1:3)] <- c("mean","lower","upper");
for(i in 1:length(object$models)){
if(object$ICw[i]==0){
next;
}
adamForecasts[] <- forecast.adam(object$models[[i]], h=h, newdata=newdata,
interval=interval,
level=level, side=side, cumulative=cumulative, nsim=nsim, ...);
yForecast[] <- yForecast + adamForecasts$mean * object$ICw[i];
yUpper[] <- yUpper + adamForecasts$upper * object$ICw[i];
yLower[] <- yLower + adamForecasts$lower * object$ICw[i];
}
# Fix the names of the columns
if(interval!="none"){
colnames(yLower) <- colnames(adamForecasts$lower);
colnames(yUpper) <- colnames(adamForecasts$upper);
}
# Fix the content of upper / lower bounds
if(side=="upper"){
yLower[] <- -Inf;
}
else if(side=="lower"){
yUpper[] <- Inf;
}
# Get rid of specific models to save RAM
object$models <- NULL;
return(structure(list(mean=yForecast, lower=yLower, upper=yUpper, model=object,
level=level, interval=interval, side=side, cumulative=cumulative, h=h),
class=c("adam.forecast","smooth.forecast","forecast")));
}
#' @export
print.adam.forecast <- function(x, ...){
if(x$interval!="none"){
returnedValue <- switch(x$side,
"both"=cbind(x$mean,x$lower,x$upper),
"lower"=cbind(x$mean,x$lower),
"upper"=cbind(x$mean,x$upper));
colnames(returnedValue) <- switch(x$side,
"both"=c("Point forecast",colnames(x$lower),colnames(x$upper)),
"lower"=c("Point forecast",colnames(x$lower)),
"upper"=c("Point forecast",colnames(x$upper)))
}
else{
returnedValue <- x$mean;
}
print(returnedValue);
}
#' @export
plot.adam.forecast <- function(x, ...){
yClasses <- class(actuals(x));
digits <- 2;
ellipsis <- list(...);
if(is.null(ellipsis$legend)){
ellipsis$legend <- FALSE;
ellipsis$parReset <- FALSE;
}
if(is.null(ellipsis$main)){
distrib <- switch(x$model$distribution,
"dnorm" = "Normal",
"dlogis" = "Logistic",
"dlaplace" = "Laplace",
"ds" = "S",
"dgnorm" = paste0("Generalised Normal with shape=",round(x$model$other$shape,digits)),
"dalaplace" = paste0("Asymmetric Laplace with alpha=",round(x$model$other$alpha,digits)),
"dt" = paste0("Student t with df=",round(x$model$other$nu, digits)),
"dlnorm" = "Log-Normal",
"dllaplace" = "Log-Laplace",
"dls" = "Log-S",
"dgnorm" = paste0("Log-Generalised Normal with shape=",round(x$model$other$shape,digits)),
# "dbcnorm" = paste0("Box-Cox Normal with lambda=",round(x$other$lambda,2)),
"dinvgauss" = "Inverse Gaussian",
"dgamma" = "Gamma",
"default"
);
ellipsis$main <- paste0("Forecast from ",x$model$model," with ",distrib," distribution");
}
if(!is.null(x$model$holdout)){
responseName <- all.vars(formula(x$model))[1];
yHoldout <- x$model$holdout[,responseName];
if(any(yClasses=="ts")){
ellipsis$actuals <- ts(c(actuals(x$model),yHoldout),
start=start(actuals(x$model)),
frequency=frequency(actuals(x$model)));
}
else{
ellipsis$actuals <- zoo(c(as.vector(actuals(x$model)),as.vector(yHoldout)),
order.by=c(time(actuals(x$model)),time(yHoldout)));
}
}
else{
ellipsis$actuals <- actuals(x$model);
}
ellipsis$forecast <- x$mean;
ellipsis$lower <- x$lower;
ellipsis$upper <- x$upper;
ellipsis$fitted <- fitted(x);
ellipsis$level <- x$level;
if(x$cumulative){
if(any(yClasses=="ts")){
ellipsis$forecast <- ts(ellipsis$forecast / x$h,
start=start(ellipsis$forecast),
frequency=frequency(ellipsis$forecast));
ellipsis$lower <- ts(ellipsis$lower / x$h,
start=start(ellipsis$lower),
frequency=frequency(ellipsis$lower));
ellipsis$upper <- ts(ellipsis$upper / x$h,
start=start(ellipsis$upper),
frequency=frequency(ellipsis$upper));
ellipsis$main <- paste0("Mean ", ellipsis$main);
}
else{
ellipsis$forecast <- zoo(ellipsis$forecast / x$h,
order.by=time(ellipsis$forecast)+c(1:x$h)-1);
ellipsis$lower <- zoo(ellipsis$lower / x$h,
order.by=time(ellipsis$lower)+c(1:x$h)-1);
ellipsis$upper <- zoo(ellipsis$upper / x$h,
order.by=time(ellipsis$upper)+c(1:x$h)-1);
ellipsis$main <- paste0("Mean ", ellipsis$main);
ellipsis$actuals <- zoo(c(as.vector(actuals(x$model)),as.vector(yHoldout)),
order.by=c(time(actuals(x$model)),time(yHoldout)));
}
}
do.call(graphmaker, ellipsis);
}
#### Refitter and reforecaster ####
#' Reapply the model with randomly generated initial parameters and produce forecasts
#'
#' \code{reapply} function generates the parameters based on the values in the provided
#' object and then reapplies the same model with those parameters to the data, getting
#' the fitted paths and updated states. \code{reforecast} function uses those values
#' in order to produce forecasts for the \code{h} steps ahead.
#'
#' The main motivation of the function is to take the randomness due to the in-sample
#' estimation of parameters into account when fitting the model and to propagate
#' this randomness to the forecasts. The methods can be considered as a special case
#' of recursive bootstrap.
#'
#' @template ssAuthor
#' @template ssKeywords
#'
#' @param object Model estimated using one of the functions of smooth package.
#' @param nsim Number of paths to generate (number of simulations to do).
#' @param h Forecast horizon.
#' @param newdata The new data needed in order to produce forecasts.
#' @param bootstrap The logical, which determines, whether to use bootstrap for the
#' covariance matrix of parameters or not.
#' @param heuristics The value for proportion to use for heuristic estimation of the
#' standard deviation of parameters. If \code{NULL}, it is not used.
#' @param occurrence The vector containing the future occurrence variable
#' (values in [0,1]), if it is known.
#' @param interval What type of mechanism to use for interval construction. The options
#' include \code{interval="none"}, \code{interval="prediction"} (prediction intervals)
#' and \code{interval="confidence"} (intervals for the point forecast). The other options
#' are not supported and do not make much sense for the refitted model.
#' @param level Confidence level. Defines width of prediction interval.
#' @param side Defines, whether to provide \code{"both"} sides of prediction
#' interval or only \code{"upper"}, or \code{"lower"}.
#' @param cumulative If \code{TRUE}, then the cumulative forecast and prediction
#' interval are produced instead of the normal ones. This is useful for
#' inventory control systems.
#' @param ... Other parameters passed to \code{reapply()} and \code{mean()} functions in case of
#' \code{reforecast} (\code{trim} parameter in \code{mean()} is set to
#' 0.01 by default) and to \code{vcov} in case of \code{reapply}.
#' @return \code{reapply()} returns object of the class "reapply", which contains:
#' \itemize{
#' \item \code{timeElapsed} - Time elapsed for the code execution;
#' \item \code{y} - The actual values;
#' \item \code{states} - The array of states of the model;
#' \item \code{refitted} - The matrix with fitted values, where columns correspond
#' to different paths;
#' \item \code{fitted} - The vector of fitted values (conditional mean);
#' \item \code{model} - The name of the constructed model;
#' \item \code{transition} - The array of transition matrices;
#' \item \code{measurement} - The array of measurement matrices;
#' \item \code{persistence} - The matrix of persistence vectors (paths in columns);
#' \item \code{profile} - The array of profiles obtained by the end of each fit.
#' }
#'
#' \code{reforecast()} returns the object of the class \link[smooth]{forecast.smooth},
#' which contains in addition to the standard list the variable \code{paths} - all
#' simulated trajectories with h in rows, simulated future paths for each state in
#' columns and different states (obtained from \code{reapply()} function) in the
#' third dimension.
#'
#' @seealso \link[smooth]{forecast.smooth}
#' @examples
#'
#' x <- rnorm(100,0,1)
#'
#' # Just as example. orders and lags do not return anything for ces() and es(). But modelType() does.
#' ourModel <- adam(x, "ANN")
#' refittedModel <- reapply(ourModel, nsim=50)
#' plot(refittedModel)
#'
#' ourForecast <- reforecast(ourModel, nsim=50)
#'
#' @rdname reapply
#' @export reapply
reapply <- function(object, nsim=1000, bootstrap=FALSE, heuristics=NULL, ...) UseMethod("reapply")
#' @export
reapply.default <- function(object, nsim=1000, bootstrap=FALSE, heuristics=NULL, ...){
warning(paste0("The method is not implemented for the object of the class ",class(object)[1]),
call.=FALSE);
return(structure(list(states=object$states, fitted=fitted(object)),
class="reapply"));
}
#' @importFrom MASS mvrnorm
#' @export
reapply.adam <- function(object, nsim=1000, bootstrap=FALSE, heuristics=NULL, ...){
# Start measuring the time of calculations
startTime <- Sys.time();
parametersNames <- names(coef(object));
vcovAdam <- suppressWarnings(vcov(object, bootstrap=bootstrap, heuristics=heuristics, nsim=nsim, ...));
# Check if the matrix is positive definite
vcovEigen <- min(eigen(vcovAdam, only.values=TRUE)$values);
if(vcovEigen<0){
if(vcovEigen>-1){
warning(paste0("The covariance matrix of parameters is not positive semi-definite. ",
"I will try fixing this, but it might make sense re-estimating adam(), tuning the optimiser."),
call.=FALSE, immediate.=TRUE);
# Tune the thing a bit - one of simple ways to fix the issue
epsilon <- -vcovEigen+1e-10;
vcovAdam[] <- vcovAdam + epsilon*diag(nrow(vcovAdam));
}
else{
warning(paste0("The covariance matrix of parameters is not positive semi-definite. ",
"I cannot fix it, so I will use the diagonal only. ",
"It makes sense to re-estimate adam(), tuning the optimiser. ",
"For example, try reoptimising via 'object <- adam(y, ..., B=object$B)'."),
call.=FALSE, immediate.=TRUE);
vcovAdam[] <- diag(diag(vcovAdam));
}
}
# All the variables needed in the refitter
yInSample <- actuals(object);
yClasses <- class(yInSample);
parametersNumber <- length(parametersNames);
obsInSample <- nobs(object);
Etype <- errorType(object);
Ttype <- substr(modelType(object),2,2);
Stype <- substr(modelType(object),nchar(modelType(object)),nchar(modelType(object)));
etsModel <- any(unlist(gregexpr("ETS",object$model))!=-1);
arimaModel <- any(unlist(gregexpr("ARIMA",object$model))!=-1);
lags <- object$lags;
lagsSeasonal <- lags[lags!=1];
lagsModelAll <- object$lagsAll;
lagsModelMax <- max(lagsModelAll);
persistence <- as.matrix(object$persistence);
# If there is xreg, but no deltas, increase persistence by including zeroes
# This can be considered as a failsafe mechanism
if(ncol(object$data)>1 && !any(substr(names(object$persistence),1,5)=="delta")){
persistence <- rbind(persistence,matrix(rep(0,sum(object$nParam[,2])),ncol=1));
}
# See if constant is required
constantRequired <- !is.null(object$constant);
# Expand persistence to include zero for the constant
# if(constantRequired){
#
# }
if(!is.null(object$initial$seasonal)){
if(is.list(object$initial$seasonal)){
componentsNumberETSSeasonal <- length(object$initial$seasonal);
}
else{
componentsNumberETSSeasonal <- 1;
}
}
else{
componentsNumberETSSeasonal <- 0;
}
componentsNumberETS <- length(object$initial$level) + length(object$initial$trend) + componentsNumberETSSeasonal;
componentsNumberARIMA <- sum(substr(colnames(object$states),1,10)=="ARIMAState");
# Prepare variables for xreg
if(!is.null(object$initial$xreg)){
xregModel <- TRUE;
#### Create xreg vectors ####
xreg <- object$data;
formula <- formula(object)
responseName <- all.vars(formula)[1];
# Robustify the names of variables
colnames(xreg) <- make.names(colnames(xreg),unique=TRUE);
# The names of the original variables
xregNamesOriginal <- all.vars(formula)[-1];
# Levels for the factors
xregFactorsLevels <- lapply(xreg,levels);
xregFactorsLevels[[responseName]] <- NULL;
# Expand the variables. We cannot use alm, because it is based on obsInSample
xregData <- model.frame(formula,data=as.data.frame(xreg));
# Binary, flagging factors in the data
xregFactors <- (attr(terms(xregData),"dataClasses")=="factor")[-1];
# Get the names from the standard model.matrix
xregNames <- colnames(model.matrix(xregData,data=xregData));
interceptIsPresent <- FALSE;
if(any(xregNames=="(Intercept)")){
interceptIsPresent[] <- TRUE;
xregNames <- xregNames[xregNames!="(Intercept)"];
}
# Expanded stuff with all levels for factors
if(any(xregFactors)){
xregModelMatrix <- model.matrix(xregData,xregData,
contrasts.arg=lapply(xregData[attr(terms(xregData),"dataClasses")=="factor"],
contrasts, contrasts=FALSE));
xregNamesModified <- colnames(xregModelMatrix)[-1];
}
else{
xregModelMatrix <- model.matrix(xregData,data=xregData);
xregNamesModified <- xregNames;
}
xregData <- as.matrix(xregModelMatrix);
# Remove intercept
if(interceptIsPresent){
xregData <- xregData[,-1,drop=FALSE];
}
xregNumber <- ncol(xregData);
# The indices of the original parameters
xregParametersMissing <- setNames(vector("numeric",xregNumber),xregNamesModified);
# # The indices of the original parameters
xregParametersIncluded <- setNames(vector("numeric",xregNumber),xregNamesModified);
# The vector, marking the same values of smoothing parameters
if(interceptIsPresent){
xregParametersPersistence <- setNames(attr(xregModelMatrix,"assign")[-1],xregNamesModified);
}
else{
xregParametersPersistence <- setNames(attr(xregModelMatrix,"assign"),xregNamesModified);
}
# If there are factors not in the alm data, create additional initials
if(any(!(xregNamesModified %in% xregNames))){
xregAbsent <- !(xregNamesModified %in% xregNames);
# Go through new names and find, where they came from. Then get the missing parameters
for(i in which(xregAbsent)){
# Find the name of the original variable
# Use only the last value... hoping that the names like x and x1 are not used.
xregNameFound <- tail(names(sapply(xregNamesOriginal,grepl,xregNamesModified[i])),1);
# Get the indices of all k-1 levels
xregParametersIncluded[xregNames[xregNames %in% paste0(xregNameFound,
xregFactorsLevels[[xregNameFound]])]] <- i;
# Get the index of the absent one
xregParametersMissing[i] <- i;
}
# Write down the new parameters
xregNames <- xregNamesModified;
}
# The vector of parameters that should be estimated (numeric + original levels of factors)
xregParametersEstimated <- xregParametersIncluded
xregParametersEstimated[xregParametersEstimated!=0] <- 1;
xregParametersEstimated[xregParametersMissing==0 & xregParametersIncluded==0] <- 1;
}
else{
xregModel <- FALSE;
xregNumber <- 0;
xregParametersMissing <- 0;
xregParametersIncluded <- 0;
xregParametersEstimated <- 0;
xregParametersPersistence <- 0;
}
indexLookupTable <- adamProfileCreator(lagsModelAll, lagsModelMax, obsInSample)$lookup;
# Generate the data from the multivariate normal
randomParameters <- mvrnorm(nsim, coef(object), vcovAdam);
#### Rectify the random values for smoothing parameters ####
if(etsModel){
# Usual bounds
if(object$bounds=="usual"){
# Set the bounds for alpha
if(any(parametersNames=="alpha")){
randomParameters[randomParameters[,"alpha"]<0,"alpha"] <- 0;
randomParameters[randomParameters[,"alpha"]>1,"alpha"] <- 1;
}
# Set the bounds for beta
if(any(parametersNames=="beta")){
randomParameters[randomParameters[,"beta"]<0,"beta"] <- 0;
randomParameters[randomParameters[,"beta"]>randomParameters[,"alpha"],"beta"] <-
randomParameters[randomParameters[,"beta"]>randomParameters[,"alpha"],"alpha"];
}
# Set the bounds for gamma
if(any(substr(parametersNames,1,5)=="gamma")){
gammas <- which(substr(colnames(randomParameters),1,5)=="gamma");
for(i in 1:length(gammas)){
randomParameters[randomParameters[,gammas[i]]<0,gammas[i]] <- 0;
randomParameters[randomParameters[,gammas[i]]>randomParameters[,"alpha"],
gammas[i]] <- 1-
randomParameters[randomParameters[,gammas[i]]>randomParameters[,"alpha"],"alpha"];
}
}
# Set the bounds for phi
if(any(parametersNames=="phi")){
randomParameters[randomParameters[,"phi"]<0,"phi"] <- 0;
randomParameters[randomParameters[,"phi"]>1,"phi"] <- 1;
}
}
# Admissible bounds
else if(object$bounds=="admissible"){
# Check, if there is alpha
if(any(parametersNames=="alpha")){
alphaBounds <- eigenBounds(object, persistence,
variableNumber=which(names(object$persistence)=="alpha"));
randomParameters[randomParameters[,"alpha"]<alphaBounds[1],"alpha"] <- alphaBounds[1];
randomParameters[randomParameters[,"alpha"]>alphaBounds[2],"alpha"] <- alphaBounds[2];
}
# Check, if there is beta
if(any(parametersNames=="beta")){
betaBounds <- eigenBounds(object, persistence,
variableNumber=which(names(object$persistence)=="beta"));
randomParameters[randomParameters[,"beta"]<betaBounds[1],"beta"] <- betaBounds[1];
randomParameters[randomParameters[,"beta"]>betaBounds[2],"beta"] <- betaBounds[2];
}
# Check, if there are gammas
if(any(substr(parametersNames,1,5)=="gamma")){
gammas <- which(substr(parametersNames,1,5)=="gamma");
for(i in 1:length(gammas)){
gammaBounds <- eigenBounds(object, persistence,
variableNumber=which(substr(names(object$persistence),1,5)=="gamma")[i]);
randomParameters[randomParameters[,gammas[i]]<gammaBounds[1],gammas[i]] <- gammaBounds[1];
randomParameters[randomParameters[,gammas[i]]>gammaBounds[2],gammas[i]] <- gammaBounds[2];
}
}
# Check, if there are deltas (for xreg)
if(any(substr(parametersNames,1,5)=="delta")){
deltas <- which(substr(parametersNames,1,5)=="delta");
for(i in 1:length(deltas)){
deltaBounds <- eigenBounds(object, persistence,
variableNumber=which(substr(names(object$persistence),1,5)=="delta")[i]);
randomParameters[randomParameters[,deltas[i]]<deltaBounds[1],deltas[i]] <- deltaBounds[1];
randomParameters[randomParameters[,deltas[i]]>deltaBounds[2],deltas[i]] <- deltaBounds[2];
}
}
}
# States
# Set the bounds for trend
if(Ttype=="M" && any(parametersNames=="trend")){
randomParameters[randomParameters[,"trend"]<0,"trend"] <- 1e-6;
}
# Seasonality
if(Stype=="M" && any(substr(parametersNames,1,8)=="seasonal")){
seasonals <- which(substr(parametersNames,1,8)=="seasonal");
for(i in seasonals){
randomParameters[randomParameters[,i]<0,i] <- 1e-6;
}
}
}
# Correct the bounds for the ARIMA model
if(arimaModel){
#### Deal with ARIMA parameters ####
ariPolynomial <- object$other$polynomial$ariPolynomial;
arPolynomial <- object$other$polynomial$arPolynomial;
maPolynomial <- object$other$polynomial$maPolynomial;
nonZeroARI <- object$other$ARIMAIndices$nonZeroARI;
nonZeroMA <- object$other$ARIMAIndices$nonZeroMA;
arPolynomialMatrix <- object$other$arPolynomialMatrix;
# Locate all thetas for ARIMA
thetas <- which(substr(parametersNames,1,5)=="theta");
# Locate phi for ARIMA (they are always phi1, phi2 etc)
phis <- which((substr(parametersNames,1,3)=="phi") & (nchar(parametersNames)>3));
# Do loop for thetas
if(length(thetas)>0){
# MA parameters
for(i in 1:length(thetas)){
psiBounds <- eigenBounds(object, persistence,
variableNumber=which(substr(names(object$persistence),1,3)=="psi")[nonZeroMA[i,2]]);
# If there are ARI elements in persistence, subtract (-(-x)) them to get proper bounds
if(any(nonZeroARI[,2]==i)){
ariIndex <- which(nonZeroARI[,2]==i);
randomParameters[randomParameters[,thetas[i]]-ariPolynomial[nonZeroARI[ariIndex,1]]<psiBounds[1],thetas[i]] <-
psiBounds[1]+ariPolynomial[nonZeroARI[ariIndex,1]];
randomParameters[randomParameters[,thetas[i]]-ariPolynomial[nonZeroARI[ariIndex,1]]>psiBounds[2],thetas[i]] <-
psiBounds[2]+ariPolynomial[nonZeroARI[ariIndex,1]];
}
else{
randomParameters[randomParameters[,thetas[i]]<psiBounds[1],thetas[i]] <- psiBounds[1];
randomParameters[randomParameters[,thetas[i]]>psiBounds[2],thetas[i]] <- psiBounds[2];
}
}
}
# Locate phi for ARIMA (they are always phi1, phi2 etc)
if(length(phis)>0){
# AR parameters
for(i in 1:length(phis)){
# Get bounds for AR based on stationarity condition
phiBounds <- arPolinomialsBounds(arPolynomialMatrix, arPolynomial,
which(arPolynomial==arPolynomial[arPolynomial!=0][-1][i]));
randomParameters[randomParameters[,phis[i]]<phiBounds[1],phis[i]] <- phiBounds[1];
randomParameters[randomParameters[,phis[i]]>phiBounds[2],phis[i]] <- phiBounds[2];
}
}
}
# Set the bounds for deltas
if(any(substr(parametersNames,1,5)=="delta")){
deltas <- which(substr(colnames(randomParameters),1,5)=="delta");
randomParameters[,deltas][randomParameters[,deltas]<0] <- 0;
randomParameters[,deltas][randomParameters[,deltas]>1] <- 1;
}
#### Prepare the necessary matrices ####
# States are defined similar to how it is done in adam.
# Inserting the existing one is needed in order to deal with the case, when one of the initials was provided
arrVt <- array(t(object$states),c(ncol(object$states),nrow(object$states),nsim),
dimnames=list(colnames(object$states),NULL,paste0("nsim",c(1:nsim))));
# Set the proper time stamps for the fitted
if(any(yClasses=="zoo")){
fittedMatrix <- zoo(array(NA,c(obsInSample,nsim),
dimnames=list(NULL,paste0("nsim",c(1:nsim)))),
order.by=time(yInSample));
}
else{
fittedMatrix <- ts(array(NA,c(obsInSample,nsim),
dimnames=list(NULL,paste0("nsim",c(1:nsim)))),
start=start(yInSample), frequency=frequency(yInSample));
}
# Transition and measurement
arrF <- array(object$transition,c(dim(object$transition),nsim));
arrWt <- array(object$measurement,c(dim(object$measurement),nsim));
# Persistence matrix
# The first one is a failsafe mechanism for xreg
matG <- array(object$persistence, c(length(object$persistence), nsim),
dimnames=list(names(object$persistence), paste0("nsim",c(1:nsim))));
#### Fill in the values in matrices ####
# k is the index for randomParameters columns
k <- 0;
# Fill in the persistence
if(etsModel){
if(any(parametersNames=="alpha")){
matG["alpha",] <- randomParameters[,"alpha"];
k <- k+1;
}
if(any(parametersNames=="beta")){
matG["beta",] <- randomParameters[,"beta"];
k <- k+1;
}
if(any(substr(parametersNames,1,5)=="gamma")){
gammas <- which(substr(colnames(randomParameters),1,5)=="gamma");
matG[colnames(randomParameters)[gammas],] <- t(randomParameters[,gammas,drop=FALSE]);
k <- k+length(gammas);
}
# If we have phi, update the transition and measurement matrices
if(any(parametersNames=="phi")){
arrF[1,2,] <- arrF[2,2,] <- randomParameters[,"phi"];
arrWt[,2,] <- matrix(randomParameters[,"phi"],nrow(object$measurement),nsim,byrow=TRUE);
k <- k+1;
}
}
if(xregModel && any(substr(parametersNames,1,5)=="delta")){
deltas <- which(substr(colnames(randomParameters),1,5)=="delta");
matG[colnames(randomParameters)[deltas],] <- t(randomParameters[,deltas,drop=FALSE]);
k <- k+length(deltas);
}
# Fill in the persistence and transition for ARIMA
if(arimaModel){
if(is.list(object$orders)){
arOrders <- object$orders$ar;
iOrders <- object$orders$i;
maOrders <- object$orders$ma;
}
else if(is.vector(object$orders)){
arOrders <- object$orders[1];
iOrders <- object$orders[2];
maOrders <- object$orders[3];
}
# See if AR is needed
arRequired <- FALSE;
if(sum(arOrders)>0){
arRequired[] <- TRUE;
}
# See if I is needed
iRequired <- FALSE;
if(sum(iOrders)>0){
iRequired[] <- TRUE;
}
# See if I is needed
maRequired <- FALSE;
if(sum(maOrders)>0){
maRequired[] <- TRUE;
}
# Define maxOrder and make all the values look similar (for the polynomials)
maxOrder <- max(length(arOrders),length(iOrders),length(maOrders),length(lags));
if(length(arOrders)!=maxOrder){
arOrders <- c(arOrders,rep(0,maxOrder-length(arOrders)));
}
if(length(iOrders)!=maxOrder){
iOrders <- c(iOrders,rep(0,maxOrder-length(iOrders)));
}
if(length(maOrders)!=maxOrder){
maOrders <- c(maOrders,rep(0,maxOrder-length(maOrders)));
}
if(length(lags)!=maxOrder){
lagsNew <- c(lags,rep(0,maxOrder-length(lags)));
arOrders <- arOrders[lagsNew!=0];
iOrders <- iOrders[lagsNew!=0];
maOrders <- maOrders[lagsNew!=0];
}
# The provided parameters
armaParameters <- object$other$armaParameters;
# Check if the AR / MA parameters were estimated
arEstimate <- any((substr(parametersNames,1,3)=="phi") & (nchar(parametersNames)>3))
maEstimate <- any(substr(parametersNames,1,5)=="theta");
# polyIndex is the index of the phi / theta parameters -1
if(any(c(arEstimate,maEstimate))){
polyIndex <- min(which((substr(parametersNames,1,3)=="phi") & (nchar(parametersNames)>3)),
which(substr(parametersNames,1,5)=="theta")) -1;
}
# If AR / MA are not estimated, then we don't care
else{
polyIndex <- -1;
}
for(i in 1:nsim){
# Call the function returning ARI and MA polynomials
# arimaPolynomials <- polynomialiser(randomParameters[i,polyIndex+1:sum(c(arOrders*arEstimate,maOrders*maEstimate))],
# arOrders, iOrders, maOrders, arRequired, maRequired, arEstimate, maEstimate,
# armaParameters, lags);
arimaPolynomials <- lapply(adamPolynomialiser(randomParameters[i,polyIndex+1:sum(c(arOrders*arEstimate,maOrders*maEstimate))],
arOrders, iOrders, maOrders,
arEstimate, maEstimate, armaParameters, lags), as.vector)
# Fill in the transition and persistence matrices
if(nrow(nonZeroARI)>0){
arrF[componentsNumberETS+nonZeroARI[,2],componentsNumberETS+1:componentsNumberARIMA,i] <-
-arimaPolynomials$ariPolynomial[nonZeroARI[,1]];
matG[componentsNumberETS+nonZeroARI[,2],i] <- -arimaPolynomials$ariPolynomial[nonZeroARI[,1]];
}
if(nrow(nonZeroMA)>0){
matG[componentsNumberETS+nonZeroMA[,2],i] <- matG[componentsNumberETS+nonZeroMA[,2],i] +
arimaPolynomials$maPolynomial[nonZeroMA[,1]];
}
}
k <- k+sum(c(arOrders*arEstimate,maOrders*maEstimate));
}
# j is the index for the components in the profile
j <- 0
# Fill in the profile values
profilesRecentArray <- array(t(object$states[1:lagsModelMax,]),c(dim(object$profile),nsim));
if(etsModel && object$initialType=="optimal"){
if(any(parametersNames=="level")){
j <- j+1;
profilesRecentArray[j,1,] <- randomParameters[,"level"];
k <- k+1;
}
if(any(parametersNames=="trend")){
j <- j+1;
profilesRecentArray[j,1,] <- randomParameters[,"trend"];
k <- k+1;
}
if(any(substr(parametersNames,1,8)=="seasonal")){
# If there is only one seasonality
if(any(substr(parametersNames,1,9)=="seasonal_")){
initialSeasonalIndices <- 1;
seasonalNames <- "seasonal"
}
# If there are several
else{
# This assumes that we cannot have more than 9 seasonalities.
initialSeasonalIndices <- as.numeric(unique(substr(parametersNames[substr(parametersNames,1,8)=="seasonal"],9,9)));
seasonalNames <- unique(substr(parametersNames[substr(parametersNames,1,8)=="seasonal"],1,9));
}
for(i in initialSeasonalIndices){
profilesRecentArray[j+i,1:(lagsSeasonal[i]-1),] <-
t(randomParameters[,paste0(seasonalNames[i],"_",c(1:(lagsSeasonal[i]-1)))]);
profilesRecentArray[j+i,lagsSeasonal[i],] <-
switch(Stype,
"A"=-apply(profilesRecentArray[j+i,1:(lagsSeasonal[i]-1),,drop=FALSE],3,sum),
"M"=1/apply(profilesRecentArray[j+i,1:(lagsSeasonal[i]-1),,drop=FALSE],3,prod),
0);
}
j <- j+max(initialSeasonalIndices);
k <- k+length(initialSeasonalIndices);
}
}
# ARIMA states in the profileRecent
if(arimaModel){
# See if the initials were estimated
# initialArimaNumber <- sum(substr(parametersNames,1,10)=="ARIMAState");
initialArimaNumber <- sum(substr(colnames(object$states),1,10)=="ARIMAState");
# This is needed in order to propagate initials of ARIMA to all components
if(object$initialType=="optimal" && any(c(arEstimate,maEstimate))){
if(nrow(nonZeroARI)>0 && nrow(nonZeroARI)>=nrow(nonZeroMA)){
for(i in 1:nsim){
# Call the function returning ARI and MA polynomials
### This is not optimal, as the polynomialiser() is called twice (for parameters and here),
### but this is simpler
# arimaPolynomials <- polynomialiser(randomParameters[i,polyIndex+1:sum(c(arOrders*arEstimate,maOrders*maEstimate))],
# arOrders, iOrders, maOrders, arRequired, maRequired, arEstimate, maEstimate,
# armaParameters, lags);
arimaPolynomials <- lapply(adamPolynomialiser(randomParameters[i,polyIndex+1:sum(c(arOrders*arEstimate,maOrders*maEstimate))],
arOrders, iOrders, maOrders,
arEstimate, maEstimate, armaParameters, lags), as.vector)
profilesRecentArray[j+componentsNumberARIMA, 1:initialArimaNumber, i] <-
randomParameters[i, k+1:initialArimaNumber];
profilesRecentArray[j+nonZeroARI[,2], 1:initialArimaNumber, i] <-
switch(Etype,
"A"= arimaPolynomials$ariPolynomial[nonZeroARI[,1]] %*%
t(profilesRecentArray[j+componentsNumberARIMA,
1:initialArimaNumber, i]),
"M"=exp(arimaPolynomials$ariPolynomial[nonZeroARI[,1]] %*%
t(log(profilesRecentArray[j+componentsNumberARIMA,
1:initialArimaNumber, i]))));
}
}
else{
for(i in 1:nsim){
# Call the function returning ARI and MA polynomials
# arimaPolynomials <- polynomialiser(randomParameters[i,polyIndex+1:sum(c(arOrders*arEstimate,maOrders*maEstimate))],
# arOrders, iOrders, maOrders, arRequired, maRequired, arEstimate, maEstimate,
# armaParameters, lags);
arimaPolynomials <- lapply(adamPolynomialiser(randomParameters[i,polyIndex+1:sum(c(arOrders*arEstimate,maOrders*maEstimate))],
arOrders, iOrders, maOrders,
arEstimate, maEstimate, armaParameters, lags), as.vector)
profilesRecentArray[componentsNumberETS+componentsNumberARIMA, 1:initialArimaNumber, i] <-
randomParameters[i, k+1:initialArimaNumber];
profilesRecentArray[j+nonZeroMA[,2], 1:initialArimaNumber, i] <-
switch(Etype,
"A"=arimaPolynomials$maPolynomial[nonZeroMA[,1]] %*%
t(profilesRecentArray[componentsNumberETS+componentsNumberARIMA,
1:initialArimaNumber, i]),
"M"=exp(arimaPolynomials$maPolynomial[nonZeroMA[,1]] %*%
t(log(profilesRecentArray[componentsNumberETS+componentsNumberARIMA,
1:initialArimaNumber, i]))));
}
}
}
j <- j+initialArimaNumber;
k <- k+initialArimaNumber;
}
# Regression part
if(xregModel){
xregNumberToEstimate <- sum(xregParametersEstimated);
profilesRecentArray[j+which(xregParametersEstimated==1),1,] <- t(randomParameters[,k+1:xregNumberToEstimate]);
# Normalise initials
for(i in which(xregParametersMissing!=0)){
profilesRecentArray[j+i,1,] <- -colSums(profilesRecentArray[j+which(xregParametersEstimated==1),1,]);
}
j[] <- j+xregNumberToEstimate;
k[] <- k+xregNumberToEstimate;
}
if(constantRequired){
profilesRecentArray[j+1,1,] <- randomParameters[,k+1];
}
if(is.null(object$occurrence)){
ot <- matrix(rep(1, obsInSample));
pt <- rep(1, obsInSample);
}
else{
ot <- matrix(actuals(object$occurrence));
pt <- fitted(object$occurrence);
}
yt <- matrix(actuals(object));
# Refit the model with the new parameter
adamRefitted <- adamRefitterWrap(yt, ot, arrVt, arrF, arrWt, matG,
Etype, Ttype, Stype,
lagsModelAll, indexLookupTable, profilesRecentArray,
componentsNumberETSSeasonal, componentsNumberETS,
componentsNumberARIMA, xregNumber, constantRequired);
arrVt[] <- adamRefitted$states;
fittedMatrix[] <- adamRefitted$fitted * as.vector(pt);
profilesRecentArray[] <- adamRefitted$profilesRecent;
# If this was a model in logarithms (e.g. ARIMA for sm), then take exponent
if(any(unlist(gregexpr("in logs",object$model))!=-1)){
fittedMatrix[] <- exp(fittedMatrix);
}
return(structure(list(timeElapsed=Sys.time()-startTime,
y=actuals(object), states=arrVt, refitted=fittedMatrix,
fitted=fitted(object), model=object$model,
transition=arrF, measurement=arrWt, persistence=matG,
profile=profilesRecentArray),
class="reapply"));
}
#' @export
reapply.adamCombined <- function(object, nsim=1000, bootstrap=FALSE, ...){
startTime <- Sys.time();
# Remove ICw, which are lower than 0.001
object$ICw[object$ICw<1e-2] <- 0;
object$ICw[] <- object$ICw / sum(object$ICw);
# List of refitted matrices
yRefitted <- vector("list", length(object$models));
names(yRefitted) <- names(object$models);
for(i in 1:length(object$models)){
if(object$ICw[i]==0){
next;
}
yRefitted[[i]] <- reapply(object$models[[i]], nsim=1000, bootstrap=FALSE, ...)$refitted;
}
# Get rid of specific models to save RAM
object$models <- NULL;
# Keep only the used weights
yRefitted <- yRefitted[object$ICw!=0];
object$ICw <- object$ICw[object$ICw!=0];
return(structure(list(timeElapsed=Sys.time()-startTime,
y=actuals(object), refitted=yRefitted,
fitted=fitted(object), model=object$model,
ICw=object$ICw),
class=c("reapplyCombined","reapply")));
}
#' @importFrom grDevices rgb
#' @export
plot.reapply <- function(x, ...){
ellipsis <- list(...);
ellipsis$x <- actuals(x);
if(any(class(ellipsis$x)=="zoo")){
yQuantiles <- zoo(matrix(0,length(ellipsis$x),11),order.by=time(ellipsis$x));
}
else{
yQuantiles <- ts(matrix(0,length(ellipsis$x),11),start=start(ellipsis$x),frequency=frequency(ellipsis$x));
}
quantileseq <- seq(0,1,length.out=11);
yQuantiles[,1] <- apply(x$refitted,1,quantile,0.975,na.rm=TRUE);
yQuantiles[,11] <- apply(x$refitted,1,quantile,0.025,na.rm=TRUE);
for(i in 2:10){
yQuantiles[,i] <- apply(x$refitted,1,quantile,quantileseq[i],na.rm=TRUE);
}
if(is.null(ellipsis$ylim)){
ellipsis$ylim <- range(c(as.vector(ellipsis$x),as.vector(fitted(x))),na.rm=TRUE);
}
if(is.null(ellipsis$main)){
ellipsis$main <- paste0("Refitted values of ",x$model);
}
if(is.null(ellipsis$ylab)){
ellipsis$ylab <- "";
}
do.call(plot, ellipsis);
polygon(c(time(yQuantiles),rev(time(yQuantiles))), c(as.vector(yQuantiles[,1]),rev(as.vector(yQuantiles[,11]))),
col=rgb(0.8,0.8,0.8,0.4), border="grey")
polygon(c(time(yQuantiles),rev(time(yQuantiles))), c(as.vector(yQuantiles[,2]),rev(as.vector(yQuantiles[,10]))),
col=rgb(0.8,0.8,0.8,0.5), border="grey")
polygon(c(time(yQuantiles),rev(time(yQuantiles))), c(as.vector(yQuantiles[,3]),rev(as.vector(yQuantiles[,9]))),
col=rgb(0.8,0.8,0.8,0.6), border="grey")
polygon(c(time(yQuantiles),rev(time(yQuantiles))), c(as.vector(yQuantiles[,4]),rev(as.vector(yQuantiles[,8]))),
col=rgb(0.8,0.8,0.8,0.7), border="grey")
polygon(c(time(yQuantiles),rev(time(yQuantiles))), c(as.vector(yQuantiles[,5]),as.vector(rev(yQuantiles[,7]))),
col=rgb(0.8,0.8,0.8,0.8), border="grey")
lines(ellipsis$x,col="black",lwd=1);
lines(fitted(x),col="purple",lwd=2,lty=2);
}
#' @export
plot.reapplyCombined <- function(x, ...){
ellipsis <- list(...);
ellipsis$x <- actuals(x);
if(any(class(ellipsis$x)=="zoo")){
yQuantiles <- zoo(matrix(0,length(ellipsis$x),11),order.by=time(ellipsis$x));
}
else{
yQuantiles <- ts(matrix(0,length(ellipsis$x),11),start=start(ellipsis$x),frequency=frequency(ellipsis$x));
}
quantileseq <- seq(0,1,length.out=11);
for(j in 1:length(x$refitted)){
yQuantiles[,1] <- yQuantiles[,1] + apply(x$refitted[[j]],1,quantile,0.975,na.rm=TRUE)* x$ICw[j];
yQuantiles[,11] <- yQuantiles[,11] + apply(x$refitted[[j]],1,quantile,0.025,na.rm=TRUE)* x$ICw[j];
for(i in 2:10){
yQuantiles[,i] <- yQuantiles[,i] + apply(x$refitted[[j]],1,quantile,quantileseq[i],na.rm=TRUE)* x$ICw[j];
}
}
if(is.null(ellipsis$ylim)){
ellipsis$ylim <- range(c(as.vector(ellipsis$x),as.vector(fitted(x))),na.rm=TRUE);
}
if(is.null(ellipsis$main)){
ellipsis$main <- paste0("Refitted values of ",x$model);
}
if(is.null(ellipsis$ylab)){
ellipsis$ylab <- "";
}
do.call(plot, ellipsis);
polygon(c(time(yQuantiles),rev(time(yQuantiles))), c(as.vector(yQuantiles[,1]),rev(as.vector(yQuantiles[,11]))),
col=rgb(0.8,0.8,0.8,0.4), border="grey")
polygon(c(time(yQuantiles),rev(time(yQuantiles))), c(as.vector(yQuantiles[,2]),rev(as.vector(yQuantiles[,10]))),
col=rgb(0.8,0.8,0.8,0.5), border="grey")
polygon(c(time(yQuantiles),rev(time(yQuantiles))), c(as.vector(yQuantiles[,3]),rev(as.vector(yQuantiles[,9]))),
col=rgb(0.8,0.8,0.8,0.6), border="grey")
polygon(c(time(yQuantiles),rev(time(yQuantiles))), c(as.vector(yQuantiles[,4]),rev(as.vector(yQuantiles[,8]))),
col=rgb(0.8,0.8,0.8,0.7), border="grey")
polygon(c(time(yQuantiles),rev(time(yQuantiles))), c(as.vector(yQuantiles[,5]),as.vector(rev(yQuantiles[,7]))),
col=rgb(0.8,0.8,0.8,0.8), border="grey")
lines(ellipsis$x,col="black",lwd=1);
lines(fitted(x),col="purple",lwd=2,lty=2);
}
#' @export
print.reapply <- function(x, ...){
nsim <- ncol(x$refitted);
cat("Time elapsed:",round(as.numeric(x$timeElapsed,units="secs"),2),"seconds");
cat("\nModel refitted:",x$model);
cat("\nNumber of simulation paths produced:",nsim);
}
#' @export
print.reapplyCombined <- function(x, ...){
nsim <- ncol(x$refitted[[1]]);
cat("Time elapsed:",round(as.numeric(x$timeElapsed,units="secs"),2),"seconds");
cat("\nModel refitted:",x$model);
cat("\nNumber of simulation paths produced:",nsim);
}
#' @rdname reapply
#' @export reforecast
reforecast <- function(object, h=10, newdata=NULL, occurrence=NULL,
interval=c("prediction", "confidence", "none"),
level=0.95, side=c("both","upper","lower"), cumulative=FALSE,
nsim=100, ...) UseMethod("reforecast")
#' @export
reforecast.default <- function(object, h=10, newdata=NULL, occurrence=NULL,
interval=c("prediction", "confidence", "none"),
level=0.95, side=c("both","upper","lower"), cumulative=FALSE,
nsim=100, ...){
warning(paste0("The method is not implemented for the object of the class ,",class(object)[1]),
call.=FALSE);
return(forecast(object=object, h=h, newdata=newdata, occurrence=occurrence,
interval=interval, level=level, side=side, cumulative=cumulative,
nsim=nsim, ...));
}
#' @export
reforecast.adam <- function(object, h=10, newdata=NULL, occurrence=NULL,
interval=c("prediction", "confidence", "none"),
level=0.95, side=c("both","upper","lower"), cumulative=FALSE,
nsim=100, bootstrap=FALSE, heuristics=NULL, ...){
objectRefitted <- reapply(object, nsim=nsim, bootstrap=bootstrap, heuristics=heuristics, ...);
ellipsis <- list(...);
# If the trim is not provided, set it to 1%
if(is.null(ellipsis$trim)){
trim <- 0.01;
}
else{
trim <- ellipsis$trim;
}
#### <--- This part is widely a copy-paste from forecast.adam()
interval <- match.arg(interval[1],c("none", "prediction", "confidence","simulated"));
side <- match.arg(side);
# Model type
model <- modelType(object);
Etype <- errorType(object);
Ttype <- substr(model,2,2);
Stype <- substr(model,nchar(model),nchar(model));
# Technical parameters
lagsModelAll <- modelLags(object);
lagsModelMax <- max(lagsModelAll);
profilesRecentArray <- objectRefitted$profile;
if(!is.null(object$initial$seasonal)){
if(is.list(object$initial$seasonal)){
componentsNumberETSSeasonal <- length(object$initial$seasonal);
}
else{
componentsNumberETSSeasonal <- 1;
}
}
else{
componentsNumberETSSeasonal <- 0;
}
componentsNumberETS <- length(object$initial$level) + length(object$initial$trend) + componentsNumberETSSeasonal;
componentsNumberARIMA <- sum(substr(colnames(object$states),1,10)=="ARIMAState");
obsStates <- nrow(object$states);
obsInSample <- nobs(object);
indexLookupTable <- adamProfileCreator(lagsModelAll, lagsModelMax,
obsInSample+h)$lookup[,-c(1:(obsInSample+lagsModelMax)),drop=FALSE];
yClasses <- class(actuals(object));
if(any(yClasses=="ts")){
# ts structure
if(h>0){
yForecastStart <- time(actuals(object))[obsInSample]+deltat(actuals(object));
}
else{
yForecastStart <- time(actuals(object))[1];
}
yFrequency <- frequency(actuals(object));
}
else{
# zoo thingy
yIndex <- time(actuals(object));
if(h>0){
yForecastIndex <- yIndex[obsInSample]+diff(tail(yIndex,2))*c(1:h);
}
else{
yForecastIndex <- yIndex;
}
}
# How many levels did user asked to produce
nLevels <- length(level);
# Cumulative forecasts have only one observation
if(cumulative){
# hFinal is the number of elements we will have in the final forecast
hFinal <- 1;
}
else{
if(h>0){
hFinal <- h;
}
else{
hFinal <- obsInSample;
}
}
# Create necessary matrices for the forecasts
if(any(yClasses=="ts")){
yForecast <- ts(vector("numeric", hFinal), start=yForecastStart, frequency=yFrequency);
yUpper <- yLower <- ts(matrix(0,hFinal,nLevels), start=yForecastStart, frequency=yFrequency);
}
else{
yForecast <- zoo(vector("numeric", hFinal), order.by=yForecastIndex);
yUpper <- yLower <- zoo(matrix(0,hFinal,nLevels), order.by=yForecastIndex);
}
# If the occurrence values are provided for the holdout
if(!is.null(occurrence) && is.numeric(occurrence)){
pForecast <- occurrence;
}
else{
# If this is a mixture model, produce forecasts for the occurrence
if(is.occurrence(object$occurrence)){
occurrenceModel <- TRUE;
if(object$occurrence$occurrence=="provided"){
pForecast <- rep(1,h);
}
else{
pForecast <- forecast(object$occurrence,h=h,newdata=newdata)$mean;
}
}
else{
occurrenceModel <- FALSE;
# If this was provided occurrence, then use provided values
if(!is.null(object$occurrence) && !is.null(object$occurrence$occurrence) &&
(object$occurrence$occurrence=="provided")){
pForecast <- object$occurrence$forecast;
}
else{
pForecast <- rep(1, h);
}
}
}
# Make sure that the values are of the correct length
if(h<length(pForecast)){
pForecast <- pForecast[1:h];
}
else if(h>length(pForecast)){
pForecast <- c(pForecast, rep(tail(pForecast,1), h-length(pForecast)));
}
# Set the levels
if(interval!="none"){
# Fix just in case a silly user used 95 etc instead of 0.95
if(any(level>1)){
level[] <- level / 100;
}
levelLow <- levelUp <- matrix(0,hFinal,nLevels);
levelNew <- matrix(level,nrow=hFinal,ncol=nLevels,byrow=TRUE);
# If this is an occurrence model, then take probability into account in the level.
# This correction is only needed for approximate, because the others contain zeroes
if(side=="both"){
levelLow[] <- (1-levelNew)/2;
levelUp[] <- (1+levelNew)/2;
}
else if(side=="upper"){
levelLow[] <- 0;
levelUp[] <- levelNew;
}
else{
levelLow[] <- 1-levelNew;
levelUp[] <- 1;
}
levelLow[levelLow<0] <- 0;
levelUp[levelUp<0] <- 0;
}
#### Return adam.predict if h<=0 ####
# If the horizon is zero, just construct fitted and potentially confidence interval thingy
if(h<=0){
# If prediction interval is needed, this can be done with predict.adam
if(any(interval==c("prediction","none"))){
warning(paste0("You've set h=",h," and interval=\"",interval,
"\". There is no point in using reforecast() function for your task. ",
"Using predict() method instead."),
call.=FALSE);
return(predict(object, newdata=newdata,
interval=interval,
level=level, side=side, ...));
}
yForecast[] <- rowMeans(objectRefitted$refitted);
if(interval=="confidence"){
for(i in 1:hFinal){
yLower[i,] <- quantile(objectRefitted$refitted[i,],levelLow[i,]);
yUpper[i,] <- quantile(objectRefitted$refitted[i,],levelUp[i,]);
}
}
return(structure(list(mean=yForecast, lower=yLower, upper=yUpper, model=object,
level=level, interval=interval, side=side),
class=c("adam.predict","adam.forecast")));
}
#### All the important matrices ####
# Last h observations of measurement
arrWt <- objectRefitted$measurement[obsInSample-c(h:1)+1,,,drop=FALSE];
# If the forecast horizon is higher than the in-sample, duplicate the last value in matWt
if(dim(arrWt)[1]<h){
arrWt <- array(tail(arrWt,1), c(h, ncol(arrWt), nsim), dimnames=list(NULL,colnames(arrWt),NULL));
}
# Deal with explanatory variables
if(ncol(object$data)>1){
xregNumber <- length(object$initial$xreg);
xregNames <- names(object$initial$xreg);
# The newdata is not provided
if(is.null(newdata) && ((!is.null(object$holdout) && nrow(object$holdout)<h) ||
is.null(object$holdout))){
# Salvage what data we can (if there is something)
if(!is.null(object$holdout)){
hNeeded <- h-nrow(object$holdout);
xreg <- tail(object$data,h);
xreg[1:nrow(object$holdout),] <- object$holdout;
}
else{
hNeeded <- h;
xreg <- tail(object$data,h);
}
if(is.matrix(xreg)){
warning("The newdata is not provided.",
"Predicting the explanatory variables based on the in-sample data.",
call.=FALSE);
for(i in 1:xregNumber){
xreg[,i] <- adam(object$data[,i+1],h=hNeeded,silent=TRUE)$forecast;
}
}
else{
warning("The newdata is not provided. Using last h in-sample observations instead.",
call.=FALSE);
}
}
else if(is.null(newdata) && !is.null(object$holdout) && nrow(object$holdout)>=h){
xreg <- object$holdout[1:h,,drop=FALSE];
}
else{
# If this is not a matrix / data.frame, then convert to one
if(!is.data.frame(newdata) && !is.matrix(newdata)){
newdata <- as.data.frame(newdata);
colnames(newdata) <- "xreg";
}
if(nrow(newdata)<h){
warning(paste0("The newdata has ",nrow(newdata)," observations, while ",h," are needed. ",
"Using the last available values as future ones."),
call.=FALSE);
newnRows <- h-nrow(newdata);
# xreg <- rbind(as.matrix(newdata),matrix(rep(tail(newdata,1),each=newnRows),newnRows,ncol(newdata)));
xreg <- newdata[c(1:nrow(newdata),rep(nrow(newdata)),each=newnRows),];
}
else if(nrow(newdata)>h){
warning(paste0("The newdata has ",nrow(newdata)," observations, while only ",h," are needed. ",
"Using the last ",h," of them."),
call.=FALSE);
xreg <- tail(newdata,h);
}
else{
xreg <- newdata;
}
}
# If the names are wrong, transform to data frame and expand
if(!all(xregNames %in% colnames(xreg))){
xreg <- as.data.frame(xreg);
}
# Expand the xreg if it is data frame to get the proper matrix
if(is.data.frame(xreg)){
testFormula <- formula(object);
# Remove response variable
testFormula[[2]] <- NULL;
# Expand the variables. We cannot use alm, because it is based on obsInSample
xregData <- model.frame(testFormula,data=xreg);
# Binary, flagging factors in the data
# Expanded stuff with all levels for factors
if(any((attr(terms(xregData),"dataClasses")=="factor")[-1])){
xregModelMatrix <- model.matrix(xregData,xregData,
contrasts.arg=lapply(xregData[attr(terms(xregData),"dataClasses")=="factor"],
contrasts, contrasts=FALSE));
}
else{
xregModelMatrix <- model.matrix(xregData,data=xregData);
}
colnames(xregModelMatrix) <- make.names(colnames(xregModelMatrix), unique=TRUE);
newdata <- as.matrix(xregModelMatrix)[,xregNames,drop=FALSE];
rm(xregData,xregModelMatrix);
}
else{
newdata <- xreg[,xregNames];
}
rm(xreg);
arrWt[,componentsNumberETS+componentsNumberARIMA+c(1:xregNumber),] <- newdata;
}
else{
xregNumber <- 0;
}
# See if constant is required
constantRequired <- !is.null(object$constant);
#### Simulate the data ####
# If scale model is included, produce forecasts
if(is.scale(object$scale)){
sigmaValue <- forecast(object$scale,h=h,newdata=newdata,interval="none")$mean;
}
else{
sigmaValue <- sigma(object);
}
# This stuff is needed in order to produce adequate values for weird models
EtypeModified <- Etype;
if(Etype=="A" && any(object$distribution==c("dlnorm","dinvgauss","dgamma","dls","dllaplace"))){
EtypeModified[] <- "M";
}
# Matrix for the errors
arrErrors <- array(switch(object$distribution,
"dnorm"=rnorm(h*nsim^2, 0, sigmaValue),
"dlaplace"=rlaplace(h*nsim^2, 0, sigmaValue/2),
"ds"=rs(h*nsim^2, 0, (sigmaValue^2/120)^0.25),
"dgnorm"=rgnorm(h*nsim^2, 0,
sigmaValue*sqrt(gamma(1/object$other$shape)/gamma(3/object$other$shape)),
object$other$shape),
"dlogis"=rlogis(h*nsim^2, 0, sigmaValue*sqrt(3)/pi),
"dt"=rt(h*nsim^2, obsInSample-nparam(object)),
"dalaplace"=ralaplace(h*nsim^2, 0,
sqrt(sigmaValue^2*object$other$alpha^2*(1-object$other$alpha)^2/
(object$other$alpha^2+(1-object$other$alpha)^2)),
object$other$alpha),
"dlnorm"=rlnorm(h*nsim^2, -extractScale(object)^2/2, extractScale(object))-1,
"dinvgauss"=rinvgauss(h*nsim^2, 1, dispersion=sigmaValue^2)-1,
"dgamma"=rgamma(h*nsim^2, shape=sigmaValue^{-2}, scale=sigmaValue^2)-1,
"dllaplace"=exp(rlaplace(h*nsim^2, 0, sigmaValue/2))-1,
"dls"=exp(rs(h*nsim^2, 0, (sigmaValue^2/120)^0.25))-1,
"dlgnorm"=exp(rgnorm(h*nsim^2, 0,
sigmaValue*sqrt(gamma(1/object$other$shape)/gamma(3/object$other$shape))))-1),
c(h,nsim,nsim));
# Normalise errors in order not to get ridiculous things on small nsim
if(nsim<=500){
if(Etype=="A"){
arrErrors[] <- arrErrors - array(apply(arrErrors,1,mean),c(h,nsim,nsim));
}
else{
arrErrors[] <- (1+arrErrors) / array(apply(1+arrErrors,1,mean),c(h,nsim,nsim))-1;
}
}
# Array of the simulated data
arrayYSimulated <- array(0,c(h,nsim,nsim));
# Start the loop... might take some time
arrayYSimulated[] <- adamReforecasterWrap(arrErrors,
array(rbinom(h*nsim^2, 1, pForecast), c(h,nsim,nsim)),
objectRefitted$transition,
arrWt,
objectRefitted$persistence,
EtypeModified, Ttype, Stype,
lagsModelAll, indexLookupTable, profilesRecentArray,
componentsNumberETSSeasonal, componentsNumberETS,
componentsNumberARIMA, xregNumber, constantRequired)$matrixYt;
#### Note that the cumulative doesn't work with oes at the moment!
if(cumulative){
yForecast[] <- mean(apply(arrayYSimulated,1,sum,na.rm=TRUE,trim=trim));
if(interval!="none"){
yLower[] <- quantile(apply(arrayYSimulated,1,sum,na.rm=TRUE),levelLow,type=7);
yUpper[] <- quantile(apply(arrayYSimulated,1,sum,na.rm=TRUE),levelUp,type=7);
}
}
else{
yForecast[] <- apply(arrayYSimulated,1,mean,na.rm=TRUE,trim=trim);
if(interval=="prediction"){
for(i in 1:h){
for(j in 1:nLevels){
yLower[i,j] <- quantile(arrayYSimulated[i,,],levelLow[i,j],na.rm=TRUE,type=7);
yUpper[i,j] <- quantile(arrayYSimulated[i,,],levelUp[i,j],na.rm=TRUE,type=7);
}
}
}
else if(interval=="confidence"){
for(i in 1:h){
yLower[i,] <- quantile(apply(arrayYSimulated[i,,],2,mean,na.rm=TRUE,trim=trim),levelLow[i,],na.rm=TRUE,type=7);
yUpper[i,] <- quantile(apply(arrayYSimulated[i,,],2,mean,na.rm=TRUE,trim=trim),levelUp[i,],na.rm=TRUE,type=7);
}
}
}
# Fix of prediction intervals depending on what has happened
if(interval!="none"){
# Make sensible values out of those weird quantiles
if(!cumulative){
if(any(levelLow==0)){
# zoo does not like, when you work with matrices of indices... silly thing
yBoundBuffer <- levelLow;
yBoundBuffer[] <- yLower
if(Etype=="A"){
yBoundBuffer[levelLow==0] <- -Inf;
yLower[] <- yBoundBuffer;
}
else{
yBoundBuffer[levelLow==0] <- 0;
yLower[] <- yBoundBuffer;
}
}
if(any(levelUp==1)){
# zoo does not like, when you work with matrices of indices... silly thing
yBoundBuffer <- levelUp;
yBoundBuffer[] <- yUpper
yBoundBuffer[levelUp==1] <- Inf;
yUpper[] <- yBoundBuffer;
}
}
else{
if(Etype=="A" && any(levelLow==0)){
yLower[] <- -Inf;
}
else if(Etype=="M" && any(levelLow==0)){
yLower[] <- 0;
}
if(any(levelUp==1)){
yUpper[] <- Inf;
}
}
# Substitute NAs and NaNs with zeroes
if(any(is.nan(yLower)) || any(is.na(yLower))){
yLower[is.nan(yLower)] <- switch(Etype,"A"=0,"M"=1);
yLower[is.na(yLower)] <- switch(Etype,"A"=0,"M"=1);
}
if(any(is.nan(yUpper)) || any(is.na(yUpper))){
yUpper[is.nan(yUpper)] <- switch(Etype,"A"=0,"M"=1);
yUpper[is.na(yUpper)] <- switch(Etype,"A"=0,"M"=1);
}
# Check what we have from the occurrence model
if(occurrenceModel){
# If there are NAs, then there's no variability and no intervals.
if(any(is.na(yUpper))){
yUpper[is.na(yUpper)] <- (yForecast/pForecast)[is.na(yUpper)];
}
if(any(is.na(yLower))){
yLower[is.na(yLower)] <- 0;
}
}
colnames(yLower) <- switch(side,
"both"=paste0("Lower bound (",(1-level)/2*100,"%)"),
"lower"=paste0("Lower bound (",(1-level)*100,"%)"),
"upper"=rep("Lower 0%",nLevels));
colnames(yUpper) <- switch(side,
"both"=paste0("Upper bound (",(1+level)/2*100,"%)"),
"lower"=rep("Upper 100%",nLevels),
"upper"=paste0("Upper bound (",level*100,"%)"));
}
else{
yUpper[] <- yLower[] <- NA;
}
# If this was a model in logarithms (e.g. ARIMA for sm), then take exponent
if(any(unlist(gregexpr("in logs",object$model))!=-1)){
yForecast[] <- exp(yForecast);
yLower[] <- exp(yLower);
yUpper[] <- exp(yUpper);
}
structure(list(mean=yForecast, lower=yLower, upper=yUpper, model=object,
level=level, interval=interval, side=side, cumulative=cumulative,
h=h, paths=arrayYSimulated),
class=c("adam.forecast","smooth.forecast","forecast"));
}
#### Other methods ####
#' @export
multicov.adam <- function(object, type=c("analytical","empirical","simulated"), h=10, nsim=1000,
...){
type <- match.arg(type);
# Model type
Ttype <- substr(modelType(object),2,2);
lagsModelAll <- modelLags(object);
lagsModelMax <- max(lagsModelAll);
lagsOriginal <- lags(object);
if(Ttype!="N"){
lagsOriginal <- c(1,lagsOriginal);
}
componentsNumberETS <- length(lagsOriginal);
componentsNumberETSSeasonal <- sum(lagsOriginal>1);
componentsNumberARIMA <- sum(substr(colnames(object$states),1,10)=="ARIMAState");
s2 <- sigma(object)^2;
matWt <- tail(object$measurement,h);
vecG <- matrix(object$persistence, ncol=1);
if(ncol(object$data)>1){
xregNumber <- ncol(object$data)-1;
}
else{
xregNumber <- 0;
}
matF <- object$transition;
if(type=="analytical"){
covarMat <- covarAnal(lagsModelAll, h, matWt[1,,drop=FALSE], matF, vecG, s2);
}
else if(type=="empirical"){
adamErrors <- rmultistep(object, h=h);
covarMat <- t(adamErrors) %*% adamErrors / (nobs(object) - h);
}
else if(type=="simulated"){
# This code is based on the forecast.adam() with simulations
obsInSample <- nobs(object, all=FALSE);
Etype <- errorType(object);
Stype <- substr(modelType(object),nchar(modelType(object)),nchar(modelType(object)));
# Get the lookup table
indexLookupTable <- adamProfileCreator(lagsModelAll, lagsModelMax,
obsInSample+h)$lookup[,-c(1:(obsInSample+lagsModelMax)),drop=FALSE];
profilesRecentTable <- object$profile;
lagsModelMin <- lagsModelAll[lagsModelAll!=1];
if(length(lagsModelMin)==0){
lagsModelMin <- Inf;
}
else{
lagsModelMin <- min(lagsModelMin);
}
# See if constant is required
constantRequired <- !is.null(object$constant);
matVt <- t(tail(object$states,lagsModelMax));
# If this is a mixture model, produce forecasts for the occurrence
if(is.occurrence(object$occurrence)){
if(object$occurrence$occurrence=="provided"){
pForecast <- rep(1,h);
}
else{
pForecast <- forecast(object$occurrence,h=h)$mean;
}
}
else{
# If this was provided occurrence, then use provided values
if(!is.null(object$occurrence) && !is.null(object$occurrence$occurrence) &&
(object$occurrence$occurrence=="provided")){
pForecast <- object$occurrence$forecast;
}
else{
pForecast <- rep(1, h);
}
}
arrVt <- array(NA, c(componentsNumberETS+componentsNumberARIMA+xregNumber+constantRequired, h+lagsModelMax, nsim));
arrVt[,1:lagsModelMax,] <- rep(matVt,nsim);
# Number of degrees of freedom to de-bias scales
df <- obsInSample-nparam(object);
# If the sample is too small, then use biased estimator
if(df<=0){
df[] <- obsInSample;
}
# If scale model is included, produce forecasts
if(is.scale(object$scale)){
# as.vector is needed to declass the mean.
scaleValue <- as.vector(forecast(object$scale,h=h,interval="none")$mean);
# De-bias the scales and transform to the appropriate scale
# dnorm, dlnorm fit model on square residuals
# dgnorm needs to be done with ^beta to get to 1/T part
# The rest do not require transformations, only de-bias
scaleValue[] <- switch(object$distribution,
"dlnorm"=,
"dnorm"=(scaleValue*obsInSample/df)^0.5,
"dgnorm"=((scaleValue^object$other$shape)*obsInSample/df)^{1/object$other$shape},
scaleValue*obsInSample/df);
}
else{
scaleValue <- object$scale*obsInSample/df;
}
matErrors <- matrix(switch(object$distribution,
"dnorm"=rnorm(h*nsim, 0, scaleValue),
"dlaplace"=rlaplace(h*nsim, 0, scaleValue),
"ds"=rs(h*nsim, 0, scaleValue),
"dgnorm"=rgnorm(h*nsim, 0, scaleValue, object$other$shape),
"dlogis"=rlogis(h*nsim, 0, scaleValue),
"dt"=rt(h*nsim, obsInSample-nparam(object)),
"dalaplace"=ralaplace(h*nsim, 0, scaleValue, object$other$alpha),
"dlnorm"=rlnorm(h*nsim, -scaleValue^2/2, scaleValue)-1,
"dinvgauss"=rinvgauss(h*nsim, 1, dispersion=scaleValue)-1,
"dgamma"=rgamma(h*nsim, shape=scaleValue^{-1}, scale=scaleValue)-1,
"dllaplace"=exp(rlaplace(h*nsim, 0, scaleValue))-1,
"dls"=exp(rs(h*nsim, 0, scaleValue))-1,
"dlgnorm"=exp(rgnorm(h*nsim, 0, scaleValue, object$other$shape))-1
),
h,nsim);
# Normalise errors in order not to get ridiculous things on small nsim
if(nsim<=500){
if(Etype=="A"){
matErrors[] <- matErrors - array(apply(matErrors,1,mean),c(h,nsim));
}
else{
matErrors[] <- (1+matErrors) / array(apply(1+matErrors,1,mean),c(h,nsim))-1;
}
}
# This stuff is needed in order to produce adequate values for weird models
EtypeModified <- Etype;
if(Etype=="A" && any(object$distribution==c("dlnorm","dinvgauss","dgamma","dls","dllaplace"))){
EtypeModified[] <- "M";
}
# States, Errors, Ot, Transition, Measurement, Persistence
ySimulated <- adamSimulatorWrap(arrVt, matErrors,
matrix(rbinom(h*nsim, 1, pForecast), h, nsim),
array(matF,c(dim(matF),nsim)), matWt,
matrix(vecG, componentsNumberETS+componentsNumberARIMA+xregNumber+constantRequired, nsim),
EtypeModified, Ttype, Stype,
lagsModelAll, indexLookupTable, profilesRecentTable,
componentsNumberETSSeasonal, componentsNumberETS,
componentsNumberARIMA, xregNumber, constantRequired)$matrixYt;
yForecast <- vector("numeric", h);
for(i in 1:h){
if(Ttype=="M" || (Stype=="M" & h>lagsModelMin)){
# Trim 1% of values just to resolve some issues with outliers
yForecast[i] <- mean(ySimulated[i,],na.rm=TRUE,trim=0.01);
}
else{
yForecast[i] <- mean(ySimulated[i,],na.rm=TRUE);
}
ySimulated[i,] <- ySimulated[i,]-yForecast[i];
# If it is the multiplicative error, return epsilon_t
if(Etype=="M"){
ySimulated[i,] <- ySimulated[i,]/yForecast[i];
}
}
covarMat <- (ySimulated %*% t(ySimulated))/nsim;
}
rownames(covarMat) <- colnames(covarMat) <- paste0("h",c(1:h));
return(covarMat);
}
#' @export
pointLik.adam <- function(object, log=TRUE, ...){
distribution <- object$distribution;
yInSample <- actuals(object);
obsInSample <- nobs(object);
if(is.occurrence(object$occurrence)){
otLogical <- yInSample!=0;
yFitted <- fitted(object) / fitted(object$occurrence);
}
else{
otLogical <- rep(TRUE, obsInSample);
yFitted <- fitted(object);
}
scale <- extractScale(object);
other <- switch(distribution,
"dalaplace"=object$other$alpha,
"dgnorm"=,"dlgnorm"=object$other$shape,
"dt"=object$other$nu);
Etype <- errorType(object);
likValues <- vector("numeric",obsInSample);
likValues[otLogical] <- switch(distribution,
"dnorm"=switch(Etype,
"A"=dnorm(x=yInSample[otLogical], mean=yFitted[otLogical],
sd=scale, log=TRUE),
"M"=dnorm(x=yInSample[otLogical], mean=yFitted[otLogical],
sd=scale*yFitted[otLogical], log=TRUE)),
"dlaplace"=switch(Etype,
"A"=dlaplace(q=yInSample[otLogical], mu=yFitted[otLogical],
scale=scale, log=TRUE),
"M"=dlaplace(q=yInSample[otLogical], mu=yFitted[otLogical],
scale=scale*yFitted[otLogical], log=TRUE)),
"ds"=switch(Etype,
"A"=ds(q=yInSample[otLogical],mu=yFitted[otLogical],
scale=scale, log=TRUE),
"M"=ds(q=yInSample[otLogical],mu=yFitted[otLogical],
scale=scale*sqrt(yFitted[otLogical]), log=TRUE)),
"dgnorm"=switch(Etype,
"A"=dgnorm(q=yInSample[otLogical], mu=yFitted[otLogical],
scale=scale, shape=other, log=TRUE),
"M"=suppressWarnings(dgnorm(q=yInSample[otLogical], mu=yFitted[otLogical],
scale=scale*yFitted[otLogical], shape=other,
log=TRUE))),
"dlogis"=switch(Etype,
"A"=dlogis(x=yInSample[otLogical], location=yFitted[otLogical],
scale=scale, log=TRUE),
"M"=dlogis(x=yInSample[otLogical], location=yFitted[otLogical],
scale=scale*yFitted[otLogical], log=TRUE)),
"dt"=switch(Etype,
"A"=dt(residuals(object)[otLogical], df=abs(other), log=TRUE),
"M"=dt(residuals(object)[otLogical]*yFitted[otLogical],
df=abs(other), log=TRUE)),
"dalaplace"=switch(Etype,
"A"=dalaplace(q=yInSample[otLogical], mu=yFitted[otLogical],
scale=scale, alpha=other, log=TRUE),
"M"=dalaplace(q=yInSample[otLogical], mu=yFitted[otLogical],
scale=scale*yFitted[otLogical], alpha=other, log=TRUE)),
"dlnorm"=dlnorm(x=yInSample[otLogical],
meanlog=log(yFitted[otLogical]) -scale^2/2,
sdlog=scale, log=TRUE),
"dllaplace"=dlaplace(q=log(yInSample[otLogical]), mu=log(yFitted[otLogical]),
scale=scale, log=TRUE),
"dls"=ds(q=log(yInSample[otLogical]), mu=log(yFitted[otLogical]),
scale=scale, log=TRUE),
"dlgnorm"=dgnorm(q=log(yInSample[otLogical]), mu=log(yFitted[otLogical]),
scale=scale, shape=other, log=TRUE),
"dinvgauss"=dinvgauss(x=yInSample[otLogical], mean=yFitted[otLogical],
dispersion=scale/yFitted[otLogical], log=TRUE),
"dgamma"=dgamma(x=yInSample[otLogical], shape=1/scale,
scale=scale*yFitted[otLogical], log=TRUE)
);
if(any(distribution==c("dllaplace","dls","dlgnorm"))){
likValues[otLogical] <- likValues[otLogical] - log(yInSample[otLogical]);
}
# If this is a mixture model, take the respective probabilities into account (differential entropy)
if(is.occurrence(object$occurrence)){
likValues[!otLogical] <- -switch(distribution,
"dnorm" = (log(sqrt(2*pi)*scale)+0.5),
"dlnorm" = (log(sqrt(2*pi)*scale)+0.5) -scale^2/2,
"dlogis" = 2,
"dlaplace" =,
"dllaplace" =,
"dalaplace" = (1 + log(2*scale)),
"dt" = ((scale+1)/2 * (digamma((scale+1)/2)-digamma(scale/2)) +
log(sqrt(scale) * beta(scale/2,0.5))),
"ds" =,
"dls" = (2 + 2*log(2*scale)),
"dgnorm" =,
"dlgnorm" = 1/other-log(other/(2*scale*gamma(1/other))),
"dinvgauss" = (0.5*(log(pi/2)+1+log(scale))),
"dgamma" = (1/scale + log(scale*yFitted[!otLogical]) +
log(gamma(1/scale)) + (1-1/scale)*digamma(1/scale))
);
likValues[] <- likValues + pointLik(object$occurrence);
}
likValues <- ts(likValues, start=start(yFitted), frequency=frequency(yFitted));
if(!log){
likValues[] <- exp(likValues);
}
return(likValues);
}
#' @export
modelType.adam <- function(object, ...){
etsModel <- any(unlist(gregexpr("ETS",object$model))!=-1);
if(etsModel){
modelType <- substring(object$model,
unlist(gregexpr("\\(",object$model))+1,
unlist(gregexpr("\\)",object$model))-1)[1];
}
else{
modelType <- "NNN";
}
return(modelType)
}
#' @export
errorType.adam <- function(object, ...){
model <- modelType(object);
if(model=="NNN"){
return(switch(object$distribution,
"dnorm"=,"dlaplace"=,"ds"=,"dgnorm"=,"dlogis"=,"dt"=,"dalaplace"="A",
"dlnorm"=,"dllaplace"=,"dls"=,"dlgnorm"=,"dinvgauss"=,"dgamma"="M"));
}
else{
return(substr(model,1,1));
}
}
#' @export
orders.adam <- function(object, ...){
return(object$orders);
}
#' @param obs Number of observations to produce in the simulated data.
#' @param nsim Number of series to generate from the model.
#' @param seed Random seed used in simulation of data.
#' @examples
#' # Fit ADAM to the data
#' ourModel <- adam(rnorm(100,100,10), model="AAdN")
#' # Simulate the data
#' x <- simulate(ourModel)
#'
#' @rdname adam
#' @export
simulate.adam <- function(object, nsim=1, seed=NULL, obs=nobs(object), ...){
# Start measuring the time of calculations
startTime <- Sys.time();
ellipsis <- list(...);
if(!is.null(seed)){
set.seed(seed);
}
# All the variables needed in the function
yInSample <- actuals(object);
yClasses <- class(yInSample);
obsInSample <- obs;
Etype <- errorType(object);
Ttype <- substr(modelType(object),2,2);
Stype <- substr(modelType(object),nchar(modelType(object)),nchar(modelType(object)));
lags <- object$lags;
lagsSeasonal <- lags[lags!=1];
lagsModelAll <- object$lagsAll;
lagsModelMax <- max(lagsModelAll);
persistence <- as.matrix(object$persistence);
# If there is xreg, but no deltas, increase persistence by including zeroes
# This can be considered as a failsafe mechanism
if(ncol(object$data)>1 && !any(substr(names(object$persistence),1,5)=="delta")){
persistence <- rbind(persistence,matrix(rep(0,sum(object$nParam[,2])),ncol=1));
}
# See if constant is required
constantRequired <- !is.null(object$constant);
# Expand persistence to include zero for the constant
# if(constantRequired){
#
# }
if(!is.null(object$initial$seasonal)){
if(is.list(object$initial$seasonal)){
componentsNumberETSSeasonal <- length(object$initial$seasonal);
}
else{
componentsNumberETSSeasonal <- 1;
}
}
else{
componentsNumberETSSeasonal <- 0;
}
componentsNumberETS <- length(object$initial$level) + length(object$initial$trend) + componentsNumberETSSeasonal;
componentsNumberARIMA <- sum(substr(colnames(object$states),1,10)=="ARIMAState");
# Prepare variables for xreg
if(!is.null(object$initial$xreg)){
xregModel <- TRUE;
#### Create xreg vectors ####
xreg <- object$data;
formula <- formula(object)
responseName <- all.vars(formula)[1];
# Robustify the names of variables
colnames(xreg) <- make.names(colnames(xreg),unique=TRUE);
# The names of the original variables
xregNamesOriginal <- all.vars(formula)[-1];
# Levels for the factors
xregFactorsLevels <- lapply(xreg,levels);
xregFactorsLevels[[responseName]] <- NULL;
# Expand the variables. We cannot use alm, because it is based on obsInSample
xregData <- model.frame(formula,data=as.data.frame(xreg));
# Binary, flagging factors in the data
xregFactors <- (attr(terms(xregData),"dataClasses")=="factor")[-1];
# Get the names from the standard model.matrix
xregNames <- colnames(model.matrix(xregData,data=xregData));
interceptIsPresent <- FALSE;
if(any(xregNames=="(Intercept)")){
interceptIsPresent[] <- TRUE;
xregNames <- xregNames[xregNames!="(Intercept)"];
}
# Expanded stuff with all levels for factors
if(any(xregFactors)){
xregModelMatrix <- model.matrix(xregData,xregData,
contrasts.arg=lapply(xregData[attr(terms(xregData),"dataClasses")=="factor"],
contrasts, contrasts=FALSE));
xregNamesModified <- colnames(xregModelMatrix)[-1];
}
else{
xregModelMatrix <- model.matrix(xregData,data=xregData);
xregNamesModified <- xregNames;
}
xregData <- as.matrix(xregModelMatrix);
# Remove intercept
if(interceptIsPresent){
xregData <- xregData[,-1,drop=FALSE];
}
xregNumber <- ncol(xregData);
# The indices of the original parameters
xregParametersMissing <- setNames(vector("numeric",xregNumber),xregNamesModified);
# # The indices of the original parameters
xregParametersIncluded <- setNames(vector("numeric",xregNumber),xregNamesModified);
# The vector, marking the same values of smoothing parameters
if(interceptIsPresent){
xregParametersPersistence <- setNames(attr(xregModelMatrix,"assign")[-1],xregNamesModified);
}
else{
xregParametersPersistence <- setNames(attr(xregModelMatrix,"assign"),xregNamesModified);
}
# If there are factors not in the alm data, create additional initials
if(any(!(xregNamesModified %in% xregNames))){
xregAbsent <- !(xregNamesModified %in% xregNames);
# Go through new names and find, where they came from. Then get the missing parameters
for(i in which(xregAbsent)){
# Find the name of the original variable
# Use only the last value... hoping that the names like x and x1 are not used.
xregNameFound <- tail(names(sapply(xregNamesOriginal,grepl,xregNamesModified[i])),1);
# Get the indices of all k-1 levels
xregParametersIncluded[xregNames[xregNames %in% paste0(xregNameFound,
xregFactorsLevels[[xregNameFound]])]] <- i;
# Get the index of the absent one
xregParametersMissing[i] <- i;
}
# Write down the new parameters
xregNames <- xregNamesModified;
}
# The vector of parameters that should be estimated (numeric + original levels of factors)
xregParametersEstimated <- xregParametersIncluded
xregParametersEstimated[xregParametersEstimated!=0] <- 1;
xregParametersEstimated[xregParametersMissing==0 & xregParametersIncluded==0] <- 1;
}
else{
xregModel <- FALSE;
xregNumber <- 0;
xregParametersMissing <- 0;
xregParametersIncluded <- 0;
xregParametersEstimated <- 0;
xregParametersPersistence <- 0;
}
profiles <- adamProfileCreator(lagsModelAll, lagsModelMax, obsInSample);
indexLookupTable <- profiles$lookup;
profilesRecentTable <- profiles$recent;
#### Prepare the necessary matrices ####
# States are defined similar to how it is done in adam.
arrVt <- array(t(object$states),c(ncol(object$states),nrow(object$states)+obsInSample-nobs(object),nsim),
dimnames=list(colnames(object$states),NULL,paste0("nsim",c(1:nsim))));
# Set profile, which is used in the data generation
profilesRecentTable <- t(object$states[1:lagsModelMax,]);
# Transition and measurement
arrF <- array(object$transition,c(dim(object$transition),nsim));
matWt <- object$measurement;
if(nrow(matWt)<obsInSample){
matWt <- rbind(matWt,
matrix(rep(tail(matWt,1),each=obsInSample-nrow(matWt)),
obsInSample-nrow(matWt), ncol(matWt)));
}
# Persistence matrix
matG <- array(persistence, c(length(persistence), nsim),
dimnames=list(names(persistence), paste0("nsim",c(1:nsim))));
if(is.null(object$occurrence)){
pt <- rep(1, obsInSample);
}
else{
pt <- fitted(object$occurrence);
}
# Number of degrees of freedom to de-bias scales
df <- obsInSample-nparam(object);
# If the sample is too small, then use biased estimator
if(df<=0){
df[] <- obsInSample;
}
# If scale model is included, produce forecasts
if(is.scale(object$scale)){
# as.vector is needed to declass the mean.
scaleValue <- as.vector(fitted(object$scale));
# De-bias the scales and transform to the appropriate scale
# dnorm, dlnorm fit model on square residuals
# dgnorm needs to be done with ^beta to get to 1/T part
# The rest do not require transformations, only de-bias
scaleValue[] <- switch(object$distribution,
"dlnorm"=,
"dnorm"=(scaleValue*obsInSample/df)^0.5,
"dgnorm"=((scaleValue^object$other$shape)*obsInSample/df)^{1/object$other$shape},
scaleValue*obsInSample/df);
}
else{
scaleValue <- object$scale*obsInSample/df;
}
matErrors <- matrix(switch(object$distribution,
"dnorm"=rnorm(obsInSample*nsim, 0, scaleValue),
"dlaplace"=rlaplace(obsInSample*nsim, 0, scaleValue),
"ds"=rs(obsInSample*nsim, 0, scaleValue),
"dgnorm"=rgnorm(obsInSample*nsim, 0, scaleValue, object$other$shape),
"dlogis"=rlogis(obsInSample*nsim, 0, scaleValue),
"dt"=rt(obsInSample*nsim, obsInSample-nparam(object)),
"dalaplace"=ralaplace(obsInSample*nsim, 0, scaleValue, object$other$alpha),
"dlnorm"=rlnorm(obsInSample*nsim, -scaleValue^2/2, scaleValue)-1,
"dinvgauss"=rinvgauss(obsInSample*nsim, 1, dispersion=scaleValue)-1,
"dgamma"=rgamma(obsInSample*nsim, shape=scaleValue^{-1}, scale=scaleValue)-1,
"dllaplace"=exp(rlaplace(obsInSample*nsim, 0, scaleValue))-1,
"dls"=exp(rs(obsInSample*nsim, 0, scaleValue))-1,
"dlgnorm"=exp(rgnorm(obsInSample*nsim, 0, scaleValue, object$other$shape))-1
), obsInSample, nsim);
# This stuff is needed in order to produce adequate values for weird models
EtypeModified <- Etype;
if(Etype=="A" && any(object$distribution==c("dlnorm","dinvgauss","dgamma","dls","dllaplace"))){
EtypeModified[] <- "M";
}
# Refit the model with the new parameter
ySimulated <- adamSimulatorWrap(arrVt, matErrors,
matrix(rbinom(obsInSample*nsim, 1, pt), obsInSample, nsim),
arrF, matWt, matG,
EtypeModified, Ttype, Stype,
lagsModelAll, indexLookupTable, profilesRecentTable,
componentsNumberETSSeasonal, componentsNumberETS,
componentsNumberARIMA, xregNumber, constantRequired);
# Set the proper time stamps for the fitted
if(any(yClasses=="zoo")){
# Get indices for the cases, when obsInSample was provided by user
yIndex <- time(yInSample)
yIndexDiff <- diff(head(yIndex,2));
yTime <- yIndex[1]+yIndexDiff*c(1:(obsInSample-1));
matrixYt <- zoo(array(ySimulated$matrixYt,c(obsInSample,nsim),
dimnames=list(NULL,paste0("nsim",c(1:nsim)))),
order.by=yTime);
}
else{
matrixYt <- ts(array(ySimulated$matrixYt,c(obsInSample,nsim),
dimnames=list(NULL,paste0("nsim",c(1:nsim)))),
start=start(yInSample), frequency=frequency(yInSample));
}
return(structure(list(timeElapsed=Sys.time()-startTime, model=object$model, distribution=object$distribution,
data=matrixYt, states=ySimulated$arrayVt, persistence=object$persistence,
measurement=matWt, transition=object$transition, initial=object$initial,
probability=pt, occurrence=object$occurrence,
residuals=matErrors, other=ellipsis),
class=c("adam.sim","smooth.sim")));
}
#' @export
print.adam.sim <- function(x, ...){
cat(paste0("Data generated from: ",x$model," estimated via adam()\n"));
cat(paste0("Number of generated series: ",ncol(x$data),"\n"));
}
##### Other methods to implement #####
# accuracy.adam <- function(object, holdout, ...){}
# pls.adam
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.