R/plot_ts.R

Defines functions plot_ts.rls_fit plot_ts_series plot_ts_iseq plot_ts.data.frame plot_ts.data.list plot_ts

Documented in plot_ts plot_ts.data.frame plot_ts.data.list plot_ts_iseq plot_ts.rls_fit plot_ts_series

# Do this in a separate file to see the generated help:
#library(devtools)
#document()
#load_all(as.package("../../onlineforecast"))
#?plot_ts
#?plot_ts.data.list
#?plot_ts.data.frame

#' Plot time series of observations and forecasts (lagged to be aligned in time).
#'
#' Generates time series plots depending on the variables matched by each regular expression given in the \code{patterns} argument.
#' 
#' The forecasts matrices in the \code{data.list} given in \code{object} will be lagged to be aligned in time (i.e. k-step forecasts will be lagged by k).
#'
#' Use the plotly package if argument \code{usely} is TRUE, see \code{\link{plotly_ts}()}.
#'
#' @title Time series plotting
#' @param object A \code{data.list} or \code{data.frame} with observations and forecasts, note diffe
#' @param patterns A character vector with regular expressions, see examples for use.
#' @param xlim The time range as a character of length 2 and form "YYYY-MM-DD" or POSIX. Date to start and end the plot.
#' @param ylims The \code{ylim} for each plot given in a list.
#' @param xlab A character with the label for the x-axis.
#' @param ylabs A character vector with labels for the y-axes.
#' @param mains A character vector with the main for each plot.
#' @param mainouter A character with the main at the top of the plot (can also be added afterwards with \code{title(main, outer=TRUE)}).
#' @param legendtexts A list with the legend texts for each plot (replaces the names of the variables).
#' @param colormaps A list of colormaps, which will be used in each plot.
#' @param xat POSIXt specifying where the ticks on x-axis should be put.
#' @param usely If TRUE then plotly will be used.
#' @param plotit If FALSE then the plot will not be generated, only data returned.
#' @param p The plot_ts parameters in a list, as generated with the function \code{\link{par_ts}()}.
#' @param ... Parameters passed to \code{\link{par_ts}}, see the list of parameters in \code{?\link{par_ts}}.
#' @seealso
#' \code{\link{par_ts}} for setting plot control parameters.
#'
#' \code{\link{regex}} for regular expressions to select which variables to plot.
#' 
#' @return A list with a data.frame with the data for each plot, if usely=TRUE, then a list of the figures (drawn with print(subplot(L, shareX=TRUE, nrows=length(L), titleY = TRUE))).
#'
#' @examples
#'
#' # Time series plots for \code{data.list}, same as for \code{data.frame} except use of \code{kseq}
#' D <- Dbuilding
#' plot_ts(D, c("heatload","Ta"), kseq=c(1,24))
#' # Make two plots (and set the space for the legend)
#' plot_ts(D, c("heatload","Ta"), kseq=c(1,24), legendspace=11)
#' # Only the Ta observations 
#' plot_ts(D, c("heatload","Taobs$"), kseq=c(1,24), legendspace=11)
#'
#' # Give labels
#' plot_ts(D, c("heatload","Ta"), kseq=c(1,24), xlab="Time", ylabs=c("Heat (kW)","Temperature (C)"))
#' # Mains (see mainsline in par_ts())
#' plot_ts(D, c("heatload","Ta"), kseq=c(1,24), mains=c("Heatload","Temperature"), mainsline=c(-1,-2))
#'
#' # Format of the xaxis (see par_ts())
#' plot_ts(D, c("heatload","Ta"), kseq=c(1,24), xaxisformat="%Y-%m-%d %H:%m")
#'
#' # Return the data, for other plots etc.
#' L <- plot_ts(D, c("heatload","Ta"), kseq=c(1,24))
#' names(L[[1]])
#' names(L[[2]])
#'
#'
#' @rdname plot_ts
#' @export
plot_ts <- function(object, patterns=".*", xlim = NA, ylims = NA, xlab = "", ylabs = NA,
                    mains = "", mainouter="", legendtexts = NA, colormaps = NA, xat = NA, usely = FALSE, plotit = TRUE, p = NA, ...){
    UseMethod("plot_ts")
}



#' @param kseq For \code{class(object)=="data.list"} an integer vector, default = NA. Control which forecast horizons to include in the plots. If NA all the horizons will be included.
#' @rdname plot_ts
#' @export
plot_ts.data.list <- function(object, patterns=".*", xlim = NA, ylims = NA, xlab = "", ylabs = NA,
                              mains = "", mainouter="", legendtexts = NA, colormaps = NA, xat = NA, usely=FALSE, plotit = TRUE, p=NA, kseq = NA, ...) {
    # Take par_ts setup parameters from options if there
    p <- par_ts(fromoptions=TRUE, p=p, ...)
    #
    DL <- object
    # Do a bit of checking
    if(is.null(DL$t)){ stop("No 't' in the data.list.")}
    # Should a subset be taken according to xlim?
    if(!is.na(xlim[1]) | length(xlim) > 1){
            if(is.na(xlim[1])) { xlim[1] <- DL$t[1] }
            if(length(xlim) == 1) { xlim[2] <- as.character(DL$t[length(DL$t)]) }
            DL <- subset(DL, in_range(xlim[1], DL$t, xlim[2]))
        }
    # More checking
    if(length(DL$t) == 0){ stop(pst("No data in the time range. ",xlim[1]," to ",xlim[2]))}
    # set kseq if not specified
    if( is.na(kseq[1]) ){
        tmp <- unique(unlist(lapply(DL, function(x){names(x)})))
        tmp <- tmp[grep("[k|h][[:digit:]]+$", tmp)]
        if( length(tmp) > 0 ){ kseq <- sort(as.integer(gsub("[k|h]","",tmp))) }
    }
    # Generate a data.frame with the series to be plotted
    X <- lapply_cbind_df(patterns, function(pattern) {
        # Find the variables to plot
        nms <- grep(pattern, names(DL), value = TRUE)
        #
        if(length(nms) == 0){
            warning("No names where found matching the pattern '",pattern,"'")
            tmp <- as.data.frame(matrix(NA,nrow=length(DL$t),ncol=1))
            names(tmp)[1] <- pattern
            return(tmp)
        }else{
            # Go through the names in nms
            do.call("cbind", lapply(nms, function(nm){
                if(is.null(dim(DL[[nm]]))) {
                    # It is a vector, just return it
                    X <- data.frame(DL[[nm]])
                    names(X) <- nm
                    return(X)
                } else {
                    # Its a matrix
                    # Find the columns with 'k' and digits
                    # Note the convention:
                    #   - starting with 'k' it's a forecast for t+k, and must be lagged to sync
                    #   - if it starts with 'h' then it's an observation for the k'th horizon and for
                    helper <- function(prefix){
                        i <- which(nams(DL[[nm]]) %in% pst(prefix,kseq))
                        if(length(i) > 0) {
                            X <- DL[[nm]][ ,i]
                            # Started with k, then it's forecasts and must be lagged to sync
                            if( prefix == "k" ){
                                ks <- as.integer(gsub("k","",nams(DL[[nm]])[i]))
                                X <- lagdf(X, lagseq=ks)
                            }
                            # Fix if it is a vector
                            if(is.null(dim(X))) {
                                X <- as.data.frame(X)
                                names(X) <- nams(DL[[nm]])[i]
                            }
                            nams(X) <- pst(nm, "_", nams(X))
                            return(X)
                        }else{
                            return(NULL)
                        }
                    }
                    X <- helper("k")
                    if(is.null(X[1])){
                        X <- helper("h")
                    }
                    if(is.null(X[1])){
                        # Not started with "k" or "h": Just take all columns
                        X <- as.data.frame(DL[[nm]])
                        names(X) <- pst(nm,"_",names(X))
                    }
                    return(X)
                }
            }))
        }
    })
    # 
    if(any(duplicated(nams(X)))){
        X <- X[ ,unique(nams(X))]
    }
    # Add "t" for the x-axis
    X$t <- DL$t
    # Since we added "_k" or "_h" to the matrix variables, we have to
    # strip it off, and pass on as the names to search for with patterns
    namesdata <- unlist(getse(strsplit(nams(X), "_k|_h"), 1))
    # Use the plot_ts function which takes the data.frame
    plot_ts.data.frame(X, patterns, ylims = ylims, xlab = xlab, ylabs = ylabs, mains = mains, mainouter = mainouter,
                       legendtexts = legendtexts, colormaps=colormaps, xat = xat, usely=usely, plotit = plotit, p=p, namesdata=namesdata, ...)
}


# Plot all with prefix
#' @param namesdata For \code{class(object)=="data.frame"} a character vector. Names of columns in object to be searched in, instead of \code{names(object)}.
#' @rdname plot_ts
#' @importFrom graphics par title
#' @export
plot_ts.data.frame <- function(object, patterns=".*", xlim = NA, ylims = NA, xlab = "", ylabs = NA,
                               mains = NA, mainouter="", legendtexts = NA, colormaps = NA, xat = NA,
                               usely=FALSE, plotit = TRUE, p = NA, namesdata=NA, ...) {
    # Take par_ts setup parameters from options if there
    p <- par_ts(fromoptions=TRUE, p=p, ...)
    #
    data <- object
    # Do a bit of checking
    if(nrow(data) == 0){ stop("No rows in the data.frame.")}
    if(is.null(data[ ,p$xnm])){ warning("No 't' or xnm found. If time is not in 't', then specify it in xnm (either as argument or in options(\"par_ts\").")}
    #
    if(!is.null(data[ ,p$xnm])){
        if(is.na(xlim[1])) { xlim[1] <- data[1,p$xnm]-1 } # if the xlim min is na, then take all points, so -1 since time point is always end of sampling (assumed in in_range)
        if(length(xlim)==1) { xlim[2] <- data[nrow(data),p$xnm] }
        data <- data[in_range(xlim[1], data[ ,p$xnm], xlim[2]), ]
    }
    # More checking
    if(nrow(data) == 0){ stop(pst("No data in the time range. ",xlim[1]," to ",xlim[2]))}
    # Extend all individual plots vars, if not set
    if(is.na(mains[1]) & length(mains)==1){ mains <- rep(NA,length(patterns)) }
    mainsline <- p$mainsline
    if(is.na(mainsline[1]) & length(mainsline)==1){ mainsline <- rep(NA,length(patterns)) }
    if(is.na(ylims[1]) & length(ylims)==1){ ylims  <- as.list(rep(NA,length(patterns))) }
    if(is.na(ylabs[1]) & length(ylabs)==1){ ylabs  <- rep(NA,length(patterns)) }
    if(is.na(legendtexts[1]) & length(legendtexts)==1){ legendtexts <- as.list(rep(NA,length(patterns))) }
    if(is.na(colormaps[1]) & length(colormaps)==1){ colormaps <- as.list(rep(NA,length(patterns))) }
    #
    if(usely){
        # with plotly
        if(!requireNamespace("plotly", quietly = TRUE)){
#            loadNamespace("plotly")
#        }else{
            stop("The plotly package must be installed.")
        }
        #
        Liseq <- list()
        for(ii in 1:length(patterns)) {
            Liseq[[ii]] <- plot_ts_iseq(data, patterns[ii], p$xnm, namesdata)
        }
        nlines <- length(unlist(Liseq))
        # Longest name
        maxchar <- max(nchar(names(data)[unique(unlist(Liseq))]))
        #colormap <- p$colorramp(nlines)
        #
        L <- list()
        # This is hacky, but take the operator from the namespce directly (cannot plotly::%>% below. And don't want to plotly in imports, since it's big and don't want to force installation)
        '%>%' <- plotly::'%>%'
        #
        for(ii in 1:length(patterns)) {
            iseq <- Liseq[[ii]]
            #
            fig <- plotly::plot_ly(x=data[ ,p$xnm])
            for(i in 1:length(iseq)){
                fig <- fig %>% plotly::add_lines(y = data[ ,iseq[i]],
                                                         name = names(data)[iseq[i]],
                                                         # color = colormap[iii]
                                                         )#, legendgroup = paste0('group',ii))
            }
            if(ii < length(patterns)){
                # Add empty to make legend gap
                fig <- fig %>% plotly::add_lines(y = rep("NA",nrow(data)), name=strrep("-",maxchar), line=list(color="white"))
            }
            # Add ylabs?
            if(!is.na(ylabs)[1]){
                fig <- fig %>% plotly::layout(yaxis=list(title=ylabs[ii]))
            }
            fig <- fig %>% plotly::layout(xaxis=list(title=xlab))
            # Keep it
            L[[ii]] <- fig
        }
        # Center legend
        L[[ii]] <- L[[ii]] %>% plotly::layout(legend = list(x = 100, y = 0.5))
        # Draw it
        if(plotit){
            print(plotly::subplot(L, shareX=TRUE, nrows=length(L), titleY = TRUE))
        }
        # Return if needed
        invisible(L)
    }else{
        #
        oldpar <- setpar("ts", mfrow=c(length(patterns),1), cex=p$cex)
        par(xpd=TRUE, mar = c(0, 4, 0.5, p$legendspace))
        on.exit(par(oldpar))
        #
        L <- lapply(1:length(patterns), function(i){
            df <- plot_ts_series(data, patterns[i], iplot=i, ylim=ylims[[i]], xlab=xlab, legendtext = legendtexts[[i]],
                           main=mains[i], mainline=mainsline[i], colormap=colormaps[[i]], xat = xat, plotit=plotit, p=p, namesdata=namesdata,
                           xaxis=(i==length(patterns)), ...)
            title(mainouter, outer=TRUE)
            if (!is.na(ylabs[1])){
                title(ylab = ylabs[i], yaxt = "s")
            }
            return(df)
        })
        invisible(L)
    }
}

#' @rdname plot_ts
#' @importFrom graphics par title
#' @export
plot_ts.matrix <- plot_ts.data.frame


#' @rdname plot_ts
#' @param data The data.frame from where to find columns to plot
#' @param xnm The name of the column
#' @param pattern The regex pattern
plot_ts_iseq <- function(data, pattern, xnm, namesdata){
    iseq <- integer(0)
    # Use these names when finding columns to plot
    if(is.na(namesdata)[1]){
        nms <- nams(data)
    }else{
        nms <- namesdata
    }
    # Find indexes of patterns in data
    # Do the for loop, to secure the order of the patterns between "|"s
    for (pf in strsplit(pattern, "\\|")[[1]]) {
        iseq <- c(iseq, grep(pf, nms))
    }
    # Only take unique (keeps the order)
    iseq <- unique(iseq) 
    #
    # Remove p$xnm if in nms
    iseq <- iseq[!nms[iseq] == xnm]
    #
    return(iseq)
}


# Plot all columns found with regex pattern
#' @importFrom graphics plot lines axis title axis.POSIXct mtext par legend
#' @rdname plot_ts
#' @param iplot The plot index
#' @param ylim ylim in the plot
#' @param main main title
#' @param mainline line for the main title
#' @param colormap the colormap for the lines
#' @param legendtext Text for the legend
#' @param xaxis Boolean determining to include the xaxis
plot_ts_series <- function(data, pattern, iplot = 1,
                           ylim = NA, xlab = "", main = "", mainline = -1.2, colormap = NA, legendtext = NA,
                           xat = NA, plotit = TRUE, p = NA, namesdata = NA, xaxis = TRUE, ...) {
    #
    # Take par_ts setup parameters from options or defaults
    p <- par_ts(fromoptions=TRUE, p=p, ...)
    #
    iseq <- plot_ts_iseq(data, pattern, p$xnm, namesdata)
    # Check if p$xnm is in the data
    if (any(names(data) == p$xnm)) {
        x <- data[, p$xnm]
    } else {
        x <- 1:nrow(data)
    }
    #
    if(plotit){
        #
        xlim <- c(min(x)+diff(range(x))*0.02, max(x))
        if(any(is.na(xlim))){ stop("Could not calculate range of x, probably there is NA in t!") }
        #
        if (all(is.na(iseq)) | length(iseq)==0){
            # No series to plot
            legendtext <- pst(pattern," not found")
            colormap <- 1
            ylim <- c(0,1)
            yat <- NA
        }else{
            # Limits on y-axis
            if (is.na(ylim[1])) {
                # For some weird reason range doesn't work with a data.frame of logicals, it works only on a vector of logicals
                if(all(sapply(iseq, function(i){ is.logical(data[, i]) }))){
                    # All are logicals
                    ylim <- c(0,1)
                }else{
                    ylim <- range(data[, iseq], na.rm = TRUE)
                    if(any(is.na(ylim)) | any(is.infinite(ylim))){
                        legendtext <- pst(pattern," all NA")
                        colormap <- 1
                        ylim <- c(0,1)
                        yat <- NA
                    }
                }
            }
            #
            if(is.na(colormap[1])){
                colormap <- p$colorramp(length(iseq))
            }
            #
            # EXTEND THE YLIM: to make room for multiple plots
            ylim <- ylim + c(-1,1) * diff(ylim) * p$ylimextend
            # for axis
            ylimmod <- ylim + c(-1,1) * diff(ylim) * p$yaxisextend
            #
            # Make the legend text
            if (p$legendrangeshow & is.na(legendtext[1])){
                rngtext <- do.call("rbind", lapply(1:length(iseq), function(i) {
                    tmp <- sapply(range(data[, iseq[i]], na.rm = TRUE), function(x){
                        if( x <= 10 ){ return(signif(x, digits = 2)) }else{ return(round(x)) } })
                    gsub("\\s+"," ",paste(tmp, collapse = " to "), perl=TRUE)
                }))
                legendtext <- paste0(nams(data)[iseq], ": ", rngtext)
            }else{
                legendtext <- paste0(nams(data)[iseq])
            }
        }
        #
        plot(x, x, type = "n", xlim = xlim, ylim = ylim, xaxs="r", yaxt = "n", bty = "n",
             xlab = "", ylab = "")
        # For grid
        xb <- c(xlim[1]-0.04*diff(xlim), xlim[2]+0.01*diff(xlim))
        # yb <- c(ylim[1]-0.04*diff(ylim), ylim[2]+0.01*diff(ylim))
        if(all(sapply(data[ ,iseq], class) == "logical")){
            # Its all logical
            yb <- c(0,1)
        }else{
            yb <- range(data[ ,iseq], na.rm=TRUE) #c(ylim[1]-0.04*diff(ylim), max(data[ ,iseq],na.rm=TRUE))#, ylim[2]+0.01*diff(ylim))
        }
        # BACKGROUND
        # polygon(c(xb[1],xb,rev(xb)), c(yb,rev(yb),yb[1]), col="grey95", lty=0)
        # GRID
        if(is.na(xat)){
            xat <- pretty(x)
            irm <- which(xat < min(x) | xat > max(x))
            if(length(irm)){ xat <- xat[-irm] }
        }
        if(!"yat" %in% ls()){
            # Where to put y grid and labels
            yat <- pretty(ylimmod, 3)
            yat <- yat[(ylim[1]-0.04*diff(ylim)) < yat]
            yat <- yat[yat <= ylim[2]]
            sapply(yat, function(y){ lines(xb,c(y,y), col="lightgrey", lty="dotted") })
            axis(2, yat, lwd=0, lwd.ticks=1)
            lines(rep(xb[1],2), range(yat))
        }
        # horizontal grid
        ygrid <- c(ylim[1]-0.04*diff(ylim), max(yb,yat))
        sapply(xat, function(x){ lines(c(x,x),ygrid,col="lightgrey",lty="dotted") })
        #
        # PLOT LINES
        if (!all(is.na(iseq))){
            for (i in 1:length(iseq)) {
                p$plotfun(x, data[, iseq[i]], col = colormap[i])
            }
        }
        title(main = main, line=mainline)
        # 
        # Make the xaxis
        if( xaxis ){
            if (any(nams(data) == p$xnm)) {
                if (class(data[ ,p$xnm])[1] != "POSIXct") {
                    axis(1, data[ ,p$xnm], xaxt = "s")
                } else {
                    # makes too few ticks: axis.POSIXct(1, data[ ,p$xnm], format = xaxisformat, xaxt = "s")
                    if(is.na(xat[1])){ xat <- pretty(data[ ,p$xnm]) }
                    # Format, per default NA, so make it here
                    xaxisformat <- p$xaxisformat
                    if(is.na(xaxisformat)){
                        if( all(as.numeric(xat,unit="secs") %% (24*3600) == 0) ){
                            xaxisformat <- "%Y-%m-%d"
                        }else{
                            xaxisformat <- "%Y-%m-%d %H:%M"
                        }
                    }
                    axis.POSIXct(1, data[ ,p$xnm], at = xat, format = xaxisformat, xaxt = "s", lwd=1, lwd.ticks=1)
                }
            } else {
                axis(1, 1:nrow(data), xaxt = "s", lwd=0, lwd.ticks=1)
            }
            mtext(xlab, 1, line=par()$mgp[1]-0.5)
        }
        #
        legend(x=xlim[2]+2*0.01*diff(xlim), y=ylim[2], legend=legendtext, lty = 1, col = colormap, cex = p$legendcex, bg="white", box.lwd=0.5)
    }
    #
    invisible(cbind(t=x, data[, iseq]))
}



#' Plot forecasts, residuals, cumulated residuals and RLS coefficients
#'
#' A useful plot for residual analysis and model validation of an RLS fitted forecast model.
#'
#' All parameters, except those described below, are simply passed to \code{\link{plot_ts}()}.
#' 
#' @title Plots for an rls_fit.
#' @param fit An \code{rls_fit}.
#' @param patterns See \code{\link{plot_ts}}. The default pattern finds the generated series in the function, '!!RLSinputs!!' will be replaced with the names of the RLS inputs (regression stage inputs).
#' @return The plotted data in a \code{data.list}.
#'
#' @seealso \code{\link{plot_ts}}.
#' @examples
#'
#' # Fit a model (see vignette 'setup-and-use-model'
#' D <- Dbuilding
#' D$scoreperiod <- in_range("2010-12-22", D$t)
#' model <- forecastmodel$new()
#' model$output = "heatload"
#' model$add_inputs(Ta = "Ta",
#'                  mu = "one()")
#' model$add_regprm("rls_prm(lambda=0.9)")
#' model$kseq <- c(3,18)
#' fit1 <- rls_fit(NA, model, D, returnanalysis = TRUE)
#'
#' # Plot it
#' plot_ts(fit1)
#'
#' # Return the data
#' Dplot <- plot_ts(fit1)
#'
#' # The RLS coefficients are now in a nice format
#' head(Dplot$mu)
#'
#' @rdname plot_ts
#' @export
plot_ts.rls_fit <- function(object, patterns = c("^y$|^Yhat$","^Residuals$","CumAbsResiduals$",pst("^",names(fit$Lfitval[[1]]),"$")),
                          xlim = NA, ylims = NA, xlab = "", ylabs = NA, mains = "", mainouter="", legendtexts = NA,
                          colormaps = NA, xat = NA, usely=FALSE, plotit=TRUE, p=NA, kseq = NA, ...){
    fit <- object
    # Calculate the residuals
    Residuals <- residuals(fit)
    # Prepares a data.list for the plot
    if(is.na(xlim[1])){
        if(!("scoreperiod" %in% names(fit$data))){
            isubset <- c(1,length(fit$data$t))
        }else{
            # Default is to plot the scoreperiod if there
            isubset <- which(fit$data$scoreperiod)
        }
        isubset <- min(isubset,na.rm=TRUE):max(isubset,na.rm=TRUE)#1:length(fit$data$t)
    }else{
        isubset <- in_range(xlim[1], fit$data$t, xlim[2])
    }
    if(is.na(kseq[1])){
        kseq <- fit$model$kseq
    }
    #
    CumAbsResiduals <- lapply_cbind_df(kseq, function(k){
        tmp <- abs(Residuals[isubset,pst("h",k)])
        tmp[is.na(tmp)] <- 0
        cumsum(tmp)
    })
    names(CumAbsResiduals) <- pst("h",kseq)
    #
    # Convert the parameter estimates
    nmsinput <- names(fit$Lfitval[[1]])
    tmp <- lapply(1:length(nmsinput), function(i){
        # Name of the input
        nm <- names(fit$Lfitval[[1]])[i]
        # Take the fitues each horizon for the input
        ik <- which(names(fit$Lfitval) %in% pst("k",kseq))
        X <- lapply_cbind_df(ik, function(ii){
            fit$Lfitval[[ii]][isubset,nm]
        })
        names(X) <- gsub("k","h",names(fit$Lfitval)[ik])
        return(X)
    })
    names(tmp) <- nmsinput
    #
    data <- list(y=fit$data[[fit$model$output]][isubset], Yhat=fit$Yhat[isubset,pst("k",kseq)], Residuals=Residuals[isubset,pst("h",kseq)], CumAbsResiduals=CumAbsResiduals)
    data <- c(data,tmp)
    data$t <- fit$data$t[isubset]
    class(data) <- "data.list"
    if(plotit){
        #
        if(is.na(ylabs[1])){
            ylabs <- c("Model output","Residuals","Cum. residuals",pst("Coef: ",nmsinput))
        }
        # The input names
        #patterns[patterns == "!!RLSinputs!!"] <- pst("^",nmsinput,"$"))
        # Make a plot of the RLS coefficients for each horizon
        plot_ts(data, patterns, xlim = xlim, ylims = ylims, xlab = xlab, ylabs = ylabs,
                mains = mains, mainouter=mainouter, legendtexts = legendtexts, colormaps=colormaps, xat = xat, usely=usely, p=p, kseq = kseq, ...)
    }
    # Return the data
    invisible(data)
}

Try the onlineforecast package in your browser

Any scripts or data that you put into this service are public.

onlineforecast documentation built on Oct. 12, 2023, 5:15 p.m.