具有固定峰值的抛物线拟合

Parabolic fit with fixed peak

我有一组数据,想对其进行抛物线拟合。这已经适用于 numpy 的 polyfit 函数,如下所示:

fit = np.polyfit(X, y, 2)
formula = np.poly1d(fit)

现在我希望抛物线的峰值在一个固定的 x 值处,并且仍然尽可能地用这个固定的峰值进行拟合。有办法实现吗?

根据我的数据我知道抛物线总是向下开口。

我认为这是一个相当困难的问题,因为二阶多项式 (ax^2 + bx + c) 的峰值的 x 坐标总是位于x = -b/2a.

您可以做的一件事是删除 b 项并将其偏移到拟合多项式时所需的峰值 x 值,如下面的代码所示。请注意,我使用 scipy.optimize.curve_fit 来适应自定义函数 func.

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

# generating a parabola with noise
np.random.seed(42)
x = np.linspace(-10, 10, 100)
y = 10 -(x-2)**2 + np.random.normal(0, 5, x.shape)

# function to fit
def func(x, a, c):
    return a*x**2 + c

# desired x peak value
x_peak = 2

popt, pcov = curve_fit(func, x - x_peak, y)

y_fit = func(x - x_peak, *popt)

# plotting
plt.plot(x, y, 'k.')
plt.plot(x, y_fit)
plt.axvline(x_peak)
plt.show()

输出图像:

在你的抛物线上固定一个点可以简化问题,因为你现在可以根据常数稍微重写你的方程式:

y = A(x - B)**2 + C

给定原始无约束拟合中的系数 abc,您有关系

a = A
b = -2AB
c = AB**2 + C

唯一的区别是,由于B是一个常数,而方程中没有x - B项,因此您需要自己设置最小二乘问题。给定数组 xy 和常量 B,问题如下所示:

m = np.stack((x - B, np.ones_like(x)), axis=-1)
(A, C), *_ = np.linalg.lstsq(m, y, rcond=None)

然后您可以从上述 abc 的公式中提取正态系数。

这是一个完整的示例,就像另一个答案中的示例一样:

B = 2

np.random.seed(42)
x = np.linspace(-10, 10, 100)
y = 10 -(x - B)**2 + np.random.normal(0, 5, x.shape)

m = np.stack(((x - B)**2, np.ones_like(x)), axis=-1)
(A, C), *_ = np.linalg.lstsq(m, y, rcond=None)

a = A
b = -2 * A * B
c = A * B**2 + C

y_fit = a * x**2 + b * x + c

您可以完全删除 abc 并执行

y_fit = A * (x - B)**2 + C

结果是一样的。

plt.plot(x, y, 'k.')
plt.plot(x, y_fit)

如果没有峰位置条件,要拟合的函数将是:

y = a x^2 + b x + c

在 x=p 处的峰位置条件下,给定 p :

-b/(2a)=p

b=-2个p

y = a x^2 -2 p x + c

y = a (x^2 - 2 p x) +c

知道 p ,变量的一个变化:

X = x^2 -2 p x

因此,首先根据数据 (x,y) 计算新数据 (X,y)

然后通过线性回归计算出 a 和 c

y = a X + c