【问题标题】:SciPy Curve_fit() doesn't fit curveSciPy Curve_fit() 不适合曲线
【发布时间】:2021-09-25 12:02:10
【问题描述】:

我制作了一个随机图,并尝试使用 SciPy curve_fit 将最佳曲线拟合到绘图中,但它失败了。

首先,我生成了一个随机指数衰减图,其中A, w, T2是使用numpy随机生成的:

def expDec(t, A, w, T2):
    return A * np.cos(w * t) * (2.718**(-t / T2))

现在我让 SciPy 猜出最佳拟合曲线:

t = x['Input'].values
hr = x['Output'].values
c, cov = curve_fit(bpm, t, hr)

然后我绘制曲线

for i in range(n):
    y[i] = bpm(x['Input'][i], c[0], c[1], c[2])
plt.plot(x['Input'], x['Output'])
plt.plot(x['Input'], y)

就是这样。以下是合身度数:

如果有人能帮忙,那就太好了。

MWE(也可通过交互方式获得 here

import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from scipy.optimize import curve_fit

inputs = []
outputs = []

# THIS GIVES THE DOMAIN
dom = np.linspace(-5, 5, 100)

# FUNCTION & PARAMETERS (RANDOMLY SELECTED)
A = np.random.uniform(3, 6)
w = np.random.uniform(3, 6)
T2 = np.random.uniform(3, 6)
y = A * np.cos(w * dom) * (2.718**(-dom / T2))

# DEFINES EXPONENTIAL DECAY FUNCTION
def expDec(t, A, w, T2):
    return A * np.cos(w * t) * (2.718**(-t / T2))

# SETS UP FIGURE FOR PLOTTING
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)

# PLOTS THE FUNCTION
plt.plot(dom, y, 'r')

# SHOW THE PLOT
plt.show()

for i in range(-9, 10): 
    inputs.append(i)
    outputs.append(expDec(i, A, w, T2))
    
# PUT IT DIRECTLY IN A PANDAS DATAFRAME
points = {'Input': inputs, 'Output': outputs}

x = pd.DataFrame(points, columns = ['Input', 'Output'])
   
# FUNCTION WHOSE PARAMETERS PROGRAM SHOULD BE GUESSING
def bpm(t, A, w, T2):
    return A * np.cos(w * t) * (2.718**(-t / T2))

# INPUT & OUTPUTS
t = x['Input'].values
hr = x['Output'].values

# USE SCIPY CURVE FIT TO USE NONLINEAR LEAST SQUARES TO FIND BEST PARAMETERS. TRY 1000 TIMES BEFORE STOPPING.
constants = curve_fit(bpm, t, hr, maxfev=1000)

# GET CONSTANTS FROM CURVE_FIT
A_fit = constants[0][0]
w_fit = constants[0][1]
T2_fit = constants[0][2]

# CREATE ARRAY TO HOLD FITTED OUTPUT
fit = []

# APPEND OUTPUT TO FIT=[] ARRAY
for i in range(-9,10):
    fit.append(bpm(i, A_fit, w_fit, T2_fit))
    
# PLOTS BEST PARAMETERS
plt.plot(x['Input'], x['Output'])
plt.plot(x['Input'], fit, "ro-")

【问题讨论】:

  • 请提供minimal reproducible example,即缺少thr 的具体值。尽管如此,考虑到少数数据点,我觉得这很合适。另请注意,您需要更多数据点才能绘制平滑图。
  • 请不要将您对网站规则的看法添加到问题中。
  • 您的拟合需要更好的初步猜测
  • 另外,你不需要循环:y = expDec(x['Input'], *c)
  • @MadPhysicist 你能澄清一下吗? 1)我没有使用初始猜测。我正在使用 SciPy 来拟合图表。 2)我不明白你说的那行代码是什么意思。 *c 是什么?

标签: python scipy curve-fitting scipy-optimize


【解决方案1】:

作为第一步,我想重写您的 MCVE 以使用矢量化操作并且仅使用函数计算的单个实例。这会将所有内容减少到几行。我建议您在进行测试时也使用种子来实现可重复性:

def exp_dec(t, A, w, T2):
    return A * np.cos(w * t) * np.exp(-t / T2)

np.random.seed(42)
A, w, T2 = np.random.uniform(3, 6, size=3)
dom = np.linspace(-9, 9, 1000)

t = np.arange(-9., 10.)
hr = exp_dec(t, A, w, T2)

fit, _ = curve_fit(exp_dec, t, hr)

fig, ax = plt.subplots()
ax.plot(dom, exp_dec(dom, A, w, T2), 'g', label='target')
ax.scatter(t, hr, c='r', label='samples')
ax.plot(dom, exp_dec(dom, *fit), 'b', label='fit')
ax.plot(dom, exp_dec(dom, 1, 1, 1), 'k:', label='start')
ax.legend()

要解释最后绘制的项目,请查看curve_fit 的文档。请注意,有一个参数p0,如果您不提供它,则默认为所有参数。这是您的拟合开始猜测值的初始猜测。

看这张照片,你几乎可以看出问题所在。起始猜测的频率比您的数据低得多。因为采样频率非常接近振荡频率,所以在能够充分提高频率以获得正确函数之前,拟合会达到局部最小值。您可以通过几种不同的方式解决此问题。

一种方法是给curve_fit 一个更好的初始猜测。如果您知道幅度、频率和衰减率的界限,请使用它们。幅度通常是直接的线性拟合。最困难的通常是频率,正如您在此处看到的,最好高估它。但是如果你过高估计它,你最终可能会得到原始数据的谐波。

这里有几个样本拟合显示了优化中的不同局部最小值。第二个显示了高估振荡频率的谐波情况:

一组不错的起始参数是您的随机范围的上限:

fit, _ = curve_fit(exp_dec, t, hr, p0=[6, 6, 6])

绿色曲线与蓝色非常接近,你看不到它:

>>> A, w, T2
(4.123620356542087, 5.852142919229749, 5.195981825434215)
>>> tuple(fit)
(4.123620356542086, 5.852142919229749, 5.195981825434215)

解决问题的另一种方法是更频繁地对数据进行采样。更多的数据通常意味着在优化中达到错误的局部最小值的可能性更低。但是,在处理正弦函数时,由于匹配的工作原理,这并不总是有帮助。这是一个样本数为 10 倍的示例(仅 2 倍的拟合,默认猜测完全失败):

...
t = np.arange(-9., 10., 0.1)
...

【讨论】:

    猜你喜欢
    • 2016-11-29
    • 2020-07-12
    • 1970-01-01
    • 2020-05-05
    • 2021-09-14
    • 1970-01-01
    • 1970-01-01
    • 2020-06-12
    • 2020-07-07
    相关资源
    最近更新 更多