使用ggplot2和funggcast函数进行预测

4
这个网站上,Davenport先生发布了一个使用ggplot2绘制arima预测的函数,以任意数据集为例,在这里发布。我可以按照他的示例进行操作而没有出现任何错误信息。

但是,当我使用我的数据时,会遇到警告:

1: In window.default(x, ...) : 'end' value not changed
2: In window.default(x, ...) : 'end' value not changed

我知道当我调用这条命令pd <- funggcast(yt, yfor)时会出现问题,这是因为我在指定数据end = c(2013)时出了问题。但我不知道如何解决。

这是我使用的代码:

library(ggplot2)
library(zoo)
library(forecast)

myts <- ts(rnorm(55), start = c(1960), end = c(2013), freq = 1)
funggcast <- function(dn, fcast){

en <- max(time(fcast$mean)) # Extract the max date used in the forecast

# Extract Source and Training Data
ds <- as.data.frame(window(dn, end = en))
names(ds) <- 'observed'
ds$date <- as.Date(time(window(dn, end = en)))

# Extract the Fitted Values (need to figure out how to grab confidence intervals)
dfit <- as.data.frame(fcast$fitted)
dfit$date <- as.Date(time(fcast$fitted))
names(dfit)[1] <- 'fitted'

ds <- merge(ds, dfit, all.x = T) # Merge fitted values with source and training data

# Extract the Forecast values and confidence intervals
dfcastn <- as.data.frame(fcast)
dfcastn$date <- as.Date(as.yearmon(row.names(dfcastn)))
names(dfcastn) <- c('forecast','lo80','hi80','lo95','hi95','date')

pd <- merge(ds, dfcastn,all.x = T) # final data.frame for use in ggplot
return(pd)

} 

yt <- window(myts, end = c(2013)) # extract training data until last year
yfit <- auto.arima(myts) # fit arima model
yfor <- forecast(yfit) # forecast
pd <- funggcast(yt, yfor) # extract the data for ggplot using function funggcast()

ggplot(data = pd, aes(x = date,y = observed)) + geom_line(color = "red") + geom_line(aes(y = fitted), color = "blue") + geom_line(aes(y = forecast)) + geom_ribbon(aes(ymin = lo95, ymax = hi95), alpha = .25) + scale_x_date(name = "Time in Decades") + scale_y_continuous(name = "GDP per capita (current US$)") + theme(axis.text.x = element_text(size = 10), legend.justification=c(0,1), legend.position=c(0,1)) + ggtitle("Arima(0,1,1) Fit and Forecast of GDP per capita for Brazil (1960-2013)") + scale_color_manual(values = c("Blue", "Red"), breaks = c("Fitted", "Data", "Forecast"))

编辑: 我发现另一篇博客在这里有一个与forecastggplot2一起使用的函数,但如果我能找到我的错误,我想要使用上面的方法。 有人可以帮忙吗?

编辑2: 如果我用我的数据在这里运行您更新的代码,那么我会得到下面的图形。请注意,我没有改变mtysend = c(2023),否则它将不会将预测值与拟合值合并。

myts <- ts(WDI_gdp_capita$Brazil, start = c(1960), end = c(2023), freq = 1)

funggcast <- function(dn, fcast){

  en <- max(time(fcast$mean)) # Extract the max date used in the forecast

  # Extract Source and Training Data
  ds <- as.data.frame(window(dn, end = en))
  names(ds) <- 'observed'
  ds$date <- as.Date(time(window(dn, end = en)))

  # Extract the Fitted Values (need to figure out how to grab confidence intervals)
  dfit <- as.data.frame(fcast$fitted)
  dfit$date <- as.Date(time(fcast$fitted))
  names(dfit)[1] <- 'fitted'

  ds <- merge(ds, dfit, all = T) # Merge fitted values with source and training data

  # Extract the Forecast values and confidence intervals
  dfcastn <- as.data.frame(fcast)
  dfcastn$date <- as.Date(paste(row.names(dfcastn),"01","01",sep="-"))
  names(dfcastn) <- c('forecast','lo80','hi80','lo95','hi95','date')

  pd <- merge(ds, dfcastn,all.x = T) # final data.frame for use in ggplot
  return(pd)

} # ggplot function by Frank Davenport

yt <- window(myts, end = c(2013)) # extract training data until last year
yfit <- auto.arima(yt) # fit arima model
yfor <- forecast(yfit) # forecast
pd <- funggcast(myts, yfor) # extract the data for ggplot using function funggcast()

ggplot(data = pd, aes(x = date, y = observed)) + geom_line(color = "red") + geom_line(aes(y = fitted), color = "blue") + geom_line(aes(y = forecast)) + geom_ribbon(aes(ymin = lo95, ymax = hi95), alpha = .25) + scale_x_date(name = "Time in Decades") + scale_y_continuous(name = "GDP per capita (current US$)") + theme(axis.text.x = element_text(size = 10), legend.justification=c(0,1), legend.position=c(0,1)) + ggtitle("Arima(0,1,1) Fit and Forecast of GDP per capita for Brazil (1960-2013)") + scale_color_manual(values = c("Blue", "Red"), breaks = c("Fitted", "Data", "Forecast")) + ggsave((filename = "gdp_forecast_ggplot.pdf"), width=330, height=180, units=c("mm"), dpi = 300, limitsize = TRUE)

我得到的几乎完美的图表: enter image description here 另一个问题:如何在这个图表中添加图例?
如果我为myts设置end=c(2013),我将获得与一开始相同的图表: enter image description here
3个回答

5

在Davenport先生的分析和您正在制作的情节之间有几个不同点。首先,他将ARIMA预测与某些观察数据进行比较,这就是为什么他在整个时间序列的一部分(训练集)上训练模型的原因。

为了做到这一点,您应该让初始时间序列更长:

myts <- ts(rnorm(55), start = c(1960), end = c(2023), freq = 1)

然后在您的脚本末尾,选择训练数据直到2013年:
yt <- window(myts, end = c(2013)) # extract training data until last year

模型应该在训练集上进行训练,而不是整个时间序列上进行训练,因此您需要将yfit行更改为:

yfit <- auto.arima(yt) # fit arima model

使用整个时间序列调用funggcast函数,因为它需要观测和拟合数据:

pd <- funggcast(myts, yfor)

最后,他使用了包含月份和年份的日期,因此在他的funggcast函数中,将这一行更改为:
dfcastn$date <- as.Date(as.yearmon(row.names(dfcastn)))

To:

dfcastn$date <- as.Date(paste(row.names(dfcastn),"01","01",sep="-"))

这是因为模型预测的数值需要转换成日期格式,例如2014年需要转换成2014-01-01的格式,才能与观测数据合并。
做完所有的修改后,代码如下:
library(ggplot2)
library(zoo)
library(forecast)

myts <- ts(rnorm(55), start = c(1960), end = c(2013), freq = 1)
funggcast <- function(dn, fcast){

        en <- max(time(fcast$mean)) # Extract the max date used in the forecast

        # Extract Source and Training Data
        ds <- as.data.frame(window(dn, end = en))
        names(ds) <- 'observed'
        ds$date <- as.Date(time(window(dn, end = en)))

        # Extract the Fitted Values (need to figure out how to grab confidence intervals)
        dfit <- as.data.frame(fcast$fitted)
        dfit$date <- as.Date(time(fcast$fitted))
        names(dfit)[1] <- 'fitted'

        ds <- merge(ds, dfit, all.x = T) # Merge fitted values with source and training data

        # Extract the Forecast values and confidence intervals
        dfcastn <- as.data.frame(fcast)
        dfcastn$date <- as.Date(paste(row.names(dfcastn),"01","01",sep="-"))
        names(dfcastn) <- c('forecast','lo80','hi80','lo95','hi95','date')

        pd <- merge(ds, dfcastn,all= T) # final data.frame for use in ggplot
        return(pd)

} 

yt <- window(myts, end = c(2013)) # extract training data until last year
yfit <- auto.arima(yt) # fit arima model
yfor <- forecast(yfit) # forecast
pd <- funggcast(myts, yfor) # extract the data for ggplot using function funggcast()

plotData<-ggplot(data = pd, aes(x = date, y = observed)) + geom_line(aes(color = "1")) +
        geom_line(aes(y = fitted,color="2")) + 
        geom_line(aes(y = forecast,color="3")) +
        scale_colour_manual(values=c("red", "blue","black"),labels = c("Observed", "Fitted", "Forecasted"),name="Data")+
        geom_ribbon(aes(ymin = lo95, ymax = hi95), alpha = .25)+
        scale_x_date(name = "Time in Decades") +
        scale_y_continuous(name = "GDP per capita (current US$)")+
        theme(axis.text.x = element_text(size = 10)) + 
        ggtitle("Arima(0,1,1) Fit and Forecast of GDP per capita for Brazil (1960-2013)")

plotData

并且您会得到这样的一个图形,拟合结果非常糟糕,是一个完全随机的时间序列。此外,ggplot会输出一些错误信息,因为预测线在2013年之前没有数据,而拟合数据在2013年之后也没有数据。(我运行了几次,取决于最初的随机时间序列,模型可能只会在所有地方预测0) enter image description here 编辑:还更改了pd赋值行,以防在2013年之后没有观察到的数据。
编辑2:我更改了代码末尾的ggplot函数,以确保图例显示出来。

谢谢你的回复,NicE!我有一个后续问题。我的数据实际上不是随机的,由54个观测值组成(从1960年到2013年),见ggtitle。因此,我不能刻意扩展myts。当我这样做时,myts会从开头重复包含的观测值。我该怎么避免这种情况?通过插入“NA”吗?换句话说(如果你看一下你的图表):我如何避免红线超出灰色区域? - Til Hund
1
我已经编辑了pd赋值行和myts描述以适应您的情况,您得到了您发布的警告,因为2013年是训练系列的结束,而您正在预测到2023年,所以en是2023。在最终数据框中,观察值和拟合值在2013年之后会自动添加NAs。 - NicE
1
当然,我在funggcast函数的最后一行进行了更改:pd <- merge(ds, dfcastn,all= T)。将all.x=T更改为all=T是为了确保保留来自两个数据框的所有行。否则,在合并过程中,由于ds数据框中缺少预测数据,因此不会保留任何来自预测数据的行,因为它们不在该数据框中。该数据框保存着直到2013年的拟合和观察到的数据。 - NicE
1
在您发布的Edit2中,将pd <- merge(ds, dfcastn,all.x = T)替换为pd <- merge(ds, dfcastn,all = T)(请参见我的先前评论原因),并且您可以将myts <- ts(rnorm(55), start = c(1960), end = c(2013), freq = 1)保留到end=c(2013)而不是end=c(2023),这应该会生成正确的图形。 - NicE
1
编辑了 ggplot 函数以显示图例,请参见我在初始帖子中的代码。 - NicE
显示剩余3条评论

2

1
这是一个比较旧的帖子,但有一个在GitHub上的函数可以产生一些不错的结果。
以下是2016年8月3日的代码:
function(forec.obj, data.color = 'blue', fit.color = 'red', forec.color = 'black',
                           lower.fill = 'darkgrey', upper.fill = 'grey', format.date = F)
{
    serie.orig = forec.obj$x
    serie.fit = forec.obj$fitted
    pi.strings = paste(forec.obj$level, '%', sep = '')

     if(format.date)
        dates = as.Date(time(serie.orig))
    else
        dates = time(serie.orig)

    serie.df = data.frame(date = dates, serie.orig = serie.orig, serie.fit = serie.fit)

    forec.M = cbind(forec.obj$mean, forec.obj$lower[, 1:2], forec.obj$upper[, 1:2])
    forec.df = as.data.frame(forec.M)
    colnames(forec.df) = c('forec.val', 'l0', 'l1', 'u0', 'u1')

    if(format.date)
        forec.df$date = as.Date(time(forec.obj$mean))
    else
        forec.df$date = time(forec.obj$mean)

    p = ggplot() + 
        geom_line(aes(date, serie.orig, colour = 'data'), data = serie.df) + 
        geom_line(aes(date, serie.fit, colour = 'fit'), data = serie.df) + 
        scale_y_continuous() +
        geom_ribbon(aes(x = date, ymin = l0, ymax = u0, fill = 'lower'), data = forec.df, alpha = I(0.4)) + 
        geom_ribbon(aes(x = date, ymin = l1, ymax = u1, fill = 'upper'), data = forec.df, alpha = I(0.3)) + 
        geom_line(aes(date, forec.val, colour = 'forecast'), data = forec.df) + 
        scale_color_manual('Series', values=c('data' = data.color, 'fit' = fit.color, 'forecast' = forec.color)) + 
        scale_fill_manual('P.I.', values=c('lower' = lower.fill, 'upper' = upper.fill))

    if (format.date)
        p = p + scale_x_date()

    p
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接