如何对 Python 中具有正斜率和负斜率的直线应用分段线性拟合?
How to apply piecewise linear fit for a line with both positive and negative slopes in Python?
我在代码中提供的数据具有负斜率和正斜率,如图所示:
使用此 post Fit a curve for data made up of two distinct regimes 中应用的代码,我创建了此代码。它适用于相同的斜率,无论是正斜率还是负斜率,但当一个正斜率和另一个负斜率时,它无法正确拟合线条。
from scipy import optimize
from scipy import optimize, interpolate
from scipy.optimize import curve_fit
import matplotlib.pyplot as plt
import numpy as np
x = np.array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20])
y = np.array([4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 22, 20, 18, 16, 14, 12, 10, 8, 6, 4])
def two_lines(x, a, b, c, d):
one = a*x + b
two = c*x + d
return np.maximum(one, two)
'''Compute approximate slope and intercept of the two lines'''
poly_low = np.polyfit(x[0:int(0.5*(len(x) + 1))], y[0:int(0.5*(len(x) + 1))], deg=1)
poly_high = np.polyfit(x[int(0.5*(len(x) + 1)):len(x)], y[int(0.5*(len(x) + 1)):len(x)], deg=1)
# This part of the code credit goes to askewchan
pw0 = (poly_low[0], poly_low[1], poly_high[0], poly_high[1]) # a guess for slope, intercept, slope, intercept
pw, cov = curve_fit(two_lines, x, y, pw0)
crossover = (pw[3] - pw[1]) / (pw[0] - pw[2])
figure = plt.figure(figsize=(5.15, 5.15))
figure.clf()
plot = plt.subplot(111)
plt.plot(x, y, 'o', x, two_lines(x, *pw), '-')
plot.set_ylabel('Y', labelpad = 6)
plot.set_xlabel('X', labelpad = 6)
plt.show()
输出
对于不同的坡度:
对于相同的负斜率(也适用于正斜率):
我有两个问题:
- 如何对 Python 中的此类情况应用分段线性拟合?
- 如何扩展到三个或更多的政权?
您可以将屏蔽区域用于分段函数:
def two_lines(x, a, b, c, d):
out = np.empty_like(x)
mask = x < 10
out[mask] = a*x[mask] + b
out[~mask] = c*x[~mask] + d
return out
第一次测试有两个不同的正斜率:
x = np.array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
17, 18, 19, 20])
y = np.array([4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 25, 26, 27, 28, 29,
30, 31, 32, 33, 34])
具有正斜率和负斜率的第二次测试(来自您的示例的数据):
您所拥有的基本上适用于您所呈现的特定问题和数据,但更普遍地解决此问题是错误的方法。它不能轻易扩展到更广泛的情况,例如,多个段、线性段之间具有更复杂转换的数据、需要调整拟合的特定方面的情况等。主要原因是你处理一个以最一般和最困难的方式(高维、多参数拟合)解决简单的特定和高度受限的问题(拟合多个非重叠线段)。除其他问题外,这种泛化将使拟合更难落在参数 space 的正确区域。 这是一个关于简单的局部拟合的问题,您正试图用一个困难的通用全局解决方案来解决。我看到了吸引力,但它不太可能适用于玩具示例。
就是说,只要以正确的斜坡标志开始,您所拥有的就可以用于玩具示例。至少它从 (1,1,-1,1)
和 (10,10,-10,10)
开始有效,但根据您对 two_lines
的定义,您还需要知道这些,所以我并不是真的假设您不知道的任何事情。此外,您需要定义 two_lines
以便它可以匹配(您的原始三角形具有向下三角形而不是向上三角形,这也可能是它适用于两条 "negative sloped" 线的原因 - 不是因为它们是负斜率但是因为它们 可以 与您的原始定义相匹配)。
from scipy import optimize
from scipy import optimize, interpolate
from scipy.optimize import curve_fit
import matplotlib.pyplot as plt
import numpy as np
x = np.array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20])
y = np.array([4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 22, 20, 18, 16, 14, 12, 10, 8, 6, 4])
def two_lines(x, a, b, c, d):
one = a*x + b
two = c*x + d
return np.minimum(one, two)
pw, cov = curve_fit(two_lines, x, y, (10, 10, -10, 10))
figure = plt.figure(figsize=(5.15, 5.15))
figure.clf()
plot = plt.subplot(111)
plt.plot(x, y, 'o')
plt.plot(x, two_lines(x, *pw), '-')
plot.set_ylabel('Y', labelpad = 6)
plot.set_xlabel('X', labelpad = 6)
plt.show()
明显的替代方法是使用独立的分段线性拟合沿着曲线移动。这种方法简单、快速、灵活,可能更符合您的直觉。这种方法也很容易扩展到无限数量的段(而您的全局解决方案可能最多为两个),并且还允许线性部分的拟合不会被不完全线性的曲线部分弄乱(例如,圆角过渡)。
我在代码中提供的数据具有负斜率和正斜率,如图所示:
使用此 post Fit a curve for data made up of two distinct regimes 中应用的代码,我创建了此代码。它适用于相同的斜率,无论是正斜率还是负斜率,但当一个正斜率和另一个负斜率时,它无法正确拟合线条。
from scipy import optimize
from scipy import optimize, interpolate
from scipy.optimize import curve_fit
import matplotlib.pyplot as plt
import numpy as np
x = np.array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20])
y = np.array([4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 22, 20, 18, 16, 14, 12, 10, 8, 6, 4])
def two_lines(x, a, b, c, d):
one = a*x + b
two = c*x + d
return np.maximum(one, two)
'''Compute approximate slope and intercept of the two lines'''
poly_low = np.polyfit(x[0:int(0.5*(len(x) + 1))], y[0:int(0.5*(len(x) + 1))], deg=1)
poly_high = np.polyfit(x[int(0.5*(len(x) + 1)):len(x)], y[int(0.5*(len(x) + 1)):len(x)], deg=1)
# This part of the code credit goes to askewchan
pw0 = (poly_low[0], poly_low[1], poly_high[0], poly_high[1]) # a guess for slope, intercept, slope, intercept
pw, cov = curve_fit(two_lines, x, y, pw0)
crossover = (pw[3] - pw[1]) / (pw[0] - pw[2])
figure = plt.figure(figsize=(5.15, 5.15))
figure.clf()
plot = plt.subplot(111)
plt.plot(x, y, 'o', x, two_lines(x, *pw), '-')
plot.set_ylabel('Y', labelpad = 6)
plot.set_xlabel('X', labelpad = 6)
plt.show()
输出
对于不同的坡度:
对于相同的负斜率(也适用于正斜率):
我有两个问题:
- 如何对 Python 中的此类情况应用分段线性拟合?
- 如何扩展到三个或更多的政权?
您可以将屏蔽区域用于分段函数:
def two_lines(x, a, b, c, d):
out = np.empty_like(x)
mask = x < 10
out[mask] = a*x[mask] + b
out[~mask] = c*x[~mask] + d
return out
第一次测试有两个不同的正斜率:
x = np.array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
17, 18, 19, 20])
y = np.array([4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 25, 26, 27, 28, 29,
30, 31, 32, 33, 34])
具有正斜率和负斜率的第二次测试(来自您的示例的数据):
您所拥有的基本上适用于您所呈现的特定问题和数据,但更普遍地解决此问题是错误的方法。它不能轻易扩展到更广泛的情况,例如,多个段、线性段之间具有更复杂转换的数据、需要调整拟合的特定方面的情况等。主要原因是你处理一个以最一般和最困难的方式(高维、多参数拟合)解决简单的特定和高度受限的问题(拟合多个非重叠线段)。除其他问题外,这种泛化将使拟合更难落在参数 space 的正确区域。 这是一个关于简单的局部拟合的问题,您正试图用一个困难的通用全局解决方案来解决。我看到了吸引力,但它不太可能适用于玩具示例。
就是说,只要以正确的斜坡标志开始,您所拥有的就可以用于玩具示例。至少它从 (1,1,-1,1)
和 (10,10,-10,10)
开始有效,但根据您对 two_lines
的定义,您还需要知道这些,所以我并不是真的假设您不知道的任何事情。此外,您需要定义 two_lines
以便它可以匹配(您的原始三角形具有向下三角形而不是向上三角形,这也可能是它适用于两条 "negative sloped" 线的原因 - 不是因为它们是负斜率但是因为它们 可以 与您的原始定义相匹配)。
from scipy import optimize
from scipy import optimize, interpolate
from scipy.optimize import curve_fit
import matplotlib.pyplot as plt
import numpy as np
x = np.array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20])
y = np.array([4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 22, 20, 18, 16, 14, 12, 10, 8, 6, 4])
def two_lines(x, a, b, c, d):
one = a*x + b
two = c*x + d
return np.minimum(one, two)
pw, cov = curve_fit(two_lines, x, y, (10, 10, -10, 10))
figure = plt.figure(figsize=(5.15, 5.15))
figure.clf()
plot = plt.subplot(111)
plt.plot(x, y, 'o')
plt.plot(x, two_lines(x, *pw), '-')
plot.set_ylabel('Y', labelpad = 6)
plot.set_xlabel('X', labelpad = 6)
plt.show()
明显的替代方法是使用独立的分段线性拟合沿着曲线移动。这种方法简单、快速、灵活,可能更符合您的直觉。这种方法也很容易扩展到无限数量的段(而您的全局解决方案可能最多为两个),并且还允许线性部分的拟合不会被不完全线性的曲线部分弄乱(例如,圆角过渡)。