You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在R中选择合适的sin()项拟合含多周期的时间序列

如何在R中自动估计时间序列的多个周期并拟合正弦函数

我想用sin()函数拟合带有周期性(波峰和波谷)的时间序列,但目前只能通过猜测周期(如1个月、2个月、1年、2年等)的方式尝试。请问R中是否存在可以估计时间序列中多个周期的函数?

以下是我手动猜测周期后拟合的示例代码(下图红线为拟合结果),如何找到具有合适周期的sin()项?

t <- 1:365
y <- c(-1,-1.3,-1.6,-1.8,-2.1,-2.3,-2.5,-2.7,-2.9,-3,-2,-1.1,-0.3,0.5,1.1,1.6,2.1,2.5,2.8,3.1,3.4,3.7,4.2,4.6,5,5.3,5.7,5.9,6.2,5.8,5.4,5,4.6,4.2,3.9,3.6,3.4,3.1,2.9,2.8,2.6,2.5,2.3,1.9,1.5,1.1,0.8,0.5,0.2,0,-0.1,-0.3,-0.4,-0.5,-0.5,-0.6,-0.7,-0.8,-0.9,-0.8,-0.6,-0.3,-0.1,0.1,0.4,0.6,0.9,1.1,1.3,1.5,1.7,2.1,2.4,2.7,3,3.3,3.5,3.8,4.3,4.7,5.1,5.5,5.9,6.2,6.4,6.6,6.7,6.8,6.8,6.9,7,6.9,6.8,6.7,
       6.5,6.4,6.4,6.3,6.2,6,5.9,5.7,5.6,5.5,5.4,5.4,5.1,4.9,4.8,4.6,4.5,4.4,4.3,3.9,3.6,3.3,3,2.8,2.6,2.4,2.6,2.5,2.4,2.3,2.3,2.2,2.2,2.3,2.4,2.4,2.5,2.5,2.6,2.6,2.4,2.1,1.9,1.8,1.6,1.4,1.3,1,0.7,0.5,0.2,0,-0.2,-0.4,-0.2,-0.1,0.1,0.1,0.1,0.1,0.1,0.1,0,0,-0.1,-0.1,-0.2,-0.2,-0.3,-0.3,-0.4,-0.5,-0.5,-0.6,-0.7,-0.7,-0.8,-0.8,-0.8,-0.9,-0.9,-0.9,-1.3,-1.6,-1.9,-2.1,-2.3,-2.6,-2.9,-2.9,-2.9,-2.9,
       -2.9,-3,-3,-3,-2.8,-2.7,-2.5,-2.4,-2.3,-2.2,-2.1,-2,-2,-1.9,-1.9,-1.8,-1.8,-1.8,-1.9,-1.9,-2,-2.1,-2.2,-2.2,-2.3,-2.4,-2.5,-2.6,-2.7,-2.8,-2.9,-2.9,-2.9,-2.9,-2.9,-2.9,-2.9,-2.9,-2.9,-2.9,-2.8,-2.8,-2.7,-2.7,-2.6,-2.6,-2.8,-3,-3.1,-3.3,-3.4,-3.5,-3.6,-3.5,-3.4,-3.3,-3.3,-3.2,-3,-2.9,-2.8,-2.8,-2.7,-2.6,-2.6,-2.6,-2.5,-2.6,-2.7,-2.8,-2.8,-2.9,-3,-3,-3,-3,-2.9,-2.9,-2.9,-2.9,-2.9,-2.8,
       -2.7,-2.6,-2.5,-2.4,-2.3,-2.3,-2.1,-1.9,-1.8,-1.7,-1.5,-1.4,-1.3,-1.5,-1.7,-1.8,-1.9,-2,-2.1,-2.2,-2.4,-2.5,-2.6,-2.7,-2.8,-2.8,-2.9,-3.1,-3.2,-3.3,-3.4,-3.5,-3.5,-3.6,-3.6,-3.5,-3.4,-3.3,-3.2,-3.1,-3,-2.7,-2.3,-2,-1.8,-1.5,-1.3,-1.1,-0.9,-0.7,-0.6,-0.5,-0.3,-0.2,-0.1,-0.3,-0.5,-0.6,-0.7,-0.8,-0.9,-1,-1.1,-1.1,-1.2,-1.2,-1.2,-1.2,-1.2,-0.8,-0.4,-0.1,0.2,0.5,0.8,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0.6,0.3,0,-0.2,-0.5,-0.7,-0.8)

dt <- data.frame(t = t, y = y)
plot(x = dt$t, y = dt$y)

lm <- lm(y ~ sin(2*3.1416/365*t)+cos(2*3.1416/365*t)+
           sin(2*2*3.1416/365*t)+cos(2*2*3.1416/365*t)+
           sin(2*4*3.1416/365*t)+cos(2*4*3.1416/365*t)+
           sin(2*5*3.1416/365*t)+cos(2*5*3.1416/365*t)+
           sin(2*6*3.1416/365*t)+cos(2*6*3.1416/365*t)+
           sin(2*0.5*3.1416/365*t)+cos(2*0.5*3.1416/365*t),
         data = dt)

summary(lm)$adj.r.squared
plot(dt$y); lines(predict(lm), type = "l", col = "red")

时间序列拟合结果


解决方案

方法1:用傅里叶变换(FFT)识别显著周期

傅里叶变换能将时间序列转换到频域,定位能量最高的频率(对应周期),再用这些频率构建拟合模型,无需手动猜测。

# 计算傅里叶变换
fft_result <- fft(dt$y)
# 计算功率谱(振幅的平方,代表能量)
power <- Mod(fft_result)^2
# 生成频率序列,仅保留前半部分(频域结果对称)
freq <- seq_along(power)/length(power)
power_half <- power[2:floor(length(power)/2)]
freq_half <- freq[2:floor(length(power)/2)]

# 提取功率最高的前5个频率,转换为周期
top_freqs <- freq_half[order(power_half, decreasing = TRUE)[1:5]]
top_periods <- 1/top_freqs
cat("识别出的主要周期:", round(top_periods, 2), "\n")

# 用这些周期构建拟合公式
formula_terms <- lapply(top_freqs, function(f) {
  paste0("sin(2*pi*", f, "*t) + cos(2*pi*", f, "*t)")
})
formula_str <- paste("y ~", paste(unlist(formula_terms), collapse = " + "))
lm_fft <- lm(as.formula(formula_str), data = dt)

# 查看拟合效果
cat("调整后R平方:", round(summary(lm_fft)$adj.r.squared, 4), "\n")
plot(dt$y); lines(predict(lm_fft), type = "l", col = "blue")

方法2:使用forecast包自动生成傅里叶项

forecast包的fourier()函数可以快速生成指定数量的傅里叶项,结合线性模型拟合,大幅简化流程。

library(forecast)

# 生成10个傅里叶项(K值可根据拟合效果调整)
fourier_terms <- fourier(dt$t, K = 10)
dt_fourier <- cbind(dt, fourier_terms)

# 拟合模型
lm_fourier <- lm(y ~ ., data = dt_fourier)

# 查看结果
cat("调整后R平方:", round(summary(lm_fourier)$adj.r.squared, 4), "\n")
plot(dt$y); lines(predict(lm_fourier), type = "l", col = "green")

方法3:用非线性最小二乘(nls)直接估计周期参数

如果需要直接得到周期的具体估计值,可以用nls()构建非线性模型,让算法自动优化周期、振幅和相位参数。

# 定义多周期拟合模型
model_formula <- y ~ a0 + 
  a1*sin(2*pi*t/p1 + phi1) + 
  a2*sin(2*pi*t/p2 + phi2) +
  a3*sin(2*pi*t/p3 + phi3)

# 设置初始参数(只需大致范围,无需精确)
start_params <- list(a0 = mean(dt$y), 
                     a1 = 5, p1 = 365, phi1 = 0,
                     a2 = 2, p2 = 180, phi2 = 0,
                     a3 = 1, p3 = 90, phi3 = 0)

# 拟合模型
nls_fit <- nls(model_formula, data = dt, start = start_params)

# 查看估计的周期参数
summary(nls_fit)
# 绘制拟合曲线
plot(dt$y); lines(predict(nls_fit), type = "l", col = "purple")

方法对比

  • 傅里叶变换:适合快速定位主要周期,数据量越大结果越可靠,适合初步筛选周期;
  • forecast包傅里叶项:代码简洁,无需手动处理频域转换,适合快速建模;
  • 非线性最小二乘:直接输出周期估计值,但需要合理的初始参数,否则可能不收敛。

内容的提问来源于stack exchange,提问作者T X

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.16 07:05:52