# File src/library/stats/R/acf.R # Part of the R package, https://www.R-project.org # # Copyright (C) 1999-2015 The R Core Team # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # A copy of the GNU General Public License is available at # https://www.R-project.org/Licenses/ acf <- function (x, lag.max = NULL, type = c("correlation", "covariance", "partial"), plot = TRUE, na.action = na.fail, demean = TRUE, ...) { type <- match.arg(type) if(type == "partial") { m <- match.call() ## need stats:: for non-standard evaluation m[[1L]] <- quote(stats::pacf) m$type <- NULL return(eval(m, parent.frame())) } series <- deparse1(substitute(x)) x <- na.action(as.ts(x)) x.freq <- frequency(x) x <- as.matrix(x) if(!is.numeric(x)) stop("'x' must be numeric") sampleT <- as.integer(nrow(x)) nser <- as.integer(ncol(x)) if(is.na(sampleT) || is.na(nser)) stop("'sampleT' and 'nser' must be integer") if (is.null(lag.max)) lag.max <- floor(10 * (log10(sampleT) - log10(nser))) lag.max <- as.integer(min(lag.max, sampleT - 1L)) if (is.na(lag.max) || lag.max < 0) stop("'lag.max' must be at least 0") if(demean) x <- sweep(x, 2, colMeans(x, na.rm = TRUE), check.margin=FALSE) lag <- matrix(1, nser, nser) lag[lower.tri(lag)] <- -1 acf <- .Call(C_acf, x, lag.max, type == "correlation") lag <- outer(0:lag.max, lag/x.freq) acf.out <- structure(list(acf = acf, type = type, n.used = sampleT, lag = lag, series = series, snames = colnames(x)), class = "acf") if (plot) { plot.acf(acf.out, ...) invisible(acf.out) } else acf.out } pacf <- function(x, lag.max, plot, na.action, ...) UseMethod("pacf") pacf.default <- function(x, lag.max = NULL, plot = TRUE, na.action = na.fail, ...) { series <- deparse1(substitute(x)) x <- drop(na.action(as.ts(x))) # use univariate code for a single series if(!is.numeric(x)) stop("'x' must be numeric") x.freq <- frequency(x) sampleT <- NROW(x) if (is.null(lag.max)) lag.max <- floor(10 * log10(sampleT / NCOL(x))) lag.max <- min(lag.max, sampleT - 1) if (lag.max < 1) stop("'lag.max' must be at least 1") if(is.matrix(x)) { if(anyNA(x)) stop("NAs in 'x'") nser <- ncol(x) x <- sweep(x, 2, colMeans(x), check.margin=FALSE) lag <- matrix(1, nser, nser) lag[lower.tri(lag)] <- -1 pacf <- ar.yw(x, order.max = lag.max)$partialacf lag <- outer(1L:lag.max, lag/x.freq) snames <- colnames(x) } else { x <- scale(x, TRUE, FALSE) acf <- drop(acf(x, lag.max = lag.max, plot = FALSE, na.action = na.action)$acf) pacf <- .Call(C_pacf1, acf, lag.max) lag <- array((1L:lag.max)/x.freq, dim=c(lag.max,1L,1L)) snames <- NULL } acf.out <- structure(.Data = list(acf = pacf, type = "partial", n.used = sampleT, lag = lag, series = series, snames = snames), class = "acf") if (plot) { plot.acf(acf.out, ...) invisible(acf.out) } else acf.out } plot.acf <- function (x, ci = 0.95, type = "h", xlab = "Lag", ylab = NULL, ylim = NULL, main = NULL, ci.col="blue", ci.type = c("white", "ma"), max.mfrow = 6, ask = Npgs > 1 && dev.interactive(), mar = if(nser > 2) c(3,2,2,0.8) else par("mar"), oma = if(nser > 2) c(1,1.2,1,1) else par("oma"), mgp = if(nser > 2) c(1.5,0.6,0) else par("mgp"), xpd = par("xpd"), cex.main = if(nser > 2) 1 else par("cex.main"), verbose = getOption("verbose"), ...) { ci.type <- match.arg(ci.type) if((nser <- ncol(x$lag)) < 1L) stop("x$lag must have at least 1 column") if (is.null(ylab)) ylab <- switch(x$type, correlation = "ACF", covariance = "ACF (cov)", partial = "Partial ACF") snames <- x$snames %||% paste("Series ", if(nser == 1L) x$series else 1L:nser) with.ci <- ci > 0 && x$type != "covariance" with.ci.ma <- with.ci && ci.type == "ma" && x$type == "correlation" if(with.ci.ma && x$lag[1L, 1L, 1L] != 0L) { warning("can use ci.type=\"ma\" only if first lag is 0") with.ci.ma <- FALSE } clim0 <- if (with.ci) qnorm((1 + ci)/2)/sqrt(x$n.used) else c(0, 0) Npgs <- 1L ## we will do [ Npgs x Npgs ] pages ! nr <- nser if(nser > 1L) { ## at most m x m (m := max.mfrow) panels per page sn.abbr <- if(nser > 2L) abbreviate(snames) else snames if(nser > max.mfrow) { ## We need more than one page: The plots are laid out ## such that we can manually paste the paper pages and get a ## nice square layout with diagonal ! ## NB: The same applies to pairs() where we'd want several pages Npgs <- ceiling(nser / max.mfrow) nr <- ceiling(nser / Npgs) # <= max.mfrow } opar <- par(mfrow = rep(nr, 2L), mar = mar, oma = oma, mgp = mgp, ask = ask, xpd = xpd, cex.main = cex.main) on.exit(par(opar)) if(verbose) { # FIXME: message() can be suppressed but not str() message("par(*) : ", appendLF=FALSE, domain = NA) str(par("mfrow","cex", "cex.main","cex.axis","cex.lab","cex.sub")) } } if (is.null(ylim)) { ## Calculate a common scale ylim <- range(x$acf[, 1L:nser, 1L:nser], na.rm = TRUE) if (with.ci) ylim <- range(c(-clim0, clim0, ylim)) if (with.ci.ma) { for (i in 1L:nser) { clim <- clim0 * sqrt(cumsum(c(1, 2*x$acf[-1, i, i]^2))) ylim <- range(c(-clim, clim, ylim)) } } } for (I in 1L:Npgs) for (J in 1L:Npgs) { dev.hold() ## Page [ I , J ] : Now do nr x nr 'panels' on this page iind <- (I-1)*nr + 1L:nr jind <- (J-1)*nr + 1L:nr if(verbose) message(gettextf("Page [%d,%d]: i =%s; j =%s", I, J, paste(iind,collapse=","), paste(jind,collapse=",")), domain = NA) for (i in iind) for (j in jind) if(max(i,j) > nser) { frame(); box(col = "light gray") ## the above is EXTREMELY UGLY; should have a version ## of frame() that really does advance a frame !! } else { clim <- if (with.ci.ma && i == j) clim0 * sqrt(cumsum(c(1, 2*x$acf[-1, i, j]^2))) else clim0 plot(x$lag[, i, j], x$acf[, i, j], type = type, xlab = xlab, ylab = if(j==1) ylab else "", ylim = ylim, ...) abline(h = 0) if (with.ci && ci.type == "white") abline(h = c(clim, -clim), col = ci.col, lty = 2) else if (with.ci.ma && i == j) { clim <- clim[-length(clim)] lines(x$lag[-1, i, j], clim, col = ci.col, lty = 2) lines(x$lag[-1, i, j], -clim, col = ci.col, lty = 2) } title(main %||% if(i == j) snames[i] else paste(sn.abbr[i], "&", sn.abbr[j]), line = if(nser > 2) 1 else 2) } if(Npgs > 1) { # label the page mtext(paste("[",I,",",J,"]"), side=1, line = -0.2, adj=1, col = "dark gray", cex = 1, outer = TRUE) } dev.flush() } invisible() } ccf <- function(x, y, lag.max = NULL, type = c("correlation", "covariance"), plot = TRUE, na.action = na.fail, ...) { type <- match.arg(type) if(is.matrix(x) || is.matrix(y)) stop("univariate time series only") X <- ts.intersect(as.ts(x), as.ts(y)) colnames(X) <- c(deparse(substitute(x))[1L], deparse(substitute(y))[1L]) acf.out <- acf(X, lag.max = lag.max, plot = FALSE, type = type, na.action = na.action) lag <- c(rev(acf.out$lag[-1,2,1]), acf.out$lag[,1,2]) y <- c(rev(acf.out$acf[-1,2,1]), acf.out$acf[,1,2]) acf.out$acf <- array(y, dim=c(length(y),1L,1L)) acf.out$lag <- array(lag, dim=c(length(y),1L,1L)) acf.out$snames <- paste(acf.out$snames, collapse = " & ") if (plot) { plot(acf.out, ...) return(invisible(acf.out)) } else return(acf.out) } `[.acf` <- function(x, i, j) { if(missing(j)) j <- seq_len(ncol(x$lag)) ii <- if(missing(i)) seq_len(nrow(x$lag)) else match(i, x$lag[, 1, 1], nomatch = NA_integer_) x$acf <- x$acf[ii, j, j, drop = FALSE] x$lag <- x$lag[ii, j, j, drop = FALSE] x } print.acf <- function(x, digits = 3L, ...) { type <- match(x$type, c("correlation", "covariance", "partial")) msg <- c("Autocorrelations", "Autocovariances", "Partial autocorrelations") cat("\n", msg[type]," of series ", sQuote(x$series), ", by lag\n\n", sep = "") nser <- ncol(x$lag) if(type != 2) x$acf <- round(x$acf, digits) if(nser == 1) { acfs <- setNames(drop(x$acf), format(drop(x$lag), digits = 3L)) print(acfs, digits = digits, ...) } else { acfs <- format(x$acf, ...) lags <- format(x$lag, digits = 3L) acfs <- array(paste0(acfs, " (", lags, ")"), dim = dim(x$acf)) dimnames(acfs) <- list(rep("", nrow(x$lag)), x$snames, x$snames) print(acfs, quote = FALSE, ...) } invisible(x) }