Python中的曲线拟合,需要曲线形状几乎完全匹配而不是最小化均方差的曲线
Curve fitting in Python, need almost exact match of the shape of the curve rather than a curve that minimize mean square difference
曲线和我的拟合尝试:
我希望为我的模型函数找到系数 (A, B, C, D, E, F)
:A * x**2 + B * x + C * np.cos(D * x - E) + F
几乎与蓝色曲线完全匹配。但是因为我使用了 SciPy 的优化 curve_fit
,它找到了平方差最小的曲线,它看起来像图像中的红色曲线。虽然我希望红色曲线与蓝色曲线的波峰和波谷相匹配。 scipy
可以做到这一点吗?你是怎么做到的。如果没有,是否有任何其他图书馆可以处理这个问题?
这是JJacquelin提到的做双线性拟合的方法。它拟合数据,可用于为 non-linear 拟合提供初始猜测。请注意,对于此方法,需要将 P sin( w t + p )
表示为 A sin( w t ) + B cos( w t )
,但这很容易做到。
import matplotlib.pyplot as plt
import numpy as np
from scipy.integrate import cumtrapz
from scipy.optimize import curve_fit
def signal( x, A, B, C, D, E, F ):
### note: C, D, E, F have different meaning here
r = (
A * x**2
+ B * x
+ C
+ D * np.sin( F * x )
+ E * np.cos( F * x )
)
return r
def signal_p( x, A, B, C, D, E, F ):
r = (
A * x**2
+ B * x
+ C * np.sin( D * x - E )
+ F
)
return r
testparams = [ -1, 1, 3, 0.005, 0.03, 22 ]
### test data with noise
xl = np.linspace( -0.3, 1.6, 190 )
sl = signal( xl, *testparams )
sl += np.random.normal( size=len( xl ), scale=0.005 )
### numerical integrals
Sl = cumtrapz( sl, x=xl, initial=0 )
SSl = cumtrapz( Sl, x=xl, initial=0 )
### fitting the integro-differential equation to get the frequency
"""
note:
with y = A x**2 +...+ D sin() + E cos()
the double integral int( int(y) ) = a x**4 + ... - y/F**2
"""
VMXT = np.array( [ xl**4, xl**3, xl**2, xl, np.ones( len( xl ) ), sl ] )
VMX = VMXT.transpose()
A = np.dot( VMXT, VMX )
SV = np.dot( VMXT, SSl )
AI = np.linalg.inv( A )
result = np.dot( AI , SV )
print ( "Fit: ",result )
F = np.sqrt( -1 / result[-1] )
print("F = ", F)
### Fitting the linear parameters with the frequency known
VMXT = np.array(
[
xl**2, xl, np.ones( len( xl ) ),
np.sin( F * xl), np.cos( F * xl )
]
)
VMX = VMXT.transpose()
A = np.dot( VMXT, VMX )
SV = np.dot( VMXT, sl )
AI = np.linalg.inv( A )
A, B, C, D, E = np.dot( AI , SV )
print( A, B, C, D, E )
### Non-linear fit with initial guesses
amp = np.sqrt( D**2 + E**2 )
phi = -np.arctan( D / E )
opt, cov = curve_fit( signal_p, xl, sl, p0=( A, B, amp, F, phi, C ) )
print( opt )
### plotting
fig = plt.figure()
ax = fig.add_subplot( 1, 1, 1 )
ax.plot(
xl, sl,
ls='', marker='+', label="data", markersize=5
)
ax.plot(
xl, signal( xl, A, B, C, D, E, F ),
ls="--", label="double linear fit"
)
ax.plot(
xl, signal_p( xl, *opt ),
ls=":", label="non-linear"
)
ax.legend( loc=0 )
ax.grid()
plt.show()
提供
Fit: [-0.083161 0.1659759 1.49879056 0.848999 0.130222 -0.001990]
F = 22.414133356157887
-0.998516 0.998429 3.000265 0.012701 0.026926
[-0.99856269 0.9973273 0.0305014 21.96402992 -1.4215656 3.00100979]
和
在没有初始猜测的情况下使用 non-linear 拟合时,我得到的基本上是抛物线。人们可以理解为什么在可视化正弦 half-wave 时。这基本上也是一条抛物线。因此,non-linear 拟合在那个方向驱动相应的参数,特别是知道默认的初始猜测是 1
。所以离小振幅和高频差远了。拟合仅在 chi-square hyper-plane.
中找到局部最小值
曲线和我的拟合尝试:
我希望为我的模型函数找到系数 (A, B, C, D, E, F)
:A * x**2 + B * x + C * np.cos(D * x - E) + F
几乎与蓝色曲线完全匹配。但是因为我使用了 SciPy 的优化 curve_fit
,它找到了平方差最小的曲线,它看起来像图像中的红色曲线。虽然我希望红色曲线与蓝色曲线的波峰和波谷相匹配。 scipy
可以做到这一点吗?你是怎么做到的。如果没有,是否有任何其他图书馆可以处理这个问题?
这是JJacquelin提到的做双线性拟合的方法。它拟合数据,可用于为 non-linear 拟合提供初始猜测。请注意,对于此方法,需要将 P sin( w t + p )
表示为 A sin( w t ) + B cos( w t )
,但这很容易做到。
import matplotlib.pyplot as plt
import numpy as np
from scipy.integrate import cumtrapz
from scipy.optimize import curve_fit
def signal( x, A, B, C, D, E, F ):
### note: C, D, E, F have different meaning here
r = (
A * x**2
+ B * x
+ C
+ D * np.sin( F * x )
+ E * np.cos( F * x )
)
return r
def signal_p( x, A, B, C, D, E, F ):
r = (
A * x**2
+ B * x
+ C * np.sin( D * x - E )
+ F
)
return r
testparams = [ -1, 1, 3, 0.005, 0.03, 22 ]
### test data with noise
xl = np.linspace( -0.3, 1.6, 190 )
sl = signal( xl, *testparams )
sl += np.random.normal( size=len( xl ), scale=0.005 )
### numerical integrals
Sl = cumtrapz( sl, x=xl, initial=0 )
SSl = cumtrapz( Sl, x=xl, initial=0 )
### fitting the integro-differential equation to get the frequency
"""
note:
with y = A x**2 +...+ D sin() + E cos()
the double integral int( int(y) ) = a x**4 + ... - y/F**2
"""
VMXT = np.array( [ xl**4, xl**3, xl**2, xl, np.ones( len( xl ) ), sl ] )
VMX = VMXT.transpose()
A = np.dot( VMXT, VMX )
SV = np.dot( VMXT, SSl )
AI = np.linalg.inv( A )
result = np.dot( AI , SV )
print ( "Fit: ",result )
F = np.sqrt( -1 / result[-1] )
print("F = ", F)
### Fitting the linear parameters with the frequency known
VMXT = np.array(
[
xl**2, xl, np.ones( len( xl ) ),
np.sin( F * xl), np.cos( F * xl )
]
)
VMX = VMXT.transpose()
A = np.dot( VMXT, VMX )
SV = np.dot( VMXT, sl )
AI = np.linalg.inv( A )
A, B, C, D, E = np.dot( AI , SV )
print( A, B, C, D, E )
### Non-linear fit with initial guesses
amp = np.sqrt( D**2 + E**2 )
phi = -np.arctan( D / E )
opt, cov = curve_fit( signal_p, xl, sl, p0=( A, B, amp, F, phi, C ) )
print( opt )
### plotting
fig = plt.figure()
ax = fig.add_subplot( 1, 1, 1 )
ax.plot(
xl, sl,
ls='', marker='+', label="data", markersize=5
)
ax.plot(
xl, signal( xl, A, B, C, D, E, F ),
ls="--", label="double linear fit"
)
ax.plot(
xl, signal_p( xl, *opt ),
ls=":", label="non-linear"
)
ax.legend( loc=0 )
ax.grid()
plt.show()
提供
Fit: [-0.083161 0.1659759 1.49879056 0.848999 0.130222 -0.001990]
F = 22.414133356157887
-0.998516 0.998429 3.000265 0.012701 0.026926
[-0.99856269 0.9973273 0.0305014 21.96402992 -1.4215656 3.00100979]
和
在没有初始猜测的情况下使用 non-linear 拟合时,我得到的基本上是抛物线。人们可以理解为什么在可视化正弦 half-wave 时。这基本上也是一条抛物线。因此,non-linear 拟合在那个方向驱动相应的参数,特别是知道默认的初始猜测是 1
。所以离小振幅和高频差远了。拟合仅在 chi-square hyper-plane.