Подтвердить что ты не робот

Как применить кусочно-линейную посадку в Python?

Я пытаюсь установить кусочно-линейную подгонку, как показано на рис .1 для набора данных

enter image description here

Эта цифра была получена путем установки на строках. Я попытался применить кусочно-линейную подгонку с использованием кода:

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

Но это дало мне установку формы на рис. 2, я пробовал играть со значениями, но без изменений я не могу получить соответствие верхней линии. Самое важное требование для меня - как я могу получить Python для получения точки изменения градиента. По существу я хочу, чтобы Python распознавал и вписывал два линейных набора в соответствующий диапазон. Как это можно сделать в Python?

enter image description here

4b9b3361

Ответ 1

Вы можете использовать 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))

вывод:

enter image description here

Ответ 2

Вы можете сделать сплайн-интерполяцию, чтобы выполнить кусочно-линейную интерполяцию и найти точку поворота кривой. Вторая производная будет самой высокой в ​​точке поворота (для монотонно возрастающей кривой) и может быть рассчитана с помощью сплайновой интерполяции порядкa > 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()

Turning point and piecewise linear interpolation

Ответ 3

Расширение ответа @binoy-pilakkat.

Вы должны использовать numpy.interp:

import numpy as np
import matplotlib.pyplot as plt

x = np.array(range(1,16), 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], dtype=float)

yinterp = np.interp(x, x, y) # simple as that

plt.plot(x, y, 'bo')
plt.plot(x, yinterp, 'g-')
plt.show()

введите описание изображения здесь

Ответ 4

Вы можете использовать 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)

[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()

The resulting fit

Ответ 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))

введите описание изображения здесь

Ответ 6

Используйте numpy.interp, который возвращает одномерную кусочно-линейную интерполяцию в функцию с заданными значениями в дискретных точках данных.

Ответ 7

кусочно работает тоже

from piecewise.regressor import piecewise
import numpy as np

x = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ,11, 12, 13, 14, 15,16,17,18], 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,120,112,110])

model = piecewise(x, y)

Оценить "модель":

FittedModel with segments:
* FittedSegment(start_t=1.0, end_t=7.0, coeffs=(2.9999999999999996, 2.0000000000000004))
* FittedSegment(start_t=7.0, end_t=16.0, coeffs=(-68.2972222222222, 13.888333333333332))
* FittedSegment(start_t=16.0, end_t=18.0, coeffs=(198.99999999999997, -5.000000000000001))

Ответ 8

Линейные выражения делают экстраполяцию и интерполируют все остальное.

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

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, 19, 15, 28.92, 42.81, 56.7, 79, 84.47, 98.36, 112.25, 126.14, 140.03])

def piecewise_linear(x, x0, y0):
    y = np.zeros(len(x))
    def get_slope(xi, xj, yi, yj):
        return (yj - yi)/(xj - xi)
    def get_intercept(m, xi, yi):
        return yi - m*xi
    def left_piecewise_linear(x, x0, y0):
        m_left = get_slope(x0[0], x0[1], y0[0], y0[1])
        b_left = get_intercept(m_left, x0[0], y0[0])
        return m_left * x + b_left
    def right_piecewise_linear(x, x0, y0):   
        m_right = get_slope(x0[-1], x0[-2], y0[-1], y0[-2])
        b_right = get_intercept(m_right, x0[-1], y0[-1])
        return m_right * x + b_right
    for i in range(0,len(x)):
        if x[i] < x0[0]:
            y[i] = left_piecewise_linear(x[i], x0, y0)
        elif x[i] > x0[-1]:
            y[i] = right_piecewise_linear(x[i], x0, y0)
        else:
            y[i] = np.interp(x[i], x0, y0)
    return y        

xd = np.linspace(-15, 25, 100)    
plt.plot(x, y, "o")
plt.plot(xd, piecewise_linear(xd, x, y))
plt.savefig('img.png')

Кусочно линейная подгонка с экстраполяцией