如何在Python中应用分段线性拟合?

Tom*_*gal 48 python numpy curve-fitting scipy piecewise

我试图将分段线性拟合拟合为数据集,如图1所示

在此输入图像描述

这个数字是通过设置线条获得的.我试图使用代码应用分段线性拟合:

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


x = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ,11, 12, 13, 14, 15])
y = np.array([5, 7, 9, 11, 13, 15, 28.92, 42.81, 56.7, 70.59, 84.47, 98.36, 112.25, 126.14, 140.03])


def linear_fit(x, a, b):
    return a * x + b
fit_a, fit_b = optimize.curve_fit(linear_fit, x[0:5], y[0:5])[0]
y_fit = fit_a * x[0:7] + fit_b
fit_a, fit_b = optimize.curve_fit(linear_fit, x[6:14], y[6:14])[0]
y_fit = np.append(y_fit, fit_a * x[6:14] + fit_b)


figure = plt.figure(figsize=(5.15, 5.15))
figure.clf()
plot = plt.subplot(111)
ax1 = plt.gca()
plot.plot(x, y, linestyle = '', linewidth = 0.25, markeredgecolor='none', marker = 'o', label = r'\textit{y_a}')
plot.plot(x, y_fit, linestyle = ':', linewidth = 0.25, markeredgecolor='none', marker = '', label = r'\textit{y_b}')
plot.set_ylabel('Y', labelpad = 6)
plot.set_xlabel('X', labelpad = 6)
figure.savefig('test.pdf', box_inches='tight')
plt.close()    
Run Code Online (Sandbox Code Playgroud)

但是这给了我图中图形的拟合.2,我试着玩这些值,但没有变化,我无法得到合适的上线.对我来说最重要的要求是如何让Python获得渐变变化点.本质上,我希望Python能够在适当的范围内识别和拟合两个线性拟合.如何在Python中完成?

在此输入图像描述

HYR*_*YRY 46

您可以使用numpy.piecewise()创建分段函数然后使用curve_fit(),这是代码

from scipy import optimize
import matplotlib.pyplot as plt
import numpy as np
%matplotlib inline

x = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ,11, 12, 13, 14, 15], dtype=float)
y = np.array([5, 7, 9, 11, 13, 15, 28.92, 42.81, 56.7, 70.59, 84.47, 98.36, 112.25, 126.14, 140.03])

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])

p , e = optimize.curve_fit(piecewise_linear, x, y)
xd = np.linspace(0, 15, 100)
plt.plot(x, y, "o")
plt.plot(xd, piecewise_linear(xd, *p))
Run Code Online (Sandbox Code Playgroud)

输出:

在此输入图像描述

  • 如何将其扩展为三个部分? (22认同)

hak*_*anc 17

您可以执行样条插值方案,以执行分段线性插值并找到曲线的转折点.二阶导数将在转折点处最高(对于单调增加的曲线),并且可以使用阶数> 2的样条插值来计算.

import numpy as np
import matplotlib.pyplot as plt
from scipy import interpolate

x = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ,11, 12, 13, 14, 15])
y = np.array([5, 7, 9, 11, 13, 15, 28.92, 42.81, 56.7, 70.59, 84.47, 98.36, 112.25, 126.14, 140.03])

tck = interpolate.splrep(x, y, k=2, s=0)
xnew = np.linspace(0, 15)

fig, axes = plt.subplots(3)

axes[0].plot(x, y, 'x', label = 'data')
axes[0].plot(xnew, interpolate.splev(xnew, tck, der=0), label = 'Fit')
axes[1].plot(x, interpolate.splev(x, tck, der=1), label = '1st dev')
dev_2 = interpolate.splev(x, tck, der=2)
axes[2].plot(x, dev_2, label = '2st dev')

turning_point_mask = dev_2 == np.amax(dev_2)
axes[2].plot(x[turning_point_mask], dev_2[turning_point_mask],'rx',
             label = 'Turning point')
for ax in axes:
    ax.legend(loc = 'best')

plt.show()
Run Code Online (Sandbox Code Playgroud)

转折点和分段线性插值


Cha*_*kel 13

您可以使用pwlf在Python中执行连续的分段线性回归。可以使用pip安装该库。

pwlf中有两种方法可以执行您的拟合:

  1. 您可以适合指定数量的线段。
  2. 您可以指定连续分段线应终止的x位置。

让我们继续使用方法1,因为它更简单,并且可以识别您感兴趣的“渐变更改点”。

在查看数据时,我注意到两个不同的区域。因此,有必要使用两个线段找到最佳的连续分段线。这是方法1。

import numpy as np
import matplotlib.pyplot as plt
import pwlf

x = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15])
y = np.array([5, 7, 9, 11, 13, 15, 28.92, 42.81, 56.7, 70.59,
              84.47, 98.36, 112.25, 126.14, 140.03])

my_pwlf = pwlf.PiecewiseLinFit(x, y)
breaks = my_pwlf.fit(2)
print(breaks)
Run Code Online (Sandbox Code Playgroud)

[1. 5.99819559 15.]

第一行段从[1.,5.99819559]开始,而第二行段从[5.99819559,15]开始。因此,您要求的梯度变化点将是5.99819559。

我们可以使用预测函数绘制这些结果。

x_hat = np.linspace(x.min(), x.max(), 100)
y_hat = my_pwlf.predict(x_hat)

plt.figure()
plt.plot(x, y, 'o')
plt.plot(x_hat, y_hat, '-')
plt.show()
Run Code Online (Sandbox Code Playgroud)

结果拟合


Mar*_*hke 13

在此输入图像描述

该方法用于Scikit-Learn应用分段线性回归。如果您的点受到噪音的影响,您可以使用此功能。与执行巨大的优化任务(任何超过 3 个参数的任务)相比,它更快稳健、更通用。scip.optimizecurve_fit

import numpy as np
import matplotlib.pylab as plt
from sklearn.tree import DecisionTreeRegressor
from sklearn.linear_model import LinearRegression

# parameters for setup
n_data = 20

# segmented linear regression parameters
n_seg = 3

np.random.seed(0)
fig, (ax0, ax1) = plt.subplots(1, 2)

# example 1
#xs = np.sort(np.random.rand(n_data))
#ys = np.random.rand(n_data) * .3 + np.tanh(5* (xs -.5))

# example 2
xs = np.linspace(-1, 1, 20)
ys = np.random.rand(n_data) * .3 + np.tanh(3*xs)

dys = np.gradient(ys, xs)

rgr = DecisionTreeRegressor(max_leaf_nodes=n_seg)
rgr.fit(xs.reshape(-1, 1), dys.reshape(-1, 1))
dys_dt = rgr.predict(xs.reshape(-1, 1)).flatten()

ys_sl = np.ones(len(xs)) * np.nan
for y in np.unique(dys_dt):
    msk = dys_dt == y
    lin_reg = LinearRegression()
    lin_reg.fit(xs[msk].reshape(-1, 1), ys[msk].reshape(-1, 1))
    ys_sl[msk] = lin_reg.predict(xs[msk].reshape(-1, 1)).flatten()
    ax0.plot([xs[msk][0], xs[msk][-1]],
             [ys_sl[msk][0], ys_sl[msk][-1]],
             color='r', zorder=1)

ax0.set_title('values')
ax0.scatter(xs, ys, label='data')
ax0.scatter(xs, ys_sl, s=3**2, label='seg lin reg', color='g', zorder=5)
ax0.legend()

ax1.set_title('slope')
ax1.scatter(xs, dys, label='data')
ax1.scatter(xs, dys_dt, label='DecisionTree', s=2**2)
ax1.legend()

plt.show()
Run Code Online (Sandbox Code Playgroud)

怎么运行的

  • 计算每个点的斜率
  • 使用决策树对相似的坡度进行分组(右图)
  • 对原始数据中的每组进行线性回归


Mar*_*ani 7

您正在寻找线性树。它们是以通用和自动化的方式应用分段线性拟合(也适用于多变量和分类上下文)的最佳方法。

线性树与决策树不同,因为它们计算在叶子中拟合简单线性模型的线性近似(而不是常数近似)。

对于我的一个项目,我开发了Linear-tree一个 Python 库,用于在叶子上构建具有线性模型的模型树。

在此输入图像描述

线性树被开发为可与 scikit-learn 完全集成。

from sklearn.linear_model import *
from lineartree import LinearTreeRegressor, LinearTreeClassifier

# REGRESSION
regr = LinearTreeRegressor(base_estimator=LinearRegression())
regr.fit(X, y)

# CLASSIFICATION
clf = LinearTreeClassifier(base_estimator=RidgeClassifier())
clf.fit(X, y)
Run Code Online (Sandbox Code Playgroud)

LinearTreeRegressorLinearTreeClassifier作为 scikit-learn 提供BaseEstimator。它们是包装器,可在拟合线性估计器的数据上构建决策树sklearn.linear_model。中的所有可用模型都sklearn.linear_model可以用作线性估计器。

比较决策树与线性树:

在此输入图像描述

在此输入图像描述

考虑到您的数据,概括非常简单:

from sklearn.linear_model import LinearRegression
from lineartree import LinearTreeRegressor
import numpy as np
import matplotlib.pyplot as plt

X = np.array(
    [1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ,11, 12, 13, 14, 15]
    ).reshape(-1,1)
y = np.array(
    [5, 7, 9, 11, 13, 15, 28.92, 42.81, 56.7, 70.59, 84.47, 98.36, 112.25, 126.14, 140.03]
    )

model = LinearTreeRegressor(base_estimator=LinearRegression())
model.fit(X, y)

plt.plot(X, y, ".", label='TRUE')
plt.plot(X, model.predict(X), label='PRED')
plt.legend()
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述


pin*_*eng 5

两个变更点的示例。如果需要,仅根据此示例测试更多的更改点。

np.random.seed(9999)
x = np.random.normal(0, 1, 1000) * 10
y = np.where(x < -15, -2 * x + 3 , np.where(x < 10, x + 48, -4 * x + 98)) + np.random.normal(0, 3, 1000)
plt.scatter(x, y, s = 5, color = u'b', marker = '.', label = 'scatter plt')

def piecewise_linear(x, x0, x1, b, k1, k2, k3):
    condlist = [x < x0, (x >= x0) & (x < x1), x >= x1]
    funclist = [lambda x: k1*x + b, lambda x: k1*x + b + k2*(x-x0), lambda x: k1*x + b + k2*(x-x0) + k3*(x - x1)]
    return np.piecewise(x, condlist, funclist)

p , e = optimize.curve_fit(piecewise_linear, x, y)
xd = np.linspace(-30, 30, 1000)
plt.plot(x, y, "o")
plt.plot(xd, piecewise_linear(xd, *p))
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明