将非线性数据拟合为分段函数的图形

3

我有一个类似这个之前的StackOverflow问题的问题。我有一个数据集,希望拟合几个分段函数,然后绘制结果。

下面以红色表示数据。

为了提供一些背景,y值表示电机转动x度需要多少毫秒。我已经上传了原始值到此Pastebin

现在我想分段拟合三个函数:

  • 数据开始时的多项式拟合,电机加速到最大速度。
  • 最大速度达到时的线性拟合。
  • 电机关闭并减速时的多项式拟合。

到目前为止,我已经尝试使用下面显示的代码执行两个线性函数的分段拟合。鉴于数据的外观,我期望看到一个斜率沿着原点到约ms=550的数据,然后从那里有一条与x轴平行的第二条线。

然而,这不是我得到的:

Data with fit

在尝试使用三个函数进行逐段拟合之前,我希望先了解为什么会得到这个图形而不是我预期的。

所以我的问题是:

  1. 有人能解释一下如何更正我的代码以使其适配两个线性函数吗?
  2. 如何扩展我的代码以绘制使用三个函数的分段拟合?

上面创建图表的代码如下:
from pandas import *
import matplotlib.pyplot as plt
import numpy as np
from scipy import optimize

#Getting data using Pandas
df = read_csv("test_data.csv")
ms = df["ms"].values
degrees = df["Degrees"].values

#A piece wise function taken from the other stackoverflow
def piecewise_linear(x, x0, y0, k1, k2):
    return np.piecewise(x, [x < x0], [lambda x:k1*x + y0-k1*x0, lambda x:k2*x + y0-k2*x0])

#Setting linspace and making the fit
x_new = np.linspace(ms[0], ms[-1])

p , e = optimize.curve_fit(piecewise_linear, ms, degrees)


#Plotting data and fit

fig = plt.figure()
ax = fig.add_subplot(111)

ax.plot(x_new, piecewise_linear(x_new, *p), '.', df)
ax.set_ylim([0, 450])
ax.set_xlim([0, 800])
1个回答

3

2. 问题: 您需要重新定义piecewise_linear,现在它有三个部分,您可以根据自己的喜好进行更改(我只提供了二次、一次和三次多项式的示例)。

#Piecewise function defining 2nd deg, 1st degree and 3rd degree exponentials
def piecewise_linear(x, x0, x1, y0, y1, k1, k2, k3, k4, k5, k6):
    return np.piecewise(x, [x < x0, x>= x0, x> x1], [lambda x:k1*x + k2*x**2, lambda x:k3*x + y0, lambda x: k4*x + k5*x**2 + k6*x**3 + y1])

1.问题:显然,为了使用curve_fit(),需要将数据转换为numpy数组。

#Setting linspace and making the fit, make sure to make you data numpy arrays
x_new = np.linspace(ms[0], ms[-1], dtype=float)
m = np.array(ms, dtype=float)
deg = np.array(degrees, dtype=float)
guess = np.array( [100, 500, -30, 350, -0.1, 0.0051, 1, -0.01, -0.01, -0.01], dtype=float)
p , e = optimize.curve_fit(piecewise_linear, m, deg)
#Plotting data and fit
fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(x_new, piecewise_linear(x_new, *p), '-', df)
ax.set_ylim([0, 450])
ax.set_xlim([0, 800])

作为一个侧面说明,我还为您的初始参数添加了一些合理的猜测,这样可以比让Python随机选择更好地拟合数据。
然后执行。
ax.plot(x_new, piecewise_linear(x_new, *p), '-', ms[::20], degrees[::20], 'o')

enter image description here


1
谢谢,这正是我在寻找的。我注意到我需要将猜测值添加到 curve_fit() 中以获得适合的平坦端。否则它就像魔术一样 :) - Martin Kristjansen
1
没问题:),我也发现了,如果随意放置第三度似乎不太合适。 - lhcgeneva

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接