autolayer <- function(object, ...){
UseMethod("autolayer")
}
ggAddExtras <- function(xlab=NA, ylab=NA, main=NA){
dots <- eval.parent(quote(list(...)))
extras <- list()
if("xlab"%in%names(dots) || is.null(xlab) || !is.na(xlab)){
if("xlab"%in%names(dots)){
extras[[length(extras)+1]] <- ggplot2::xlab(dots$xlab)
}
else{
extras[[length(extras)+1]] <- ggplot2::xlab(xlab)
}
}
if("ylab"%in%names(dots) || is.null(ylab) || !is.na(ylab)){
if("ylab"%in%names(dots)){
extras[[length(extras)+1]] <- ggplot2::ylab(dots$ylab)
}
else{
extras[[length(extras)+1]] <- ggplot2::ylab(ylab)
}
}
if("main"%in%names(dots) || is.null(main) || !is.na(main)){
if("main"%in%names(dots)){
extras[[length(extras)+1]] <- ggplot2::ggtitle(dots$main)
}
else{
extras[[length(extras)+1]] <- ggplot2::ggtitle(main)
}
}
if("xlim"%in%names(dots)){
extras[[length(extras)+1]] <- ggplot2::xlim(dots$xlim)
}
if("ylim"%in%names(dots)){
extras[[length(extras)+1]] <- ggplot2::ylim(dots$ylim)
}
return(extras)
}
ggtsbreaks <- function(x){
# Make x axis contain only whole numbers (e.g., years)
return(unique(round(pretty(floor(x[1]):ceiling(x[2])))))
}
autoplot.acf <- function(object, ci=0.95, ...){
if (!requireNamespace("ggplot2", quietly = TRUE)) {
stop("ggplot2 is needed for this function to work. Install it via install.packages(\"ggplot2\")", call. = FALSE)
}
else{
if (!inherits(object, "acf")){
stop("autoplot.acf requires a acf object, use object=object")
}
data <- data.frame(Lag=object$lag,ACF=object$acf)
if (data$Lag[1] == 0 & object$type == "correlation"){
data <- data[-1,]
}
#Initialise ggplot object
p <- ggplot2::ggplot(ggplot2::aes_(x = ~Lag, xend = ~Lag, y = 0, yend = ~ACF),
data=data)
p <- p + ggplot2::geom_hline(yintercept = 0)
#Add data
p <- p + ggplot2::geom_segment(lineend = "butt")
#Add ci lines (assuming white noise input)
ci <- qnorm((1 + ci)/2)/sqrt(object$n.used)
p <- p + ggplot2::geom_hline(yintercept=c(-ci, ci), colour="blue", linetype="dashed")
#Prepare graph labels
if(object$series == "X"){
ylab <- "CCF"
ticktype <- "ccf"
main <- paste("Series:",object$snames)
}
else if(object$type == "partial"){
ylab <- "PACF"
ticktype <- "acf"
main <- paste("Series:",object$series)
}
else if(object$type == "correlation"){
ylab <- "ACF"
ticktype <- "acf"
main <- paste("Series:",object$series)
}
else{
ylab <- NULL
}
# Add seasonal x-axis
#Change ticks to be seasonal and prepare default title
if(!is.null(object$tsp))
freq <- object$tsp[3]
else
freq <- 1
if(!is.null(object$periods))
{
periods <- object$periods
periods <- periods[periods != freq]
minorbreaks <- periods * seq(-20:20)
}
else
minorbreaks <- NULL
p <- p + ggplot2::scale_x_continuous(breaks = seasonalaxis(freq,
length(data$Lag), type=ticktype, plot=FALSE), minor_breaks=minorbreaks)
p <- p + ggAddExtras(ylab=ylab, xlab="Lag", main=main)
return(p)
}
}
ggAcf <- function(x, lag.max = NULL,
type = c("correlation", "covariance", "partial"),
plot = TRUE, na.action = na.contiguous, demean=TRUE, ...){
cl <- match.call()
if(plot==TRUE){
cl$plot=FALSE
}
cl[[1]] <- quote(Acf)
object <- eval.parent(cl)
object$tsp <- tsp(x)
object$periods <- attributes(x)$msts
if(plot){
return(autoplot(object, ...))
}
else{
return(object)
}
}
ggPacf <- function(x, lag.max = NULL,
plot = TRUE, na.action = na.contiguous, demean=TRUE, ...)
{
object <- Acf(x, lag.max=lag.max, type="partial", na.action=na.action, demean=demean, plot=FALSE)
object$series <- deparse(substitute(x))
if(plot)
return(autoplot(object, ...))
else
return(object)
}
ggCcf <- function(x, y, lag.max=NULL, type=c("correlation","covariance"),
plot=TRUE, na.action=na.contiguous, ...){
cl <- match.call()
if(plot==TRUE){
cl$plot <- FALSE
}
cl[[1]] <- quote(Ccf)
object <- eval.parent(cl)
object$snames <- paste(substitute(x), "&", substitute(y))
if(plot==TRUE){
return(autoplot(object, ...))
}
else{
return(object)
}
}
autoplot.mpacf <- function(object, ...){
if (!requireNamespace("ggplot2", quietly = TRUE)) {
stop("ggplot2 is needed for this function to work. Install it via install.packages(\"ggplot2\")", call. = FALSE)
}
else{
if (!inherits(object, "mpacf")){
stop("autoplot.mpacf requires a mpacf object, use object=object")
}
if(!is.null(object$lower)){
data <- data.frame(Lag=1:object$lag, z=object$z, sig=(object$lower<0 & object$upper>0))
cidata <- data.frame(Lag=rep(1:object$lag,each=2) + c(-0.5,0.5), z=rep(object$z, each=2), upper=rep(object$upper, each=2), lower=rep(object$lower, each=2))
plotpi <- TRUE
}
else{
data <- data.frame(Lag=1:object$lag, z=object$z)
plotpi <- FALSE
}
#Initialise ggplot object
p <- ggplot2::ggplot()
p <- p + ggplot2::geom_hline(ggplot2::aes(yintercept=0), size=0.2)
#Add data
if(plotpi){
p <- p + ggplot2::geom_ribbon(ggplot2::aes_(x = ~Lag, ymin = ~lower, ymax = ~upper), data=cidata, fill="grey50")
}
p <- p + ggplot2::geom_line(ggplot2::aes_(x = ~Lag, y = ~z), data=data)
if(plotpi){
p <- p + ggplot2::geom_point(ggplot2::aes_(x = ~Lag, y = ~z, colour = ~sig), data=data)
}
#Change ticks to be seasonal
freq <- frequency(object$x)
msts <- is.element("msts",class(object$x))
# Add seasonal x-axis
if(msts)
{
periods <- attributes(object$x)$msts
periods <- periods[periods != freq]
minorbreaks <- periods * seq(-20:20)
}
else
minorbreaks <- NULL
p <- p + ggplot2::scale_x_continuous(breaks = seasonalaxis(frequency(object$x), length(data$Lag), type="acf", plot=FALSE),
minor_breaks=minorbreaks)
if(object$type=="partial"){
ylab <- "PACF"
}
else if(object$type=="correlation"){
ylab <- "ACF"
}
p <- p + ggAddExtras(ylab=ylab)
return(p)
}
}
ggtaperedacf <- function(x, lag.max=NULL, type=c("correlation", "partial"),
plot=TRUE, calc.ci=TRUE, level=95, nsim=100, ...){
cl <- match.call()
if(plot==TRUE){
cl$plot=FALSE
}
cl[[1]] <- quote(taperedacf)
object <- eval.parent(cl)
if(plot==TRUE){
return(autoplot(object, ...))
}
else{
return(object)
}
}
ggtaperedpacf <- function(x, ...){
ggtaperedacf(x, type="partial", ...)
}
autoplot.Arima <- function (object, type = c("both", "ar", "ma"), ...){
if (!requireNamespace("ggplot2", quietly = TRUE)) {
stop("ggplot2 is needed for this function to work. Install it via install.packages(\"ggplot2\")", call. = FALSE)
}
else{
if (is.Arima(object)){
#Detect type
type <- match.arg(type)
q <- p <- 0
if (length(object$model$phi) > 0) {
test <- abs(object$model$phi) > 1e-09
if (any(test)){
p <- max(which(test))
}
}
if (length(object$model$theta) > 0) {
test <- abs(object$model$theta) > 1e-09
if (any(test)) {
q <- max(which(test))
}
}
if (type == "both") {
if (p == 0)
type <- "ma"
else if (q == 0)
type <- "ar"
}
}
else if (inherits(object, "ar")){
type <- "ar"
p <- length(arroots(object)$roots)
q <- 0
}
else{
stop("autoplot.Arima requires an Arima object, use object=object")
}
if (type == "both") {
type <- c("ar", "ma")
}
#Prepare data
arData <- maData <- NULL
if("ar" %in% type){
arData <- arroots(object)
arData <- data.frame(roots = arData$roots, type = arData$type)
}
if("ma" %in% type){
maData <- maroots(object)
maData <- data.frame(roots = maData$roots, type = maData$type)
}
allRoots <- rbind(arData, maData)
allRoots$Real <- Re(1/allRoots$roots)
allRoots$Imaginary <- Im(1/allRoots$roots)
allRoots$UnitCircle <- factor(ifelse((abs(allRoots$roots) > 1), "Within", "Outside"))
#Initialise general ggplot object
p <- ggplot2::ggplot(ggplot2::aes_(x=~Real, y=~Imaginary, colour=~UnitCircle), data=allRoots)
p <- p + ggplot2::coord_fixed(ratio = 1)
p <- p + ggplot2::annotate("path", x=cos(seq(0,2*pi,length.out=100)),
y=sin(seq(0,2*pi,length.out=100)))
p <- p + ggplot2::geom_vline(xintercept = 0)
p <- p + ggplot2::geom_hline(yintercept = 0)
p <- p + ggAddExtras(xlab = "Real", ylab="Imaginary")
if(NROW(allRoots) == 0)
return(p + ggAddExtras(main = "No AR or MA roots"))
p <- p + ggplot2::geom_point(size=3)
if(length(type)==1){
p <- p + ggAddExtras(main = paste("Inverse",toupper(type),"roots"))
}
else{
p <- p + ggplot2::facet_wrap(~ type, labeller = function(labels) lapply(labels, function(x) paste(as.character(x), "roots")))
}
}
return(p)
}
autoplot.ar <- function(object, ...){
autoplot.Arima(object, ...)
}
autoplot.decomposed.ts <- function (object, labels=NULL, range.bars = NULL, ...){
if (!requireNamespace("ggplot2", quietly = TRUE)) {
stop("ggplot2 is needed for this function to work. Install it via install.packages(\"ggplot2\")", call. = FALSE)
}
else{
if (!inherits(object, "decomposed.ts")){
stop("autoplot.decomposed.ts requires a decomposed.ts object")
}
if(is.null(labels)){
labels <- c("seasonal","trend","remainder")
}
cn <- c("data", labels)
data <- data.frame(datetime = rep(time(object$x), 4),
y = c(object$x, object$seasonal, object$trend, object$random),
parts = factor(rep(cn, each=NROW(object$x)), levels=cn))
# Initialise ggplot object
p <- ggplot2::ggplot(ggplot2::aes_(x=~datetime, y=~y), data=data)
# Add data
int <- as.numeric(object$type=="multiplicative")
p <- p + ggplot2::geom_line(ggplot2::aes_(x=~datetime, y=~y), data=subset(data,data$parts!=cn[4]), na.rm=TRUE)
p <- p + ggplot2::geom_segment(ggplot2::aes_(x = ~datetime, xend = ~datetime, y = int, yend = ~y),
data=subset(data,data$parts==cn[4]), lineend = "butt", na.rm = TRUE)
p <- p + ggplot2::facet_grid("parts ~ .", scales="free_y", switch="y")
p <- p + ggplot2::geom_hline(ggplot2::aes_(yintercept = ~y), data=data.frame(y = int, parts = cn[4]))
if(is.null(range.bars)){
range.bars <- object$type == "additive"
}
if(range.bars){
yranges <- vapply(split(data$y, data$parts), function(x) range(x, na.rm = TRUE), numeric(2))
xranges <- range(data$datetime)
barmid <- apply(yranges, 2, mean)
barlength <- min(apply(yranges, 2, diff))
barwidth <- (1/64)*diff(xranges)
barpos <- data.frame(left = xranges[2]+barwidth, right = xranges[2]+barwidth*2,
top = barmid+barlength/2, bottom = barmid-barlength/2,
parts = colnames(yranges), datetime = xranges[2], y = barmid)
p <- p + ggplot2::geom_rect(ggplot2::aes_(xmin = ~left, xmax = ~right, ymax = ~top, ymin = ~bottom), data=barpos, fill="gray75", colour="black", size=1/3)
}
# Add axis labels
p <- p + ggAddExtras(main = paste("Decomposition of",object$type,"time series"), xlab="Time",
ylab="")
# Make x axis contain only whole numbers (e.g., years)
p <- p + ggplot2::scale_x_continuous(breaks=unique(round(pretty(data$datetime))))
return(p)
}
}
autoplot.ets <- function (object, range.bars = NULL, ...){
if (!requireNamespace("ggplot2", quietly = TRUE)) {
stop("ggplot2 is needed for this function to work. Install it via install.packages(\"ggplot2\")", call. = FALSE)
}
else{
if (!is.ets(object)){
stop("autoplot.ets requires an ets object, use object=object")
}
names <- c(y="observed", l="level", b="slope", s1="season")
data <- cbind(object$x, object$states[,colnames(object$states)%in%names(names)])
cn <- c("y",c(colnames(object$states)))
colnames(data) <- cn <- names[stats::na.exclude(match(cn, names(names)))]
#Convert to longform
data <- data.frame(datetime=rep(time(data),NCOL(data)), y=c(data),
parts=factor(rep(cn, each=NROW(data)), levels=cn))
#Initialise ggplot object
p <- ggplot2::ggplot(ggplot2::aes_(x=~datetime, y=~y), data=data, ylab="")
#Add data
p <- p + ggplot2::geom_line(na.rm=TRUE)
p <- p + ggplot2::facet_grid(parts ~ ., scales="free_y", switch="y")
browser()
if(is.null(range.bars)){
range.bars <- is.null(object$lambda)
}
if(range.bars){
yranges <- vapply(split(data$y, data$parts), function(x) range(x, na.rm = TRUE), numeric(2))
xranges <- range(data$datetime)
barmid <- apply(yranges, 2, mean)
barlength <- min(apply(yranges, 2, diff))
barwidth <- (1/64)*diff(xranges)
barpos <- data.frame(left = xranges[2]+barwidth, right = xranges[2]+barwidth*2,
top = barmid+barlength/2, bottom = barmid-barlength/2,
parts = colnames(yranges), datetime = xranges[2], y = barmid)
p <- p + ggplot2::geom_rect(ggplot2::aes_(xmin = ~left, xmax = ~right, ymax = ~top, ymin = ~bottom), data=barpos, fill="gray75", colour="black", size=1/3)
}
p <- p + ggAddExtras(xlab = NULL, ylab = "", main = paste("Decomposition by",object$method,"method"))
return(p)
}
}
autoplot.forecast <- function (object, include, PI=TRUE, shadecols=c("#596DD5","#D5DBFF"), fcol="#0000AA", flwd=0.5, ...){
if (!requireNamespace("ggplot2", quietly = TRUE)) {
stop("ggplot2 is needed for this function to work. Install it via install.packages(\"ggplot2\")", call. = FALSE)
}
else{
if (!is.forecast(object)){
stop("autoplot.forecast requires a forecast object, use object=object")
}
if(is.null(object$lower) | is.null(object$upper) | is.null(object$level)) {
PI <- FALSE
}
else if(!is.finite(max(object$upper))) {
PI <- FALSE
}
if (!is.null(object$model$terms) && !is.null(object$model$model)){
#Initialise original dataset
mt <- object$model$terms
yvar <- deparse(mt[[2]]) # Perhaps a better way to do this
xvar <- attr(mt,"term.labels")
vars <- c(yvar=yvar, xvar=xvar)
data <- object$model$model
colnames(data) <- names(vars)[match(colnames(data), vars)]
if(!is.null(object$model$lambda)){
data$yvar <- InvBoxCox(data$yvar, object$model$lambda)
}
}
else {
if (!is.null(object$x)) {
data <- data.frame(yvar=c(object$x))
}
else if (!is.null(object$residuals) && !is.null(object$fitted)) {
data <- data.frame(yvar=c(object$residuals+object$fitted))
}
else {
stop("Could not find data")
}
if (!is.null(object$series)) {
vars <- c(yvar=object$series)
}
else if (!is.null(object$model$call)) {
vars <- c(yvar=deparse(object$model$call$y))
if (vars=="object")
vars <- c(yvar="y")
}
else {
vars <- c(yvar="y")
}
}
#Initialise ggplot object
p <- ggplot2::ggplot()
# Cross sectional forecasts
if (!is.element("ts",class(object$mean))){
if (length(xvar) > 1){
stop("Forecast plot for regression models only available for a single predictor")
}
if(NCOL(object$newdata)==1){ # Make sure column has correct name
colnames(object$newdata) <- xvar
}
flwd <- 2*flwd # Scale for points
#Data points
p <- p + ggplot2::geom_point(ggplot2::aes_(x=~xvar, y=~yvar), data=data)
p <- p + ggplot2::labs(y=vars["yvar"], x=vars["xvar"])
#Forecasted intervals
if (PI){
levels <- NROW(object$level)
interval <- data.frame(xpred=rep(object$newdata[[1]],levels),lower=c(object$lower),upper=c(object$upper),level=object$level)
interval<-interval[order(interval$level,decreasing = TRUE),] #Must be ordered for gg z-index
p <- p + ggplot2::geom_linerange(ggplot2::aes_(x=~xpred, ymin=~lower, ymax=~upper, colour=~level), data=interval, size=flwd)
if(length(object$level)<=5){
p <- p + ggplot2::scale_colour_gradientn(breaks=object$level, colours = shadecols, guide="legend")
}
else{
p <- p + ggplot2::scale_colour_gradientn(colours = shadecols, guide="colourbar")
}
}
#Forecasted points
predicted <- data.frame(object$newdata, object$mean)
colnames(predicted) <- c("xpred", "ypred")
p <- p + ggplot2::geom_point(ggplot2::aes_(x=~xpred, y=~ypred), data=predicted, color=fcol, size=flwd)
#Line of best fit
coef <- data.frame(int=0,m=0)
i <- match("(Intercept)",names(object$model$coefficients))
if (i!=0){
coef$int <- object$model$coefficients[i]
if (NROW(object$model$coefficients)==2){
coef$m <- object$model$coefficients[-i]
}
}
else{
if (NROW(object$model$coefficients)==1){
coef$m <- object$model$coefficients
}
}
p <- p + ggplot2::geom_abline(intercept = coef$int, slope=coef$m)
}
else{
# Time series objects (assumed)
#Data points
if(!is.null(time(object$x))){
timex <- time(object$x)
}
else if (!is.null(time(object$model$residuals))){
timex <- time(object$model$residuals)
}
data <- data.frame(yvar = as.numeric(data$yvar), datetime = as.numeric(timex))
if(!missing(include))
data <- tail(data, include)
p <- p + ggplot2::scale_x_continuous()
p <- p + ggplot2::geom_line(ggplot2::aes_(x=~datetime, y=~yvar), data=data) +
ggplot2::labs(y=vars["yvar"], x="Time")
#Forecasted intervals
predicted <- data.frame(xvar = time(object$mean), yvar = object$mean)
colnames(predicted) <- c("datetime","ypred")
if (PI){
levels <- NROW(object$level)
interval <- data.frame(datetime=rep(predicted$datetime,levels),lower=c(object$lower),upper=c(object$upper),level=rep(object$level,each=NROW(object$mean)))
interval <- interval[order(interval$level,decreasing = TRUE),] #Must be ordered for gg z-index
p <- p + ggplot2::geom_ribbon(ggplot2::aes_(x=~datetime, ymin=~lower, ymax=~upper, group=~-level, fill=~level),data=interval)
if(min(object$level)<50){
scalelimit <- c(1,99)
}
else{
scalelimit <- c(50,99)
}
if(length(object$level)<=5){
p <- p + ggplot2::scale_fill_gradientn(breaks=object$level, colours=shadecols, limit=scalelimit, guide="legend")
}
else{
p <- p + ggplot2::scale_fill_gradientn(colours=shadecols, limit=scalelimit)
}
#Negative group is a work around for missing z-index
}
#Forecasted points
p <- p + ggplot2::geom_line(ggplot2::aes_(x=~datetime,y=~ypred), data=predicted, color=fcol, size=flwd)
}
p <- p + ggAddExtras(main=paste("Forecasts from ",object$method,sep=""))
return(p)
}
}
autoplot.mforecast <- function (object, PI = TRUE, facets = TRUE, colour = FALSE, ...){
if (!requireNamespace("ggplot2", quietly = TRUE)) {
stop("ggplot2 is needed for this function to work. Install it via install.packages(\"ggplot2\")", call. = FALSE)
}
else{
if (!is.mforecast(object)){
stop("autoplot.mforecast requires a mforecast object, use object=object")
}
if (is.ts(object$forecast[[1]]$mean)){
# ts forecasts
p <- autoplot(getResponse(object), facets = facets, colour = colour) + autolayer(object, ...)
if (facets){
p <- p + ggplot2::facet_wrap(~ series,
labeller = function(labels){
if(!is.null(object$method)){
lapply(labels, function(x) paste0(as.character(x), "\n", object$method[as.character(x)]))
}
else{
lapply(labels, function(x) paste0(as.character(x)))
}
},
ncol = 1,
scales = "free_y"
)
}
p <- p + ggAddExtras(ylab = NULL)
return(p)
}
else{
# lm forecasts
if (!requireNamespace("grid")){
stop("grid is needed for this function to work. Install it via install.packages(\"grid\")", call. = FALSE)
}
K <- length(object$forecast)
if (K<2){
warning("Expected at least two plots but forecast required less.")
}
#Set up vector arguments
if (missing(PI)){
PI <- rep(TRUE, K)
}
#Set up grid
# ncol: Number of columns of plots
# nrow: Number of rows needed, calculated from # of cols
gridlayout <- matrix(seq(1, K), ncol = 1, nrow = K)
grid::grid.newpage()
grid::pushViewport(grid::viewport(layout = grid::grid.layout(nrow(gridlayout), ncol(gridlayout))))
for (i in 1:K){
partialfcast <- object$forecast[[i]]
partialfcast$model <- mlmsplit(object$model,index=i)
matchidx <- as.data.frame(which(gridlayout == i, arr.ind = TRUE))
print(autoplot(structure(partialfcast,class="forecast"),
PI=PI[i], ...) + ggAddExtras(ylab=colnames(object$x)[i]),
vp = grid::viewport(layout.pos.row = matchidx$row,
layout.pos.col = matchidx$col))
}
}
}
}
ggtsdisplay <- function(x, plot.type=c("partial","histogram","scatter","spectrum"),
points=TRUE, smooth=FALSE,
lag.max, na.action=na.contiguous, theme=NULL, ...){
if (!requireNamespace("ggplot2", quietly = TRUE)) {
stop("ggplot2 is needed for this function to work. Install it via install.packages(\"ggplot2\")", call. = FALSE)
}
else if (!requireNamespace("grid", quietly = TRUE)) {
stop("grid is needed for this function to work. Install it via install.packages(\"grid\")", call. = FALSE)
}
else{
plot.type <- match.arg(plot.type)
main <- deparse(substitute(x))
if(!is.ts(x)){
x <- ts(x)
}
if(missing(lag.max)){
lag.max <- round(min(max(10*log10(length(x)), 3*frequency(x)), length(x)/3))
}
dots <- list(...)
if(is.null(dots$xlab))
dots$xlab <- ""
if(is.null(dots$ylab))
dots$ylab <- ""
labs <- match(c("xlab", "ylab", "main"), names(dots), nomatch=0)
#Set up grid for plots
gridlayout <- matrix(c(1,2,1,3), nrow=2)
grid::grid.newpage()
grid::pushViewport(grid::viewport(layout = grid::grid.layout(nrow(gridlayout), ncol(gridlayout))))
#Add ts plot with points
matchidx <- as.data.frame(which(gridlayout == 1, arr.ind = TRUE))
tsplot <- do.call(ggplot2::autoplot, c(object=quote(x), dots[labs]))
if(points){
tsplot <- tsplot + ggplot2::geom_point(size=0.5)
}
if(smooth){
tsplot <- tsplot + ggplot2::geom_smooth(method="loess", se=FALSE)
}
if(is.null(tsplot$labels$title)){ #Add title if missing
tsplot <- tsplot + ggplot2::ggtitle(main)
}
if(!is.null(theme)){
tsplot <- tsplot + theme
}
print(tsplot,
vp = grid::viewport(layout.pos.row = matchidx$row,
layout.pos.col = matchidx$col))
#Prepare Acf plot
acfplot <- do.call(ggAcf, c(x=quote(x), lag.max=lag.max, na.action=na.action, dots[-labs])) + ggplot2::ggtitle(NULL)
if(!is.null(theme)){
acfplot <- acfplot + theme
}
#Prepare last plot (variable)
if(plot.type == "partial"){
lastplot <- ggPacf(x, lag.max=lag.max, na.action=na.action) + ggplot2::ggtitle(NULL)
#Match y-axis
acfplotrange <- ggplot2::layer_scales(acfplot)$y$range$range
pacfplotrange <- ggplot2::layer_scales(lastplot)$y$range$range
yrange <- range(c(acfplotrange, pacfplotrange))
acfplot <- acfplot + ggplot2::ylim(yrange)
lastplot <- lastplot + ggplot2::ylim(yrange)
}
else if(plot.type == "histogram")
{
lastplot <- gghistogram(x, add.normal=TRUE, add.rug=TRUE) + ggplot2::xlab(main)
}
else if(plot.type == "scatter"){
scatterData <- data.frame(y = x[2:NROW(x)], x = x[1:NROW(x)-1])
lastplot <- ggplot2::ggplot(ggplot2::aes_(y = ~y, x = ~x), data=scatterData) +
ggplot2::geom_point() + ggplot2::labs(x = expression(Y[t-1]), y = expression(Y[t]))
}
else if(plot.type == "spectrum"){
specData <- spec.ar(x, plot=FALSE)
specData <- data.frame(spectrum = specData$spec, frequency = specData$freq)
lastplot <- ggplot2::ggplot(ggplot2::aes_(y = ~spectrum, x = ~frequency), data=specData)+
ggplot2::geom_line() + ggplot2::scale_y_log10()
}
if(!is.null(theme)){
lastplot <- lastplot + theme
}
#Add ACF plot
matchidx <- as.data.frame(which(gridlayout == 2, arr.ind = TRUE))
print(acfplot,
vp = grid::viewport(layout.pos.row = matchidx$row,
layout.pos.col = matchidx$col))
#Add last plot
matchidx <- as.data.frame(which(gridlayout == 3, arr.ind = TRUE))
print(lastplot,
vp = grid::viewport(layout.pos.row = matchidx$row,
layout.pos.col = matchidx$col))
}
}
gglagplot <- function(x, lags = 1, set.lags = 1:lags, diag=TRUE, diag.col="gray", do.lines = TRUE, colour = TRUE, continuous = TRUE, labels = FALSE, seasonal = TRUE, ...){
if (!requireNamespace("ggplot2", quietly = TRUE)) {
stop("ggplot2 is needed for this function to work. Install it via install.packages(\"ggplot2\")", call. = FALSE)
}
else{
freq <- frequency(x)
if(freq > 1){
linecol = cycle(x)
}
else{
seasonal=FALSE
continuous=TRUE
}
x <- as.matrix(x)
#Prepare data for plotting
n <- NROW(x)
data <- data.frame()
for(i in 1:NCOL(x)){
for(lagi in set.lags){
sname <- colnames(x)[i]
if(is.null(sname)){
sname <- deparse(match.call()$x)
}
data <- rbind(data,
data.frame(lagnum = 1:(n-lagi),
freqcur = ifelse(rep(seasonal,n-lagi), linecol[1:(n-lagi)], 1:(n-lagi)),
orig = x[1:(n-lagi),i],
lagged = x[(lagi+1):n,i],
lagVal = factor(rep(lagi, n-lagi)),
series = factor(rep(sname, n-lagi))))
}
}
if(!continuous){
data$freqcur <- factor(data$freqcur)
}
#Initialise ggplot object
p <- ggplot2::ggplot(ggplot2::aes_(x=~lagged, y=~orig), data=data)
if(diag){
p <- p + ggplot2::geom_abline(colour=diag.col, linetype="dashed")
}
if(labels){
linesize = 0.25 * (2 - do.lines)
}
else{
linesize = 0.5 * (2 - do.lines)
}
plottype <- if(do.lines){
ggplot2::geom_path
}
else{
ggplot2::geom_point
}
if(colour){
p <- p + plottype(ggplot2::aes_(colour=~freqcur), size=linesize)
}
else{
p <- p + plottype(size=linesize)
}
if(labels){
p <- p + ggplot2::geom_text(ggplot2::aes_(label=~lagnum))
}
#Ensure all facets are of size size (if extreme values are excluded in lag specification)
if(max(set.lags)>NROW(x)/2){
axissize <- rbind(aggregate(orig ~ series, data=data, min),aggregate(orig~ series, data=data, max))
axissize <- data.frame(series = rep(axissize$series, length(set.lags)), orig = rep(axissize$orig, length(set.lags)), lagVal = rep(set.lags, each=NCOL(x)))
p <- p + ggplot2::geom_blank(ggplot2::aes_(x=~orig, y=~orig), data=axissize)
}
#Facet
if(NCOL(x)>1){
p <- p + ggplot2::facet_wrap(~lagVal + series, scales = "free", labeller = function(labels) list(unname(unlist(do.call("Map", c(list(paste, sep=", lag "), lapply(rev(labels), as.character)))))))
}
else{
p <- p + ggplot2::facet_wrap(~lagVal, labeller = function(labels) lapply(labels, function(x) paste0("lag ",as.character(x))))
}
p <- p + ggplot2::theme(aspect.ratio=1)
if(colour){
if(seasonal)
{
if(freq==4L)
title <- "Quarter"
else if(freq==12L)
title <- "Month"
else if(freq==7L)
title <- "Day"
else if(freq==24L)
title <- "Hour"
else
title <- "Season"
}
else
title <- "Time"
if(continuous){
p <- p + ggplot2::guides(colour = ggplot2::guide_colourbar(title=title))
}
else{
p <- p + ggplot2::guides(colour = ggplot2::guide_legend(title=title))
}
}
p <- p + ggAddExtras(ylab = NULL, xlab = NULL)
return(p)
}
}
gglagchull <- function(x, lags = 1, set.lags = 1:lags, diag=TRUE, diag.col="gray", ...){
if (!requireNamespace("ggplot2", quietly = TRUE)) {
stop("ggplot2 is needed for this function to work. Install it via install.packages(\"ggplot2\")", call. = FALSE)
}
else{
x <- as.matrix(x)
#Prepare data for plotting
n <- NROW(x)
data <- data.frame()
for(i in 1:NCOL(x)){
for(lag in set.lags){
sname <- colnames(x)[i]
if(is.null(sname)){
sname <- substitute(x)
}
data <- rbind(data, data.frame(orig = x[(lag+1):n,i], lagged = x[1:(n-lag),i], lag = rep(lag, n-lag), series = rep(sname, n-lag))[grDevices::chull(x[(lag+1):n,i], x[1:(n-lag),i]),])
}
}
#Initialise ggplot object
p <- ggplot2::ggplot(ggplot2::aes_(x=~orig, y=~lagged), data=data)
if(diag){
p <- p + ggplot2::geom_abline(colour=diag.col, linetype="dashed")
}
p <- p + ggplot2::geom_polygon(ggplot2::aes_(group=~lag,colour=~lag,fill=~lag), alpha=1/length(set.lags))
p <- p + ggplot2::guides(colour = ggplot2::guide_colourbar(title="lag"))
p <- p + ggplot2::theme(aspect.ratio=1)
#Facet
if(NCOL(x)>1){
p <- p + ggplot2::facet_wrap(~series, scales = "free")
}
p <- p + ggAddExtras(ylab = "lagged", xlab = "original")
return(p)
}
}
ggmonthplot <- function (x, labels = NULL, times = time(x), phase = cycle(x), ...){
ggsubseriesplot(x, labels, times, phase, ...)
}
ggsubseriesplot <- function (x, labels = NULL, times = time(x), phase = cycle(x), ...){
if (!requireNamespace("ggplot2", quietly = TRUE)) {
stop("ggplot2 is needed for this function to work. Install it via install.packages(\"ggplot2\")", call. = FALSE)
}
else{
if (!inherits(x, "ts")){
stop("ggsubseriesplot requires a ts object, use x=object")
}
data <- data.frame(y=as.numeric(x),year=factor(trunc(time(x))),season=as.numeric(phase))
avgLines <- stats::aggregate(data$y, by=list(data$season), FUN=mean)
colnames(avgLines) <- c("season", "avg")
data <- merge(data, avgLines, by="season")
#Initialise ggplot object
p <- ggplot2::ggplot(ggplot2::aes_(x=~interaction(year, season), y=~y, group=~season), data=data, na.rm=TRUE)
#Remove vertical break lines
p <- p + ggplot2::theme(panel.grid.major.x = ggplot2::element_blank())
#Add data
p <- p + ggplot2::geom_line()
#Add average lines
p <- p + ggplot2::geom_line(ggplot2::aes_(y=~avg), col="#0000AA")
#Create x-axis labels
xfreq <- frequency(x)
if(xfreq==4){
xbreaks <- c("Q1","Q2","Q3","Q4")
xlab <- "Quarter"
}
else if (xfreq==7){
xbreaks <- c("Sunday", "Monday", "Tuesday", "Wednesday", "Thursday",
"Friday", "Saturday")
xlab <- "Day"
}
else if(xfreq==12){
xbreaks <- month.abb
xlab <- "Month"
}
else{
xbreaks <- 1:frequency(x)
xlab <- "Season"
}
midYear <- sort(levels(data$year))[length(levels(data$year))%/%2]
p <- p + ggplot2::scale_x_discrete(breaks=paste(midYear,".",1:xfreq,sep=""), labels=xbreaks)
#Graph labels
p <- p + ggAddExtras(ylab = deparse(substitute(x)), xlab = xlab)
return(p)
}
}
ggseasonplot <- function (x, year.labels=FALSE, year.labels.left=FALSE, type=NULL, col=NULL, continuous=FALSE, polar=FALSE, labelgap=0.04, ...){
if (!requireNamespace("ggplot2", quietly = TRUE)) {
stop("ggplot2 is needed for this function to work. Install it via install.packages(\"ggplot2\")", call. = FALSE)
}
else{
if (!inherits(x, "ts")){
stop("autoplot.seasonplot requires a ts object, use x=object")
}
if(!is.null(type)){
message("Plot types are not yet supported for seasonplot()")
}
# Check data are seasonal
s <- frequency(x)
if(s <= 1)
stop("Data are not seasonal")
data <- data.frame(y=as.numeric(x),year=trunc(time(x)),time=as.numeric(round(time(x)%%1,digits = 6)))
data$year <- if(continuous){
as.numeric(data$year)
}
else{
as.factor(data$year)
}
if(polar){
startValues <- data[data$time==0,]
if(round(data$time[1], 6) == 0){
startValues <- startValues[-1,]
}
startValues$time <- 1-.Machine$double.eps
levels(startValues$year) <- as.numeric(levels(startValues$year)) - 1
data <- rbind(data, startValues)
}
#Initialise ggplot object
p <- ggplot2::ggplot(ggplot2::aes_(x=~time, y=~y, group=~year, colour=~year), data=data, na.rm=TRUE)
#p <- p + ggplot2::scale_x_continuous()
#Add data
p <- p + ggplot2::geom_line()
if(!is.null(col)){
if(continuous){
p <- p + ggplot2::scale_color_gradientn(colours=col)
}
else{
ncol <- length(unique(data$year))
if(length(col)==1){
p <- p + ggplot2::scale_color_manual(guide="none", values=rep(col, ncol))
}
else{
p <- p + ggplot2::scale_color_manual(values=rep(col, ceiling(ncol/length(col)))[1:ncol])
}
}
}
if(year.labels){
yrlab <- stats::aggregate(time ~ year, data=data, FUN = max)
yrlab <- cbind(yrlab, offset=labelgap)
}
if(year.labels.left){
yrlabL <- stats::aggregate(time ~ year, data=data, FUN = min)
yrlabL <- cbind(yrlabL, offset=-labelgap)
if(year.labels){
yrlab <- rbind(yrlab, yrlabL)
}
}
if(year.labels | year.labels.left){
yrlab <- merge(yrlab, data)
yrlab$time <- yrlab$time+yrlab$offset
p <- p + ggplot2::guides(colour=FALSE)
p <- p + ggplot2::geom_text(ggplot2::aes_(x=~time, y=~y, label=~year), data=yrlab)
}
# Add seasonal labels
if(s == 12)
{
labs <- month.abb
xLab <- "Month"
}
else if(s == 4)
{
labs <- paste("Q",1:4,sep="")
xLab <- "Quarter"
}
else if(s == 7)
{
labs <- c("Sun","Mon","Tue","Wed","Thu","Fri","Sat")
xLab <- "Day"
}
else
{
labs <- NULL
xLab <- "Season"
}
if(polar){
labs <- c(labs, '')
p <- p + ggplot2::coord_polar()
}
p <- p + ggplot2::scale_x_continuous(breaks=sort(unique(data$time)), minor_breaks=NULL, labels=labs)
#Graph title and axes
p <- p + ggAddExtras(main=paste("Seasonal plot:", deparse(substitute(x))), xlab=xLab, ylab=NULL)
return(p)
}
}
autoplot.splineforecast <- function (object, PI=TRUE, ...){
if (!requireNamespace("ggplot2", quietly = TRUE)) {
stop("ggplot2 is needed for this function to work. Install it via install.packages(\"ggplot2\")", call. = FALSE)
}
else{
p <- autoplot(object$x) + autolayer(object)
p <- p + ggplot2::geom_point(size=2)
fit <- data.frame(datetime=as.numeric(time(object$fitted)),y=as.numeric(object$fitted))
p <- p + ggplot2::geom_line(ggplot2::aes_(x=~datetime,y=~y), colour="red", data=fit)
p <- p + ggAddExtras(ylab=deparse(object$model$call$x))
return(p)
}
}
autoplot.stl <- function (object, labels = NULL, range.bars = TRUE, ...){
if (!requireNamespace("ggplot2", quietly = TRUE)) {
stop("ggplot2 is needed for this function to work. Install it via install.packages(\"ggplot2\")", call. = FALSE)
}
else{
if (!inherits(object, "stl")){
stop("autoplot.stl requires a stl object, use x=object")
}
# Re-order series as trend, seasonal, remainder
object$time.series <- object$time.series[,c("trend","seasonal","remainder")]
if(is.null(labels)){
labels <- colnames(object$time.series)
}
data <- object$time.series
cn <- c("data",labels)
data <- data.frame(datetime=rep(time(data),NCOL(data)+1), y=c(rowSums(data),data),
parts=factor(rep(cn, each=NROW(data)), levels=cn))
#Initialise ggplot object
p <- ggplot2::ggplot(ggplot2::aes_(x=~datetime, y=~y), data=data)
#Add data
# Timeseries lines
p <- p + ggplot2::geom_line(ggplot2::aes_(x=~datetime, y=~y), data=subset(data,data$parts!=cn[4]), na.rm=TRUE)
p <- p + ggplot2::geom_segment(ggplot2::aes_(x = ~datetime, xend = ~datetime, y = 0, yend = ~y),
data=subset(data,data$parts==cn[4]), lineend = "butt")
# Rangebars
if(range.bars){
yranges <- vapply(split(data$y, data$parts), function(x) range(x, na.rm = TRUE), numeric(2))
xranges <- range(data$datetime)
barmid <- apply(yranges, 2, mean)
barlength <- min(apply(yranges, 2, diff))
barwidth <- (1/64)*diff(xranges)
barpos <- data.frame(left = xranges[2]+barwidth, right = xranges[2]+barwidth*2,
top = barmid+barlength/2, bottom = barmid-barlength/2,
parts = colnames(yranges), datetime = xranges[2], y = barmid)
p <- p + ggplot2::geom_rect(ggplot2::aes_(xmin = ~left, xmax = ~right, ymax = ~top, ymin = ~bottom), data=barpos, fill="gray75", colour="black", size=1/3)
}
# Remainder
p <- p + ggplot2::facet_grid("parts ~ .", scales="free_y", switch="y")
p <- p + ggplot2::geom_hline(ggplot2::aes_(yintercept = ~y), data=data.frame(y = 0, parts = cn[4]))
# Add axis labels
p <- p + ggAddExtras(xlab="Time", ylab="")
# Make x axis contain only whole numbers (e.g., years)
p <- p + ggplot2::scale_x_continuous(breaks=unique(round(pretty(data$datetime))))
# ^^ Remove rightmost x axis gap with `expand=c(0.05, 0, 0, 0)` argument when assymetric `expand` feature is supported
# issue: tidyverse/ggplot2#1669
return(p)
}
}
autoplot.StructTS <- function (object, labels = NULL, range.bars = TRUE, ...){
if (!requireNamespace("ggplot2", quietly = TRUE)) {
stop("ggplot2 is needed for this function to work. Install it via install.packages(\"ggplot2\")", call. = FALSE)
}
else{
if (!inherits(object, "StructTS")){
stop("autoplot.StructTS requires a StructTS object.")
}
if(is.null(labels)){
labels <- colnames(object$fitted)
}
data <- object$fitted
cn <- c("data",labels)
data <- data.frame(datetime=rep(time(data),NCOL(data)+1), y=c(object$data,data),
parts=factor(rep(cn, each=NROW(data)), levels=cn))
#Initialise ggplot object
p <- ggplot2::ggplot(ggplot2::aes_(x=~datetime, y=~y), data=data)
#Add data
p <- p + ggplot2::geom_line(ggplot2::aes_(x=~datetime, y=~y), na.rm=TRUE)
p <- p + ggplot2::facet_grid("parts ~ .", scales="free_y", switch="y")
if(range.bars){
yranges <- vapply(split(data$y, data$parts), function(x) range(x, na.rm = TRUE), numeric(2))
xranges <- range(data$datetime)
barmid <- apply(yranges, 2, mean)
barlength <- min(apply(yranges, 2, diff))
barwidth <- (1/64)*diff(xranges)
barpos <- data.frame(left = xranges[2]+barwidth, right = xranges[2]+barwidth*2,
top = barmid+barlength/2, bottom = barmid-barlength/2,
parts = colnames(yranges), datetime = xranges[2], y = barmid)
p <- p + ggplot2::geom_rect(ggplot2::aes_(xmin = ~left, xmax = ~right, ymax = ~top, ymin = ~bottom), data=barpos, fill="gray75", colour="black", size=1/3)
}
# Add axis labels
p <- p + ggAddExtras(xlab="Time", ylab="")
# Make x axis contain only whole numbers (e.g., years)
p <- p + ggplot2::scale_x_continuous(breaks=unique(round(pretty(data$datetime))))
return(p)
}
}
autoplot.seas <- function (object, labels = NULL, range.bars = NULL, ...){
if (!requireNamespace("ggplot2", quietly = TRUE)) {
stop("ggplot2 is needed for this function to work. Install it via install.packages(\"ggplot2\")", call. = FALSE)
}
else{
if (!inherits(object, "seas")){
stop("autoplot.seas requires a seas object")
}
if(is.null(labels)){
labels <- c("seasonal", "trend", "remainder")
}
data <- cbind(object$x, object$data[,c("seasonal", "trend", "irregular")])
cn <- c("data",labels)
data <- data.frame(datetime=rep(time(data),NCOL(data)), y=c(data),
parts=factor(rep(cn, each=NROW(data)), levels=cn))
#Initialise ggplot object
p <- ggplot2::ggplot(ggplot2::aes_(x=~datetime, y=~y), data=data)
#Add data
p <- p + ggplot2::geom_line(ggplot2::aes_(x=~datetime, y=~y), data=subset(data,data$parts!=cn[4]), na.rm=TRUE)
p <- p + ggplot2::geom_segment(ggplot2::aes_(x = ~datetime, xend = ~datetime, y = 1, yend = ~y),
data=subset(data,data$parts==cn[4]), lineend = "butt")
p <- p + ggplot2::facet_grid("parts ~ .", scales="free_y", switch="y")
p <- p + ggplot2::geom_hline(ggplot2::aes_(yintercept = ~y), data=data.frame(y = 1, parts = cn[4]))
# Rangebars
if(is.null(range.bars)){
range.bars <- object$spc$transform$`function`=="none"
}
if(range.bars){
yranges <- vapply(split(data$y, data$parts), function(x) range(x, na.rm = TRUE), numeric(2))
xranges <- range(data$datetime)
barmid <- apply(yranges, 2, mean)
barlength <- min(apply(yranges, 2, diff))
barwidth <- (1/64)*diff(xranges)
barpos <- data.frame(left = xranges[2]+barwidth, right = xranges[2]+barwidth*2,
top = barmid+barlength/2, bottom = barmid-barlength/2,
parts = colnames(yranges), datetime = xranges[2], y = barmid)
p <- p + ggplot2::geom_rect(ggplot2::aes_(xmin = ~left, xmax = ~right, ymax = ~top, ymin = ~bottom), data=barpos, fill="gray75", colour="black", size=1/3)
}
# Add axis labels
p <- p + ggAddExtras(xlab="Time", ylab="")
# Make x axis contain only whole numbers (e.g., years)
p <- p + ggplot2::scale_x_continuous(breaks=unique(round(pretty(data$datetime))))
return(p)
}
}
autolayer.mts <- function(object, colour=TRUE, series=NULL, ...){
if (!requireNamespace("ggplot2", quietly = TRUE)) {
stop("ggplot2 is needed for this function to work. Install it via install.packages(\"ggplot2\")", call. = FALSE)
}
else{
cl <- match.call()
cl[[1]] <- quote(autolayer)
cl$object <- quote(object[,i])
if(length(series)!=NCOL(object)){
if(colour){
message("For a multivariate timeseries, specify a seriesname for each timeseries. Defaulting to column names.")
}
series <- colnames(object)
}
out <- list()
for(i in 1:NCOL(object)){
cl$series <- series[i]
out[[i]] <- eval(cl)
}
return(out)
}
}
autolayer.ts <- function(object, colour=TRUE, series=NULL, ...){
if (!requireNamespace("ggplot2", quietly = TRUE)) {
stop("ggplot2 is needed for this function to work. Install it via install.packages(\"ggplot2\")", call. = FALSE)
}
else{
tsdata <- data.frame(timeVal = as.numeric(time(object)),
series = ifelse(is.null(series), deparse(substitute(object)), series),
seriesVal = as.numeric(object))
if(colour){
ggplot2::geom_line(ggplot2::aes_(x=~timeVal, y=~seriesVal, group=~series, colour=~series), data=tsdata, ...)
}
else{
ggplot2::geom_line(ggplot2::aes_(x=~timeVal, y=~seriesVal, group=~series), data=tsdata, ...)
}
}
}
autolayer.forecast <- function(object, series = NULL, PI = TRUE, showgap = TRUE, ...){
PI <- PI & !is.null(object$level)
data <- fortify(object, PI=PI, showgap=showgap)
mapping <- ggplot2::aes_(x = ~x, y = ~y)
if(!is.null(object$series)){
data[["series"]] <- object$series
}
if(!is.null(series)){
data[["series"]] <- series
mapping$colour <- quote(series)
}
if(PI){
mapping$level <- quote(level)
mapping$ymin <- quote(ymin)
mapping$ymax <- quote(ymax)
}
geom_forecast(mapping=mapping, data=data, stat="identity", ...)
}
autolayer.mforecast <- function(object, series = NULL, PI = TRUE, ...){
cl <- match.call()
cl[[1]] <- quote(autolayer)
cl$object <- quote(object$forecast[[i]])
if(!is.null(series)){
if(length(series)!=length(object$forecast)){
series <- names(object$forecast)
}
}
out <- list()
for(i in 1:length(object$forecast)){
cl$series <- series[i]
out[[i]] <- eval(cl)
}
return(out)
}
autoplot.ts <- function(object, series=NULL, ...){
if (!requireNamespace("ggplot2", quietly = TRUE)) {
stop("ggplot2 is needed for this function to work. Install it via install.packages(\"ggplot2\")", call. = FALSE)
}
else{
if(!is.ts(object)){
stop("autoplot.ts requires a ts object, use object=object")
}
# Create data frame with time as a column labelled x
# and time series as a column labelled y.
data <- data.frame(y = as.numeric(object), x = as.numeric(time(object)))
if(!is.null(series)){
data <- transform(data, series=series)
}
#Initialise ggplot object
p <- ggplot2::ggplot(ggplot2::aes_(y=~y, x=~x), data=data)
#Add data
if(!is.null(series)){
p <- p + ggplot2::geom_line(ggplot2::aes_(group=~series, colour=~series), na.rm = TRUE)
}
else{
p <- p + ggplot2::geom_line(na.rm = TRUE)
}
# Add labels
p <- p + ggAddExtras(xlab="Time", ylab=deparse(substitute(object)))
# Make x axis contain only whole numbers (e.g., years)
p <- p + ggplot2::scale_x_continuous(breaks=ggtsbreaks)
return(p)
}
}
autoplot.mts <- function(object, colour=TRUE, facets=FALSE, ...){
if (!requireNamespace("ggplot2", quietly = TRUE)) {
stop("ggplot2 is needed for this function to work. Install it via install.packages(\"ggplot2\")", call. = FALSE)
}
else{
if(!stats::is.mts(object)){
stop("autoplot.mts requires a mts object, use x=object")
}
data <- data.frame(y=as.numeric(c(object)), x=rep(as.numeric(time(object)),NCOL(object)),
series=factor(rep(colnames(object), each=NROW(object)), levels=colnames(object)))
#Initialise ggplot object
mapping <- ggplot2::aes_(y=~y, x=~x, group=~series)
if (colour & (!facets | !missing(colour))){
mapping$colour <- quote(series)
}
p <- ggplot2::ggplot(mapping, data=data)
p <- p + ggplot2::geom_line(na.rm = TRUE)
if(facets){
p <- p + ggplot2::facet_grid(series~., scales = "free_y")
}
p <- p + ggAddExtras(xlab="Time", ylab=deparse(substitute(object)))
return(p)
}
}
fortify.ts <- function(model, data, ...)
{
# Use ggfortify version if it is loaded
# to prevent cran errors
if(exists("ggfreqplot"))
{
tsp <- attr(model, which = "tsp")
dtindex <- time(model)
if (any(tsp[3] == c(4, 12)))
dtindex <- zoo::as.Date.yearmon(dtindex)
model <- data.frame(Index = dtindex, Data = as.numeric(model))
return(ggplot2::fortify(model))
}
else
{
model <- cbind(x = as.numeric(time(model)), y = as.numeric(model))
as.data.frame(model)
}
}
fortify.forecast <- function(model, data=as.data.frame(model), PI=TRUE, showgap=TRUE, ...){
# Time series forecasts
if (is.element("ts",class(model$mean))){
xVals <- as.numeric(time(model$mean)) # x axis is time
}
# Cross-sectional forecasts
else if (!is.null(model[["newdata"]])){
xVals <- as.numeric(model[["newdata"]][,1]) # Only display the first column of newdata, should be generalised.
if(NCOL(model[["newdata"]]) > 1){
message("Note: only extracting first column of data")
}
}
else {
stop("Could not find forecast x axis")
}
Hiloc <- grep("Hi ", names(data))
Loloc <- grep("Lo ", names(data))
if(PI & !is.null(model$level)){ # PI
if(length(Hiloc)==length(Loloc)){
if(length(Hiloc)>0){
out <- data.frame(x=rep(xVals, length(Hiloc)+1),
y=c(rep(NA,NROW(data)*(length(Hiloc))),data[,1]),
level=c(as.numeric(rep(gsub("Hi ","",names(data)[Hiloc]), each=NROW(data))), rep(NA,NROW(data))),
ymax=c(unlist(data[,Hiloc]),rep(NA,NROW(data))), ymin=c(unlist(data[,Loloc]),rep(NA,NROW(data))))
numInterval <- length(model$level)
}
}
else{
warning("missing intervals detected, plotting point predictions only")
PI <- FALSE
}
}
if(!PI){ # No PI
out <- data.frame(x=xVals, y=as.numeric(model$mean), level=rep(NA,NROW(model$mean)), ymax=rep(NA,NROW(model$mean)), ymin=rep(NA,NROW(model$mean)))
numInterval <- 0
}
if(!showgap){
if(is.null(model$x)){
warning("Removing the gap requires historical data, provide this via model$x. Defaulting showgap to TRUE.")
}
else{
intervalGap <- data.frame(x=rep(time(model$x)[length(model$x)], numInterval +1),
y=c(model$x[length(model$x)], rep(NA, numInterval)),
level=c(NA, model$level)[seq_along(1:(numInterval+1))],
ymax=c(NA, rep(model$x[length(model$x)], numInterval)),
ymin=c(NA, rep(model$x[length(model$x)], numInterval)))
out <- rbind(intervalGap, out)
}
}
return(out)
}
StatForecast <- ggplot2::ggproto("StatForecast", ggplot2::Stat,
required_aes = c("x","y"),
compute_group = function(data, scales, params, PI=TRUE, showgap=TRUE, series=NULL,
h=NULL, level=c(80,95), fan=FALSE, robust=FALSE, lambda=NULL,
find.frequency=FALSE, allow.multiplicative.trend=FALSE, ...) {
## TODO: Rewrite
tspx <- recoverTSP(data$x)
if(is.null(h)){
h <- ifelse(tspx[3] > 1, 2 * tspx[3], 10)
}
tsdat <- ts(data = data$y, start = tspx[1], frequency = tspx[3])
fcast <- forecast(tsdat, h=h, level=level, fan=fan, robust=robust,
lambda=lambda, find.frequency=find.frequency,
allow.multiplicative.trend=allow.multiplicative.trend)
fcast <- ggplot2::fortify(fcast, PI=PI, showgap=showgap)
# Add ggplot & series information
extraInfo <- as.list(data[1,!colnames(data)%in%colnames(fcast)])
extraInfo$`_data` <- quote(fcast)
if(!is.null(series)){
if(data$group[1] > length(series)){
message("Recycling series argument, please provide a series name for each time series")
}
extraInfo[["series"]] <- series[(abs(data$group[1])-1)%%length(series)+1]
}
do.call("transform", extraInfo)
}
)
GeomForecast <- ggplot2::ggproto("GeomForecast", ggplot2::Geom, # Produces both point forecasts and intervals on graph
required_aes = c("x", "y"),
optional_aes = c("ymin", "ymax", "level"),
default_aes = ggplot2::aes(colour = "blue", fill = "grey60", size = .5,
linetype = 1, weight = 1, alpha = 1),
draw_key = function(data, params, size){
lwd <- min(data$size, min(size) / 4)
# Calculate and set colour
linecol <- blendHex(data$col, "gray30", 1)
fillcol <- blendHex(data$col, "#CCCCCC", 0.8)
grid::grobTree(
grid::rectGrob(
width = grid::unit(1, "npc") - grid::unit(lwd, "mm"),
height = grid::unit(1, "npc") - grid::unit(lwd, "mm"),
gp = grid::gpar(
col = fillcol,
fill = scales::alpha(fillcol, data$alpha),
lty = data$linetype,
lwd = lwd * ggplot2::.pt,
linejoin = "mitre")
),
grid::linesGrob(
x=c(0, 0.4, 0.6, 1),
y=c(0.2, 0.6, 0.4, 0.9),
gp = grid::gpar(
col = linecol,
fill = scales::alpha(linecol, data$alpha),
lty = data$linetype,
lwd = lwd * ggplot2::.pt,
linejoin = "mitre")
)
)
},
handle_na = function(self, data, params){ ## TODO: Consider removing/changing
data
},
draw_group = function(data, panel_scales, coord){
data <- split(data, is.na(data$y))
#Draw forecasted points and intervals
if(length(data) == 1){ #PI=FALSE
ggplot2:::ggname("geom_forecast",
GeomForecastPoint$draw_panel(data[[1]], panel_scales, coord))
}
else{ #PI=TRUE
ggplot2:::ggname("geom_forecast",
grid::addGrob(GeomForecastInterval$draw_group(data[[2]], panel_scales, coord),
GeomForecastPoint$draw_panel(data[[1]], panel_scales, coord)))
}
}
)
GeomForecastPoint <- ggplot2::ggproto("GeomForecastPoint", GeomForecast, ## Produces only point forecasts
required_aes = c("x","y"),
setup_data = function(data, params){
data[!is.na(data$y),] # Extract only forecast points
},
draw_group = function(data, panel_scales, coord){
linecol <- blendHex(data$colour[1], "gray30", 1)
# Compute alpha transparency
data$alpha <- grDevices::col2rgb(linecol, alpha = TRUE)[4,]/255 * data$alpha
# Select appropriate Geom and set defaults
if(NROW(data)==0){ #Blank
ggplot2::GeomBlank$draw_panel
}
else if(NROW(data)==1){ #Point
GeomForecastPointGeom <- ggplot2::GeomPoint$draw_panel
pointpred <- transform(data, fill = NA, colour = linecol, size=1, shape=19, stroke=0.5)
}
else{ #Line
GeomForecastPointGeom <- ggplot2::GeomLine$draw_panel
pointpred <- transform(data, fill = NA, colour = linecol)
}
#Draw forecast points
ggplot2:::ggname("geom_forecast_point",
grid::grobTree(GeomForecastPointGeom(pointpred, panel_scales, coord)))
}
)
blendHex <- function(mixcol, seqcol, alpha=1){
requireNamespace("colorspace")
if(is.na(seqcol)){
return(mixcol)
}
#transform to hue/lightness/saturation colorspace
seqcol <- grDevices::col2rgb(seqcol, alpha = TRUE)
mixcol <- grDevices::col2rgb(mixcol, alpha = TRUE)
seqcolHLS <- suppressWarnings(colorspace::coerce(colorspace::RGB(R = seqcol[1,]/255, G = seqcol[2,]/255, B = seqcol[3,]/255), structure(NULL, class="HLS")))
mixcolHLS <- suppressWarnings(colorspace::coerce(colorspace::RGB(R = mixcol[1,]/255, G = mixcol[2,]/255, B = mixcol[3,]/255), structure(NULL, class="HLS")))
#copy luminence
mixcolHLS@coords[, "L"] <- seqcolHLS@coords[, "L"]
mixcolHLS@coords[, "S"] <- alpha*mixcolHLS@coords[, "S"] + (1-alpha)*seqcolHLS@coords[, "S"]
mixcolHex <- suppressWarnings(colorspace::coerce(mixcolHLS, structure(NULL, class="RGB")))
mixcolHex <- colorspace::hex(mixcolHex)
mixcolHex <- ggplot2::alpha(mixcolHex, mixcol[4,]/255)
return(mixcolHex)
}
GeomForecastInterval <- ggplot2::ggproto("GeomForecastInterval", GeomForecast, ## Produces only forecasts intervals on graph
required_aes = c("x","ymin","ymax"),
setup_data = function(data, params){
data[is.na(data$y),] # Extract only forecast intervals
},
draw_group = function(data, panel_scales, coord){
leveldiff <- diff(range(data$level))
if(leveldiff == 0){
leveldiff <- 1
}
shadeVal <- (data$level - min(data$level))/leveldiff * 0.2 + 8/15
data$shadeCol <- rgb(shadeVal, shadeVal, shadeVal)
intervalGrobList <- lapply(split(data, data$level),
FUN = function(x){
# Calculate colour
fillcol <- blendHex(x$colour[1], x$shadeCol[1], 0.7)
# Compute alpha transparency
x$alpha <- grDevices::col2rgb(fillcol, alpha = TRUE)[4,]/255 * x$alpha
# Select appropriate Geom and set defaults
if(NROW(x)==0){ #Blank
ggplot2::GeomBlank$draw_panel
}
else if(NROW(x)==1){ #Linerange
GeomForecastIntervalGeom <- ggplot2::GeomLinerange$draw_panel
x <- transform(x, colour=fillcol, fill = NA, size=1)
}
else{ #Ribbon
GeomForecastIntervalGeom <- ggplot2::GeomRibbon$draw_group
x <- transform(x, colour=NA, fill = fillcol)
}
#Create grob
return(GeomForecastIntervalGeom(x, panel_scales, coord)) ## Create list pair with average ymin/ymax to order layers
}
)
#Draw forecast intervals
ggplot2:::ggname("geom_forecast_interval", do.call(grid::grobTree, rev(intervalGrobList))) #TODO: Find reliable method to stacking them correctly
}
)
geom_forecast <- function(mapping = NULL, data = NULL, stat = "forecast",
position = "identity", na.rm = FALSE, show.legend = NA,
inherit.aes = TRUE, PI=TRUE, showgap=TRUE, series=NULL, ...) {
if(is.forecast(mapping) || is.mforecast(mapping)){
warning("Use autolayer instead of geom_forecast to add a forecast layer to your ggplot object.")
cl <- match.call()
cl[[1]] <- quote(autolayer)
names(cl)[names(cl)=="mapping"] <- "object"
return(eval.parent(cl))
}
if(is.ts(mapping)){
data <- data.frame(y = as.numeric(mapping), x = as.numeric(time(mapping)))
mapping <- ggplot2::aes_(y=~y, x=~x)
}
if(stat=="forecast"){
paramlist <- list(na.rm = na.rm, PI=PI, showgap=showgap, series=series, ...)
if(!is.null(series)){
if(inherits(mapping, "uneval")){
mapping$colour = quote(..series..)
}
else{
mapping <- ggplot2::aes_(colour = ~..series..)
}
}
}
else{
paramlist <- list(na.rm = na.rm, ...)
}
ggplot2::layer(
geom = GeomForecast, mapping = mapping, data = data, stat = stat,
position = position, show.legend = show.legend, inherit.aes = inherit.aes,
params = paramlist)
}
# Produce nice histogram with appropriately chosen bin widths
# Designed to work with time series data without issuing warnings.
gghistogram <- function(x, add.normal=FALSE, add.kde=FALSE, add.rug=TRUE, bins, boundary=0)
{
if (!requireNamespace("ggplot2", quietly = TRUE)) {
stop("ggplot2 is needed for this function to work. Install it via install.packages(\"ggplot2\")", call. = FALSE)
}
else{
if(missing(bins))
bins <- grDevices::nclass.FD(na.omit(x))
data <- data.frame(x=as.numeric(c(x)))
#Initialise ggplot object and plot histogram
binwidth <- (max(x,na.rm=TRUE) - min(x,na.rm=TRUE))/bins
p <- ggplot2::ggplot() +
ggplot2::geom_histogram(ggplot2::aes(x), data=data, binwidth=binwidth, boundary=boundary) +
ggplot2::xlab(deparse(substitute(x)))
# Add normal density estimate
if(add.normal | add.kde)
{
xmin <- min(x, na.rm=TRUE)
xmax <- max(x, na.rm=TRUE)
if(add.kde)
{
h <- stats::bw.SJ(x)
xmin <- xmin - 3*h
xmax <- xmax + 3*h
}
if(add.normal)
{
xmean <- mean(x, na.rm=TRUE)
xsd <- sd(x, na.rm=TRUE)
xmin <- min(xmin, xmean-3*xsd)
xmax <- max(xmax, xmean+3*xsd)
}
xgrid <- seq(xmin, xmax, l=512)
if(add.normal)
{
df <- data.frame(x=xgrid, y=length(x) * binwidth * stats::dnorm(xgrid, xmean, xsd))
p <- p + ggplot2::geom_line(ggplot2::aes(df$x,df$y), col="#ff8a62")
}
if(add.kde)
{
kde <- stats::density(x, bw=h, from=xgrid[1], to=xgrid[512], n=512)
p <- p + ggplot2::geom_line(ggplot2::aes(x=kde$x,y=length(x) * binwidth * kde$y), col='#67a9ff')
}
}
if(add.rug)
{
p <- p + ggplot2::geom_rug(ggplot2::aes(x))
}
return(p)
}
}
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.