绘制分段拟合非线性数据
Plotting a piece-wise fit to non-linear data
我有一个类似于 的问题。我有一个数据集,我想将几个分段函数拟合到其中,然后绘制结果。
数据在下面用红色绘制。
为了提供一些上下文,y 值表示电机转动 x 度需要多少毫秒。我已将原始值上传到 this Pastebin.
我现在想分段拟合三个函数:
- 适合数据开始的多项式,其中电机正在加速到最大速度。
- 达到最大速度时的线性拟合。
- 多项式拟合然后电机关闭,并减速。
到目前为止,我已经尝试使用下面显示的代码对两个线性函数执行分段拟合。鉴于数据的样子,我希望看到一个斜率跟随数据从原点到大约 ms=550
,然后从那里看到第二条线 运行 平行于 x 轴。
然而,这不是我得到的:
在我尝试使用三个函数执行分段拟合之前,我首先想了解为什么我得到这个图而不是我所期望的。
所以我的问题是:
- 任何人都可以解释如何更正我的代码以使其适合两个线性函数吗?
- 如何扩展我的代码以使用三个函数绘制分段拟合?
用于创建上图的代码如下:
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 Whosebug
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])
2。问题:你需要重新定义piecewise_linear
,现在它有三个部分,随便改(我只是举了2次,1次和3次多项式的例子)。
#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')
我有一个类似于
数据在下面用红色绘制。
为了提供一些上下文,y 值表示电机转动 x 度需要多少毫秒。我已将原始值上传到 this Pastebin.
我现在想分段拟合三个函数:
- 适合数据开始的多项式,其中电机正在加速到最大速度。
- 达到最大速度时的线性拟合。
- 多项式拟合然后电机关闭,并减速。
到目前为止,我已经尝试使用下面显示的代码对两个线性函数执行分段拟合。鉴于数据的样子,我希望看到一个斜率跟随数据从原点到大约 ms=550
,然后从那里看到第二条线 运行 平行于 x 轴。
然而,这不是我得到的:
在我尝试使用三个函数执行分段拟合之前,我首先想了解为什么我得到这个图而不是我所期望的。
所以我的问题是:
- 任何人都可以解释如何更正我的代码以使其适合两个线性函数吗?
- 如何扩展我的代码以使用三个函数绘制分段拟合?
用于创建上图的代码如下:
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 Whosebug
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])
2。问题:你需要重新定义piecewise_linear
,现在它有三个部分,随便改(我只是举了2次,1次和3次多项式的例子)。
#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')