如何为fable::TSLM定义nstrend特殊项以预测自然样条趋势?
在tidyverts中为TSLM实现自然样条趋势的预测
当使用tidyverts体系的fable::TSLM构建含自然样条趋势的时间序列预测模型时,直接调用splines::ns()会遇到预测期基矩阵不连续的问题——因为ns()生成的正交基依赖于训练集的时间范围,无法直接扩展到验证/预测集。而splines::predict.ns()可以基于训练时的样条对象生成一致的扩展基矩阵,因此我们可以通过fabletools的自定义special功能,创建类似trend()的nstrend()特殊项来解决这个问题。
实现步骤
1. 定义nstrend的special处理函数
需要分别实现拟合(fit)和预测(forecast)两个阶段的逻辑:
library(fabletools) library(splines) library(fable) library(tsibble) # 拟合阶段:生成自然样条基矩阵并保存样条对象 nstrend_fit <- function(.data, df = 3, ...) { time_var <- index(.data) time_vals <- .data[[time_var]] ns_obj <- ns(time_vals, df = df, ...) structure( list( ns_obj = ns_obj, x = model.matrix(ns_obj), df = df, args = list(...) ), class = "nstrend_spec" ) } # 预测阶段:基于训练时的样条对象生成连续的基矩阵 nstrend_forecast <- function(.spec, new_data, ...) { time_var <- index(new_data) new_time <- new_data[[time_var]] predict(.spec$ns_obj, newx = new_time) } # 注册nstrend为fabletools的special项 register_special( nstrend, fit = nstrend_fit, forecast = nstrend_forecast, args = list(df = integer(1)), mode = "regression" )
2. 使用nstrend()构建并预测模型
用示例数据验证效果:
# 创建示例时间序列数据 set.seed(123) data <- tsibble( time = seq.Date(as.Date("2020-01-01"), as.Date("2023-12-31"), by = "month"), value = 10 + ns(seq_along(time), df = 3) %*% c(2, -1, 3) + rnorm(length(time), 0, 0.5), index = time ) # 拆分训练集与测试集 train <- data %>% filter(time <= as.Date("2023-06-30")) test <- data %>% filter(time > as.Date("2023-06-30")) # 拟合含自然样条趋势的TSLM模型 fit_model <- train %>% model(TSLM(value ~ nstrend(df = 3))) # 生成预测结果 fc <- fit_model %>% forecast(new_data = test) # 查看预测输出 fc
关键说明
- 拟合阶段:
nstrend_fit会生成训练集的自然样条基矩阵,并完整保存ns对象(包含样条节点、边界等核心信息)。 - 预测阶段:
nstrend_forecast调用splines::predict.ns(),基于训练时的ns对象生成预测期的基矩阵,确保样条趋势在训练集和预测集之间的连续性。 - 注册
special时指定mode = "regression",保证TSLM能正确识别并适配这个自定义趋势项。
内容的提问来源于stack exchange,提问作者tom67
相关产品推荐
相关产品推荐

