拉平或去除季节性时间序列的趋势
Flatten or detrend a seasonal time series
我有一个具有季节性(每周)模式的重复时间序列,我想 return 没有每周趋势的相同时间序列,以第一个值作为起始值点.
具体来说,第 1 个值仍然是 39.8,但第 8 个值也将是 39.8 而不是 17.1。如果只是重复前七个值,那么将重复一周的负趋势,我希望根本没有趋势(因此 6.2 的第 7 个值也会更高)。
有没有一种优雅的方法来做到这一点,尤其是对时间序列中的零值条目具有鲁棒性的方法(我有很多)?
我们可以假设时间序列趋势是线性且恒定的(即不仅仅是分段线性)。
demand <- ts(
c(39.8, 33.5, 40.6, 23.6, 11.9, 12.3, 6.2, 17.1, 10.8, 18, 1, -10.7,
-10.4, -16.5, -5.6, -11.9, -4.7, -21.7, -33.4, -33.1, -39.2, -28.2,
-34.6, -27.4, -44.4, -56.1, -55.7, -61.8, -50.9, -57.2, -50.1),
frequency = 7
)
plot(demand)
你可以这样做:
trend = stl(demand, s.window = "periodic")$time.series[,2]
detrend_ts = demand - (trend - trend[1])
plot(detrend_ts)
结果:
> detrend_ts
Time Series:
Start = c(1, 1)
End = c(5, 3)
Frequency = 7
[1] 39.80000 36.72792 47.05584 33.28224 24.80864 28.43514 25.56165 39.69889 36.63614
[10] 47.08241 33.32868 24.86478 28.40088 25.53956 39.67825 36.63383 47.08942 33.32204
[19] 24.85466 28.38747 25.52029 39.76777 36.61526 47.05556 33.29586 24.82129 28.44673
[28] 25.57045 39.69417 36.61948 46.94480
备注:
基本上,我使用STL分解(Loess的Seasonal Decomposition of Time Series)来估计趋势,然后从demand
中减去它。由于您希望去除趋势的时间序列从 39.8
开始,我还从 trend
中减去 trend
的第一个值。
由于趋势是线性的,您也可以使用线性拟合
linear_fit <- lm(demand ~ time(demand))
plot(demand - linear_fit$fitted.values + linear_fit$fitted.values[1],
ylab = "detrended demand", typ = 'o')
函数decompose
也有助于获取趋势。由于它应用了中心移动平均线,趋势在系列开始时将有 3 个 NA,在结束时有 3 个。
demand_decomposed <- decompose(demand)
plot(demand - demand_decomposed$trend, ylab = 'detrended demand', typ = 'o')
注意可以直接使用filter
函数计算趋势得到decompose
函数使用的移动平均线
trend_ma <- filter(demand, rep(1/7, 7), method = "convolution", sides = 2)
plot(demand - trend_ma, typ = 'o', ylab = "detrended demand")
最后两个图没有调整到时间 1 的趋势值,因为它不存在。可以根据季节性模式将 NA 替换为它应该具有的值:
seasonal <- demand - trend_ma
na_values_time_in_period <- cycle(demand)[is.na(seasonal)]
value_time_in_period <- tapply(seasonal, cycle(demand), mean, na.rm = TRUE)
seasonal[is.na(seasonal)] <- value_time_in_period[na_values_time_in_period]
我有一个具有季节性(每周)模式的重复时间序列,我想 return 没有每周趋势的相同时间序列,以第一个值作为起始值点.
具体来说,第 1 个值仍然是 39.8,但第 8 个值也将是 39.8 而不是 17.1。如果只是重复前七个值,那么将重复一周的负趋势,我希望根本没有趋势(因此 6.2 的第 7 个值也会更高)。
有没有一种优雅的方法来做到这一点,尤其是对时间序列中的零值条目具有鲁棒性的方法(我有很多)?
我们可以假设时间序列趋势是线性且恒定的(即不仅仅是分段线性)。
demand <- ts(
c(39.8, 33.5, 40.6, 23.6, 11.9, 12.3, 6.2, 17.1, 10.8, 18, 1, -10.7,
-10.4, -16.5, -5.6, -11.9, -4.7, -21.7, -33.4, -33.1, -39.2, -28.2,
-34.6, -27.4, -44.4, -56.1, -55.7, -61.8, -50.9, -57.2, -50.1),
frequency = 7
)
plot(demand)
你可以这样做:
trend = stl(demand, s.window = "periodic")$time.series[,2]
detrend_ts = demand - (trend - trend[1])
plot(detrend_ts)
结果:
> detrend_ts
Time Series:
Start = c(1, 1)
End = c(5, 3)
Frequency = 7
[1] 39.80000 36.72792 47.05584 33.28224 24.80864 28.43514 25.56165 39.69889 36.63614
[10] 47.08241 33.32868 24.86478 28.40088 25.53956 39.67825 36.63383 47.08942 33.32204
[19] 24.85466 28.38747 25.52029 39.76777 36.61526 47.05556 33.29586 24.82129 28.44673
[28] 25.57045 39.69417 36.61948 46.94480
备注:
基本上,我使用STL分解(Loess的Seasonal Decomposition of Time Series)来估计趋势,然后从demand
中减去它。由于您希望去除趋势的时间序列从 39.8
开始,我还从 trend
中减去 trend
的第一个值。
由于趋势是线性的,您也可以使用线性拟合
linear_fit <- lm(demand ~ time(demand))
plot(demand - linear_fit$fitted.values + linear_fit$fitted.values[1],
ylab = "detrended demand", typ = 'o')
函数decompose
也有助于获取趋势。由于它应用了中心移动平均线,趋势在系列开始时将有 3 个 NA,在结束时有 3 个。
demand_decomposed <- decompose(demand)
plot(demand - demand_decomposed$trend, ylab = 'detrended demand', typ = 'o')
注意可以直接使用filter
函数计算趋势得到decompose
函数使用的移动平均线
trend_ma <- filter(demand, rep(1/7, 7), method = "convolution", sides = 2)
plot(demand - trend_ma, typ = 'o', ylab = "detrended demand")
最后两个图没有调整到时间 1 的趋势值,因为它不存在。可以根据季节性模式将 NA 替换为它应该具有的值:
seasonal <- demand - trend_ma
na_values_time_in_period <- cycle(demand)[is.na(seasonal)]
value_time_in_period <- tapply(seasonal, cycle(demand), mean, na.rm = TRUE)
seasonal[is.na(seasonal)] <- value_time_in_period[na_values_time_in_period]