我想要拟合的信号是多个正弦函数(和噪声)的叠加,我希望同时适合所有频率。这里是一个示例数据文件,由两个频率240d ^ -1和261.8181d ^ -1生成: https://owncloud.gwdg.de/index.php/s/JZQTJ3VMYZH8qNB和plot of the time series (excerpt)
到目前为止,我可以将一个正弦函数置于另一个之后,同时将频率固定为一个值。我得到频率从例如一个周期图,最后我对拟合的幅度和相位感兴趣。
import numpy as np
from scipy import optimize
import bottleneck as bn
def f_sinus0(x,a,b,c,d):
return a*np.sin(b*x+c)+d
def fit_single(t, flux, flux_err, freq_model, c0 = 0.):
# initial guess for the parameter
d0 = bn.nanmean(flux)
a0 = 3*np.std(flux)/np.sqrt(2.)
# fit function with fixed frequency "freq_model"
popt, pcov = optimize.curve_fit(lambda x, a, c, d:
f_sinus0(x, a, freq_model*2*np.pi, c, d),
t, flux, sigma = flux_err, p0 = (a0,c0,d0),
bounds=([a0-0.5*abs(a0),-np.inf,d0-0.25*abs(d0)],
[a0+0.5*abs(a0),np.inf,d0+0.25*abs(d0)]),
absolute_sigma=True)
perr = np.sqrt(np.diag(pcov))
return popt, perr
filename = 'data-test.csv'
data = np.loadtxt(filename)
time = data[0]
flux = data[1]
flux_err = data[2]
freq_model = 260 #d^-1
popt, perr = fit_single(time, flux, flux_err, freq_model, c0 = 0.)
现在我想同时适应两个频率。我定义了一个函数,它返回一个拟合函数的总和,这取决于input-parameter-list的长度,如下所示
def f_multiple_sin(x, *params):
y = np.zeros_like(x)
for i in range(0, len(params), 4): #4=amplitude, freq, phase, offset
amplitude = params[i]
freq = params[i+1]
phase = params[i+2]
offset = params[i+3]
y = y + amplitude*np.sin(np.multiply(freq, x)+phase)+offset
return y
执行契合
def fit_multiple(t, flux, flux_err, guess):
popt, pcov = optimize.curve_fit(
f_multiple_sin, t, flux, sigma=flux_err, p0=guess,
bounds=(guess-np.multiply(guess,0.1),guess+np.multiply(guess,0.1)),
absolute_sigma=True
)
perr = np.sqrt(np.diag(pcov))
return popt, perr
guess = [4.50148944e-03, 2.40000040e+02, 3.01766641e-03, 8.99996136e-01, 3.14546648e-03, 2.61818207e+02, 2.94282247e-03, 5.56770657e-06]
popt, perr = fit_multiple(time, flux, flux_err, guess)
使用个别拟合的结果作为初始参数guess = [amplitude1, frequency1, phase1, offset1, amplitude2,...]
但是我如何适应多个正弦函数,每个函数都有一个固定的频率?在这种情况下,lambda
方法对我来说似乎并不那么直接。
答案 0 :(得分:0)
这是使用scipy.optimize.leastsq
的解决方案,这给了我更多自由。但是,在进行错误评估时,您必须要小心。另一方面,它不像curve_fit
那样严格,涉及参数的数量。
在这个解决方案中,我基本上适合三个列表,即幅度,频率和相位。似乎方便传递它像这样排序到函数。
最后,您可以修复任何频率子集。不过,我的印象是收敛对启动参数非常敏感。
import matplotlib.pyplot as plt
import numpy as np
import scipy.optimize as so
def multisine(x, ampList, freqList, phaseList):
assert len( ampList ) == len( freqList )
assert len( ampList ) == len( phaseList )
out=0
for a, f, p in zip( ampList, freqList, phaseList ):
out += a * np.sin( x * f + p )
return out
### FixedFrequencies is a list of values and positions in the list to pass to multisine....remember counting from zero
def multisine_fixed_fs(x, params, n, FixedFrequencies=None):
if FixedFrequencies is None:
assert len( params ) == 3 * n
ampList = params[ : n]
freqList = params[ n : 2* n]
phaseList = params[ 2 * n : ]
else:
assert len( params ) + len( FixedFrequencies ) == 3 * n
ampList = params[ : n]
freqList = list(params[ n : -n ])
phaseList = params[ -n : ]
sortedList = sorted( list(FixedFrequencies), key=lambda x: x[-1] )
for fixed in sortedList:
freqList.insert(fixed[-1], fixed[0] )
return multisine(x, ampList, freqList, phaseList)
def residuals(params, data, n, FixedFrequencies=None):
xList, yList = zip( *data )
thyList = [ multisine_fixed_fs( x, params, n , FixedFrequencies=FixedFrequencies ) for x in xList ]
d = [ y1- y2 for y1, y2 in zip( yList, thyList ) ]
return d
xList = np.linspace( 0, 100, 100 )
yList = np.fromiter( ( multisine(x, [ 1, .3 ], [ .4, .42 ],[ 0, .1] ) for x in xList ), np.float )
data = zip( xList, yList )
fit, err = so.leastsq( residuals, x0=[ 1.2, .32 ] + [ .42, .43 ] + [ 0.1, 0.12 ], args=( data, 2 ) )
print fit
fit, err = so.leastsq( residuals, x0=[ 1.2, .32 ] + [ .42 ] + [ 0.1, 0.12 ], args=( data, 2 , [ [ .45, 1 ] ]) )
print fit
y2List = np.fromiter( ( multisine(x, [ fit[0], fit[1] ], [ fit[2], .45 ],[ fit[-2], fit[-1] ] ) for x in xList ), np.float )
fit, err = so.leastsq( residuals, x0=[ 1.2, .32 ] + [ 0.1, 0.12 ], args=( data, 2 , [ [ .39, 0 ],[ .45, 1 ] ]) )
print fit
y3List = np.fromiter( ( multisine(x, [ fit[0], fit[1] ], [ .39, .45 ],[ fit[-2], fit[-1] ] ) for x in xList ), np.float )
fig = plt.figure(1)
ax = fig.add_subplot( 1, 1, 1 )
ax.plot(xList,yList)
ax.plot(xList,y2List)
ax.plot(xList,y3List)
plt.show()
提供了:
>> [ 1.00000006e+00 2.99999889e-01 3.99999999e-01 4.20000009e-01 1.47117910e-07 6.38318486e+00 ]
>> [ 1.12714624 0.12278804 0.40198029 0.08039605 -1.08564396 ]
>> [ 1.05124097 -0.32600116 0.6633511 1.18400026 ]