blob: 5e21b07e91a5fd4b1edae0589fd746ea43820265 [file] [log] [blame]
# 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 <- deparse(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 <- deparse(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 <- if(is.matrix(x)) floor(10 * (log10(sampleT) - log10(ncol(x))))
else floor(10 * (log10(sampleT)))
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")
if (is.null(snames <- x$snames))
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(if (!is.null(main)) main else
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)
}