将正弦项拟合到数据


26

尽管我读了这篇文章,但我仍然不知道如何将其应用于我自己的数据,并希望有人能帮助我。

我有以下数据:

y <- c(11.622967, 12.006081, 11.760928, 12.246830, 12.052126, 12.346154, 12.039262, 12.362163, 12.009269, 11.260743, 10.950483, 10.522091,  9.346292,  7.014578,  6.981853,  7.197708,  7.035624,  6.785289, 7.134426,  8.338514,  8.723832, 10.276473, 10.602792, 11.031908, 11.364901, 11.687638, 11.947783, 12.228909, 11.918379, 12.343574, 12.046851, 12.316508, 12.147746, 12.136446, 11.744371,  8.317413, 8.790837, 10.139807,  7.019035,  7.541484,  7.199672,  9.090377,  7.532161,  8.156842,  9.329572, 9.991522, 10.036448, 10.797905)
t <- 18:65

现在我只想适合一个正弦波

y(t)=Asin(ωt+ϕ)+C.

与四个未知数,,和到它。ω φ ÇAωϕC

我的代码其余部分如下

res <- nls(y ~ A*sin(omega*t+phi)+C, data=data.frame(t,y), start=list(A=1,omega=1,phi=1,C=1))
co <- coef(res)

fit <- function(x, a, b, c, d) {a*sin(b*x+c)+d}

# Plot result
plot(x=t, y=y)
curve(fit(x, a=co["A"], b=co["omega"], c=co["phi"], d=co["C"]), add=TRUE ,lwd=2, col="steelblue")

但是结果确实很差。

正弦拟合

非常感谢您的帮助。

干杯。


您正在尝试将正弦波拟合到数据中,还是在尝试使用正弦和余弦分量拟合某种谐波模型?R中的TSA软件包中有一个谐波函数,您可能需要检查一下。使用该模型拟合模型,并查看得到的结果。
埃里克·彼得森

5
您是否尝试过其他起始值?您的损失函数是非凸的,因此不同的起始值可能导致不同的解决方案。
Stefan Wager 2013年

1
告诉我们更多有关数据的信息。通常有一个已知的周期性,因此不需要从数据中估算出来。这是时间序列还是其他?如果可以通过线性模型拟合单独的正弦和余弦项,则容易得多。
Nick Cox

2
不确定的周期会使您的模型非线性(在链接的帖子中,所选答案中暗示了这种事件)。给定的是,其他参数是条件线性的;对于某些非线性LS例程,信息很重要,并且可以改善行为。一种选择可能是使用频谱方法来获得周期和条件。另一个方法是分别以迭代方式通过非线性和线性优化来更新周期和其他参数。
Glen_b-恢复莫妮卡

(我刚刚编辑了答案,以使未知时期的特殊情况成为使其非线性的明确示例。)
Glen_b -Reinstate Monica

Answers:


18

如果您只想对一个很好的估计,而不必关心它的标准错误:ω

ssp <- spectrum(y)  
per <- 1/ssp$freq[ssp$spec==max(ssp$spec)]
reslm <- lm(y ~ sin(2*pi/per*t)+cos(2*pi/per*t))
summary(reslm)

rg <- diff(range(y))
plot(y~t,ylim=c(min(y)-0.1*rg,max(y)+0.1*rg))
lines(fitted(reslm)~t,col=4,lty=2)   # dashed blue line is sin fit

# including 2nd harmonic really improves the fit
reslm2 <- lm(y ~ sin(2*pi/per*t)+cos(2*pi/per*t)+sin(4*pi/per*t)+cos(4*pi/per*t))
summary(reslm2)
lines(fitted(reslm2)~t,col=3)    # solid green line is periodic with second harmonic

正弦图

(更合适的位置可能仍会以某种方式解释该系列中的异常值,从而降低其影响力。)

---

如果您想了解中的不确定性,可以使用轮廓可能性(pdf1pdf2-从轮廓可能性或其变体中获取近似CI或SE的引用并不难找到)ω

(或者,您可以将这些估计值输入nls ...并开始已经收敛。)


(+1)个好答案。我试图用来拟合线性模型,lm(y~sin(2*pi*t)+cos(2*pi*t)但这没有用(cos术语始终为1)。出于好奇:前两行是做什么的(我知道这会spectrum估计频谱密度)?
COOLSerdash

1
@COOLSerdash是的,您必须使的单位为工作周期(与链接的问题相同)。我应该回过头来强调另一个答案。(ctd)t2*pi*t
Glen_b-恢复莫妮卡

1
@COOLSerdash(ctd)-第二行找到与频谱中最大峰值相关的频率,然后反转以识别周期。至少在这种情况下(但我怀疑范围更广),其默认值实质上确定了使似然最大化的时段,因此我删除了为了使该时段周围区域的轮廓似然最大化而采取的步骤。specTSA中的功能可能更好(它似乎有更多的选择,其中有时可能很重要),但是在这种情况下,主峰与完全在同一位置,spectrum因此我没有理会。
Glen_b-恢复莫妮卡

@Glen_b此方法对我的用例有用。我还需要拟合一个cos(x)曲线,但它也无法正常工作...我将更改为reslmreslm <- lm(y ~ cos(2*pi/per*t)+tan(2*pi/per*t))但看起来并不正确。有什么提示吗?
阿米特·科利

你为什么在那里晒黑呢?
Glen_b-恢复莫妮卡

15

正如@Stefan所建议的那样,不同的起始值似乎确实可以显着提高拟合度。我查看了一下数据,认为欧米茄应该为,因为这些峰看起来相隔约20个单位。2π/20

当我将其添加到nlsstart列表中时,尽管它仍然存在一些系统偏差,但我得到的曲线要合理得多。

根据此数据集的目标,您可以尝试通过添加其他项或使用非参数方法(例如带有周期性核的高斯过程)来提高拟合度。

正弦拟合

自动选择起始值

如果要选择主导频率,则可以使用快速傅立叶变换(FFT)。这超出了我的专业知识范围,因此,如果其他人愿意,我可以让他们填写详细信息(尤其是关于步骤2和3),但是R下面的代码应该可以工作。

# Step 1: do the FFT
raw.fft = fft(y)

# Step 2: drop anything past the N/2 - 1th element.
# This has something to do with the Nyquist-shannon limit, I believe
# (https://en.wikipedia.org/wiki/Nyquist%E2%80%93Shannon_sampling_theorem)
truncated.fft = raw.fft[seq(1, length(y)/2 - 1)]

# Step 3: drop the first element. It doesn't contain frequency information.
truncated.fft[1] = 0

# Step 4: the importance of each frequency corresponds to the absolute value of the FFT.
# The 2, pi, and length(y) ensure that omega is on the correct scale relative to t.
# Here, I set omega based on the largest value using which.max().
omega = which.max(abs(truncated.fft)) * 2 * pi / length(y)

您还可以绘图abs(truncated.fft)以查看是否还有其他重要的频率,但是您将不得不稍微调整x轴的缩放比例。

另外,我相信@Glen_b是正确的,一旦您知道omega(或者也许您也需要了解phi,这个问题就凸出来了?我不确定)。无论如何,了解其他参数的起始值并不会像omega一样重要。您可能可以从FFT得到其他参数的合理估计,但是我不确定这将如何工作。


1
感谢您的提示。只是澄清一下:数据是微阵列的一部分,其中随着时间的推移测量了基因的周期性,即显示的数据是一个基因的表达数据。现在的问题是,我想将此方法应用于大约40k个均具有不同周期性和振幅的基因。因此,独立于初始条件找到合适的位置非常重要。
Pascal

1
@Pascal有关自动选择omega起始值的建议,请参见上面的更新。
David J. Harris

2
@ DavidJ.Harris您也可以在线性模型中估计(好吧,直接从线性模型中的和计算得出),请参阅OP链接到的文章。ϕab
Glen_b-恢复莫妮卡

我想知道x值在这里起作用。确保给定y值分隔1或5 x步长对Ω有所不同,不是吗?
knub 2014年

1
与问题无关的编程技巧:将R对象命名为时要小心foo.bar。这是由于R如何指定类的方法
Firebug

10

作为已经说过的替代方案,可能值得注意的是,可以使用ARIMA模型类别的AR(2)模型来生成正弦波模式的预测。

AR(2)模型可以编写如下: 其中是常数,和是要估计的参数,而是随机冲击项。

yt=C+ϕ1yt1+ϕ2yt2+at
Cϕ1ϕ2at

现在,并不是所有的AR(2)模型都在其预测中产生正弦波模式(也称为随机周期),但是在满足以下条件时确实会发生:

ϕ12+4ϕ2<0.

Panratz(1991)告诉我们以下有关随机周期的信息:

随机周期模式可以认为是预测模式中的正弦波模式失真:它是具有随机(概率)周期,幅度和相位角的正弦波。

为了查看是否可以将这样的模型拟合到数据,我使用auto.arima()了预测包中的函数来找出它是否建议使用AR(2)模型。事实证明,该auto.arima()函数建议使用ARMA(2,2)模型。不是纯AR(2)模型,但这没关系。可以,因为ARMA(2,2)模型包含AR(2)组件,所以适用相同的规则(大约随机周期)。也就是说,我们仍然可以检查上述条件,看看是否会产生正弦波预测。

的结果auto.arima(y)如下所示。

Series: y 
ARIMA(2,0,2) with non-zero mean 

Coefficients:
         ar1      ar2      ma1     ma2  intercept
      1.7347  -0.8324  -1.2474  0.6918    10.2727
s.e.  0.1078   0.0981   0.1167  0.1911     0.5324

sigma^2 estimated as 0.6756:  log likelihood=-60.14
AIC=132.27   AICc=134.32   BIC=143.5

现在让我们检查一下条件: ,我们发现条件确实得到满足。

ϕ12+4ϕ2<01.73472+4(0.8324)<00.3202914<0

下图显示了原始序列y,ARMA(2,2)模型的拟合以及14个样本外的预测。可以看出,样本外预测遵循正弦波模式。

在此处输入图片说明

请记住两件事。1)这只是一个非常快速的分析(使用自动化工具),正确的处理将涉及遵循Box-Jenkins方法。2)ARIMA预测擅长短期预测,因此您可能会发现@David J. Harris和@Glen_b的答案中的模型提供的长期预测更为可靠。

最后,希望这是对已经非常有用的答案的很好补充。

参考:使用动态回归模型进行的预测:Alan Pankratz,1991年,(John Wiley and Sons,纽约),ISBN 0-471-61528-5


1

将正弦曲线拟合到给定数据集的当前方法需要先猜测参数,然后进行交互过程。这是一个非线性回归问题。借助便利的积分方程,可以将非线性回归转换为线性回归。这样,就不需要初始猜测,也不需要迭代过程:直接获得拟合。如果函数y = a + r * sin(w * x + phi)或y = a + b * sin(w * x)+ c * cos(w * x),请参见论文第35-36页在Scribd上发布的“Régressionsinusoidale”:http : //www.scribd.com/JJacquelin/documents 在函数y = a + p * x + r * sin(w * x + phi)的情况下:“线性和正弦混合回归”一章的第49-51页。对于更复杂的函数,一般过程在第54-61页的“广义正弦回归”一章中进行了说明,后面是数值示例y = r * sin(w * x + phi)+(b / x)+ c * ln(x),第62-63页


0

如果您知道余弦外观数据的最低和最高点,则可以使用以下简单函数来计算所有余弦系数:

getMyCosine <- function(lowest_point=c(pi,-1), highest_point=c(0,1)){
  cosine <- list(
    T = pi / abs(highest_point[1] - lowest_point[1]),
    b = - highest_point[1],
    k = (highest_point[2] + lowest_point[2]) / 2,
    A = (highest_point[2] - lowest_point[2]) / 2
  )
  return(cosine)
}

通过输入余弦函数的最低和最暖小时的小时数和温度值,可在下方使用余弦函数模拟全天温度的变化:

c <- getMyCosine(c(4,10),c(17,25)) 
# lowest temprature at 4:00 (10 degrees), highest at 17:00 (25 degrees)

x = seq(0,23,by=1);  y = c$A*cos(c$T*(x +c$b))+c$k ; 
library(ggplot2);   qplot(x,y,geom="step")

输出如下: 根据最低和最高点计算余弦


3
这种方法似乎对纯正弦行为的任何随机现象都特别敏感,这将使其几乎不适用于问题中所示的任何数据集。可以想象,它可以用来为该线程中建议的其他一些迭代方法提供起始值。
ub

同意,这是最简单的方法,在某些假设下适合简单近似
IVIM

0

另一种选择是使用通用函数optim 或nls。我试过他们都不是完全健壮

以下函数获取y中的数据并计算参数。

calc.period <- function(y,t)
{     
   fs <- 1/(t[2]-t[1])
   ssp <- spectrum(y,plot=FALSE )  
   fN <- ssp$freq[which.max(ssp$spec)]
   per <- 1/(fN*fs)
   return(per)
 }

fit.sine<- function(y, t)
{ 
  data <- data.frame(x = as.vector(t), y=as.vector(y))
  min.RSS <- function (data, par){
    with(data, sum((par[1]*sin(2*pi*par[2]*x + par[3])+par[4]-y )^2))
  }  
  amp = sd(data$y)*2.**0.5
  offset = mean(data$y)
  fest <- 1/calc.period(y,t)
  guess = c( amp, fest,  0,   offset)
  #res <- optim(par=guess, fn = min.RSS, data=data ) 
  r<-nls(y~offset+A*sin(2*pi*f*t+phi), 
     start=list(A=amp, f=fest, phi=0, offset=offset))
  res <- list(par=as.vector(r$m$getPars()))
  return(res)
}

 genSine <- function(t, params)
     return( params[1]*sin(2*pi*params[2]*t+ params[3])+params[4])

用途如下:

t <- seq(0, 10, by = 0.01)
A <- 2 
f <- 1.5
phase <- 0.2432
offset <- -2

y <- A*sin(2*pi*f*t +phase)+offset + rnorm(length(t), mean=0, sd=0.2)

reslm1 <- fit.sine(y = y, t= t)

以下代码比较数据

ysin <- genSine(as.vector(t), params=reslm1$par)
ysin.cor <- genSine(as.vector(t), params=c(A, f, phase, offset))

plot(t, y)
lines(t, ysin, col=2)
lines(t, ysin.cor, col=3)
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.