将年度时间序列预测函数适配为月度版本的技术问题
问题背景
现有可用于年度时间序列预测的R语言函数,需修改为支持月度时间序列预测的版本。
现有年度预测函数代码
library(forecast) library(zoo) set.seed(289805) y <- arima.sim(n = 10, model = list(ar = 0.8, order = c(1, 0, 0)), sd = 1) y <- ts(y, frequency = 1, start = c(1981)) #-- 提取训练数据、拟合模型并生成预测 yt <- window(y, end = 1990) #yfit <- Arima(yt,order = c(3, 1, 4)) yfit <- forecast::auto.arima(yt) yfor <- forecast(yfit) #--- 提取ggplot可用数据的函数funggcast() funggcast <- function(dn, fcast){ en <- max(time(fcast$mean)) # 提取预测用到的最大日期 # 提取源数据和训练数据 ds <- as.data.frame(window(dn, end = en)) names(ds) <- 'observed' ds$Year <- as.Date(time(window(dn, end = en))) # 提取拟合值(需解决置信区间提取问题) dfit <- as.data.frame(fcast$fitted) dfit$Year <- as.Date(time(fcast$fitted)) names(dfit)[1] <- 'fitted' ds <- merge(ds, dfit, all.x = T) # 将拟合值与源数据、训练数据合并 # 提取预测值和置信区间 dfcastn <- as.data.frame(fcast) dfcastn$Year <- as.Date(paste(row.names(dfcastn), "01", "01", sep = "-")) names(dfcastn) <- c('forecast', 'lo80', 'hi80', 'lo95', 'hi95', 'Year') pd <- merge(ds, dfcastn, all = T) # 生成ggplot可用的最终数据框 return(pd) } pd <- funggcast(y ,yfor) pd
尝试的月度预测修改代码
library(forecast) library(zoo) set.seed(289805) y <- arima.sim(n = 10, model = list(ar = 0.8, order = c(1, 0, 0)), sd = 1) y <- ts(y, frequency = 12, start = c(1981, 1)) #-- 提取训练数据、拟合模型并生成预测 yt <- window(y, end = c(1981, 10)) #yfit <- Arima(yt,order = c(3, 1, 4)) yfit <- forecast::auto.arima(yt) yfor <- forecast(yfit) #--- 提取ggplot可用数据的函数funggcast() funggcast <- function(dn, fcast){ en <- max(time(fcast$mean)) # 提取预测用到的最大日期 # 提取源数据和训练数据 ds <- as.data.frame(window(dn, end = en)) names(ds) <- 'observed' ds$Year <- as.Date(time(window(dn, end = en))) # 提取拟合值(需解决置信区间提取问题) dfit <- as.data.frame(fcast$fitted) dfit$Year <- as.Date(time(fcast$fitted)) names(dfit)[1] <- 'fitted' ds <- merge(ds, dfit, all.x = T) # 将拟合值与源数据、训练数据合并 # 提取预测值和置信区间 dfcastn <- as.data.frame(fcast) dfcastn$Year <- as.Date(paste(row.names(dfcastn), "01", "01", sep = "-")) names(dfcastn) <- c('forecast', 'lo80', 'hi80', 'lo95', 'hi95', 'Year') pd <- merge(ds, dfcastn, all = T) # 生成ggplot可用的最终数据框 return(pd) } pd <- funggcast(y ,yfor) pd
报错信息
Error in charToDate(x) : character string is not in a standard unambiguous format In addition: Warning messages: 1: In window.default(x, ...) : 'end' value not changed 2: In window.default(x, ...) : 'end' value not changed
预期需求
需将输出数据框中的Year列替换为Year/Month列,生成包含1981年1月至1982年8月的月度数据,包含观测值、拟合值、预测值及对应置信区间,格式示例如下:
# Year/Month observed fitted forecast lo80 hi80 lo95 hi95 #1 1981-01-01 -0.2505125 -0.1002050 NA NA NA NA NA #2 1981-02-01 0.8855338 -0.2004100 NA NA NA NA NA #...(省略后续行)
解决方案
报错核心原因是月度时间序列的time()返回值为带小数的数值(如1981.083代表1981年2月),直接转换为Date类型会失败,同时window函数对月度序列使用数值型end参数会触发警告。以下是修正后的完整代码:
library(forecast) library(zoo) set.seed(289805) # 生成10个月度观测值,后续预测扩展至1982年8月(共20个时间点) y <- arima.sim(n = 10, model = list(ar = 0.8, order = c(1, 0, 0)), sd = 1) y <- ts(y, frequency = 12, start = c(1981, 1)) # 提取训练数据(1981年1-10月) yt <- window(y, end = c(1981, 10)) # 自动拟合ARIMA模型,预测10个月(覆盖至1982年8月) yfit <- forecast::auto.arima(yt) yfor <- forecast(yfit, h = 10) # 修正后的funggcast函数,支持月度时间序列 funggcast <- function(dn, fcast){ # 将时间转换为yearmon格式,适配月度序列 en <- max(as.yearmon(time(fcast$mean))) # 提取源数据和训练数据 ds <- as.data.frame(window(dn, end = en)) names(ds) <- 'observed' # 转换为YYYY-MM-01格式的日期,作为Year/Month列 ds$`Year/Month` <- as.Date(as.yearmon(time(window(dn, end = en)))) # 提取拟合值 dfit <- as.data.frame(fcast$fitted) dfit$`Year/Month` <- as.Date(as.yearmon(time(fcast$fitted))) names(dfit)[1] <- 'fitted' # 合并拟合值与源数据 ds <- merge(ds, dfit, all.x = TRUE) # 提取预测值和置信区间 dfcastn <- as.data.frame(fcast) # 处理预测结果的行名,转换为标准日期格式 dfcastn$`Year/Month` <- as.Date(as.yearmon(row.names(dfcastn))) names(dfcastn) <- c('forecast', 'lo80', 'hi80', 'lo95', 'hi95', 'Year/Month') # 合并所有数据并按时间排序 pd <- merge(ds, dfcastn, all = TRUE) pd <- pd[order(pd$`Year/Month`), ] rownames(pd) <- NULL return(pd) } # 生成最终结果 pd <- funggcast(y ,yfor) print(pd)
关键修正说明
- 时间格式适配:用
zoo包的as.yearmon()将数值型时间转换为年月格式,再转为Date类型(自动设为每月1号),解决日期转换报错问题。 - window参数优化:使用
yearmon类型的end参数,消除window函数的警告。 - 预测期数控制:通过
h=10指定预测10个月,确保输出覆盖至1982年8月。 - 列名替换:将原
Year列替换为Year/Month,完全匹配需求格式。
内容的提问来源于stack exchange,提问作者Daniel James
相关产品推荐
相关产品推荐

