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.

中找到局部最小值