R tsibble构建组合预测时mable转tibble无法调用forecast问题
我正在跟随Rob Hyndman博士的R语言时间序列预测教材学习,当前学到第13章“实用预测问题”的组合预测小节,实操时遇到如下问题:
构建组合预测模型时,对已训练模型做线性组合计算的代码会把原本的mable对象强制转换成普通tibble对象;如果移除组合计算的代码行,model()函数会正常返回mable对象,且无法将得到的tibble重新转回mable类型。
教材参考代码
auscafe <- aus_retail %>% filter(stringr::str_detect(Industry, "Takeaway")) %>% summarise(Turnover = sum(Turnover)) train <- auscafe %>% filter(year(Month) <= 2013) STLF <- decomposition_model( STL(log(Turnover) ~ season(window = Inf)), ETS(season_adjust ~ season("N")) ) cafe_models <- train %>% model( ets = ETS(Turnover), stlf = STLF, arima = ARIMA(log(Turnover)) ) %>% mutate(combination = (ets + stlf + arima) / 3) cafe_fc <- cafe_models %>% forecast(h = "5 years")
本地运行同类代码无法得到预期结果,教材示例所用数据集存放在fable、fabletools、feasts、tsibble、tsibbledata其中一个包中。
个人测试数据结构
structure(list(Date = structure(c(19114, 19115, 19116, 19117, 19118, 19119, 19120, 19121, 19122, 19123, 19124, 19125, 19126, 19127, 19128, 19129, 19130, 19131, 19132, 19133, 19134, 19135, 19136, 19137, 19138, 19139, 19140, 19141, 19142, 19143, 19144, 19145, 19146, 19147, 19148, 19149, 19150, 19151, 19152, 19153, 19154, 19155, 19156, 19157, 19158, 19159, 19160, 19161, 19162, 19163, 19164, 19165), class = "Date"), Sales = c(2147350, 1953453, 1930514, 1951737, 2496552, 2091370, 1921364, 2342280, 2224779, 2124766, 2229922, 2501654, 2056751, 1908814, 2109249, 1946929, 2057711, 2001398, 2535514, 2060774, 1793765, 1954603, 2019082, 2077929, 2152838, 2802181, 2314866, 2268680, 2380746, 1887751, 2201204, 2004422, 2783170, 2238542, 2000024, 1777258, 1844045, 2138638, 2387784, 2783170, 1988945, 1749007, 2128774, 2101340, 2122877, 2085712, 2532569, 1995143, 1713529, 2045398, 1781901, 2164901)), class = c("tbl_ts", "tbl_df", "tbl", "data.frame"), row.names = c(NA, -52L), key = structure(list(.rows = structure(list(1:52), ptype = integer(0), class = c("vctrs_list_of", "vctrs_vctr", "list"))), class = c("tbl_df", "tbl", "data.frame" ), row.names = c(NA, -1L)), index = structure("Date", ordered = TRUE), index2 = "Date", interval = structure(list( year = 0, quarter = 0, month = 0, week = 0, day = 1, hour = 0, minute = 0, second = 0, millisecond = 0, microsecond = 0, nanosecond = 0, unit = 0), .regular = TRUE, class = c("interval", "vctrs_rcrd", "vctrs_vctr")))
个人编写代码
library(tsibble) library(tsibbledata) library(fable) library(fabletools) library(feasts) my_dcmp_spec <- decomposition_model( STL(Sales), ETS(season_adjust ~ season("N")) ) fit <- time_series_sample %>% model( stl_ets = my_dcmp_spec, `Seasonal naïve` = SNAIVE(Sales), holt_winters = ETS(Sales ~ error("A") + trend("N") + season("N")) ) %>% mutate(combination = (stl_ets + `Seasonal naïve` + holt_winters) / 3)
运行预测步骤代码时报错:
fc <- fit %>% forecast(h = 21)
报错信息:
Error in UseMethod("forecast") : no applicable method for 'forecast' applied to an object of class "c('tbl_df', 'tbl', 'data.frame')"
尝试用as_mable()强制转换类型时也报错:
fit %>% as_mable()
报错信息:
Error in `build_mable()`: ! A mable must contain at least one model. Backtrace: 1. fit %>% as_mable() 3. fabletools:::as_mable.data.frame(.) 4. fabletools:::build_mable(x, key = !!enquo(key), model = !!enquo(model))
实际tibble中确实包含模型对象,对应单元格存储的是S3类型lst_model对象,尝试结合pivot_longer()与as_mable()转换也无法解决问题。只要代码中包含mutate()生成组合模型的步骤,最终得到的就是普通tibble而非mable。
移除组合模型生成步骤时可以正常运行预测和绘图:
fit <- time_series_sample %>% model( stl_ets = my_dcmp_spec, seasonal_naive = SNAIVE(Sales), holt_winters = ETS(Sales ~ error("A") + trend("N") + season("N")) ) fc <- fit %>% forecast(h = 21) fc %>% autoplot(time_series_sample)
但该结果缺少需要的组合预测输出,不确定是否为包的bug:mutate步骤确实成功生成了模型的线性组合(单元格为lst_model类型),但mable被转为tibble后无法调用forecast方法完成预测。
这个问题是旧版本fabletools包的已知bug,mutate()新增组合模型列时没有正确保留mable类属性,导致后续方法调用失败,修复方法如下:
- 先把fable相关的包全部更新到CRAN最新版本,直接在控制台运行更新命令即可:
install.packages(c("fable", "fabletools", "tsibble", "feasts", "tsibbledata")) - 更新完成后重启R会话,重新运行原有代码,
mutate()新增组合模型后会正常保留mable类型,可直接调用forecast()方法,不需要额外做类型转换。 - 如果暂时无法更新包,可以用临时绕过方案:不要在
model()后链式调用mutate()加组合模型,而是把组合模型直接写在model()函数的参数里,这样从一开始就会正确生成mable对象:
注意这个写法要求所有被组合的模型都在同一个fit <- time_series_sample %>% model( stl_ets = my_dcmp_spec, `Seasonal naïve` = SNAIVE(Sales), holt_winters = ETS(Sales ~ error("A") + trend("N") + season("N")), combination = (stl_ets + `Seasonal naïve` + holt_winters) / 3 )model()调用里定义,顺序不能错,组合列要放在被引用的模型列后面。
不要尝试手动用
as_mable()转换被转成tibble的对象,旧版本中这个方法无法自动识别新增的模型列,手动指定模型列参数的写法非常繁琐,远不如上面两种方案高效。
内容的提问来源于stack exchange,提问作者hachiko

