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

У меня есть данные, представленные в коде, которые имеют отрицательный и положительный наклон, как показано на рисунке:

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

Используя код, примененный в этом посте, установите кривую для данных, состоящих из двух разных режимов, я создал этот код. Он работает для одинаковых наклонов, как положительных, так и отрицательных, но когда один положительный, а другой отрицательный, он не может правильно разместить линии.

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

Вывод

Для разных наклонов: введите здесь описание изображения

Для одного и того же наклона оба отрицательных (отлично работает и для положительных наклонов):

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

У меня есть два вопроса:

  1. Как применить кусочно-линейные подгонки для таких случаев в Python?
  2. Как расширить его до трех и более режимов?

person Tom Kurushingal    schedule 30.04.2015    source источник
comment
Вы пробовали предложение в моем ответе ниже (с использованием замаскированных областей)?   -  person Saullo G. P. Castro    schedule 03.05.2015
comment
Он работает для положительных и отрицательных наклонов. Но когда они одного наклона, это не так.   -  person Tom Kurushingal    schedule 03.05.2015
comment
но когда наклоны равны, вам нужна кусочная подгонка кривой?   -  person Saullo G. P. Castro    schedule 03.05.2015
comment
Я имел в виду оба положительных или оба отрицательных.   -  person Tom Kurushingal    schedule 03.05.2015
comment
Я только что отредактировал свой ответ, показав, что он работает в обоих случаях.   -  person Saullo G. P. Castro    schedule 03.05.2015


Ответы (2)


Вы можете использовать замаскированные области для кусочных функций:

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

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

Второй тест с положительным и отрицательным наклоном (данные из вашего примера):

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

person Saullo G. P. Castro    schedule 01.05.2015
comment
Вероятно, вы допустили ошибку в out[mask] = a*x[mask] + b, так как это вернет только один элемент. Я попробую этот подход, хотя. - person Johncowk; 14.04.2020

То, что у вас есть, в основном подходит для конкретной проблемы и данных, которые вы представляете, но это неправильный способ решения этой проблемы в целом. Его нельзя легко расширить для более широкого круга обстоятельств, например, нескольких сегментов, данных с более сложными переходами между линейными сегментами, ситуаций, когда вам нужно настроить определенные аспекты подгонки и т. д. Основная причина заключается в том, что вы обрабатываете простая конкретная задача с сильными ограничениями (подгонка нескольких непересекающихся отрезков) наиболее общим и сложным способом (многопараметрическая подгонка большой размерности). Помимо других проблем, это обобщение затруднит попадание подгонки в правильную область пространства параметров. Это проблема простых локализованных подгонок, которую вы пытаетесь решить с помощью сложного обобщенного глобального решения. Я вижу привлекательность, но вряд ли это сработает для чего-либо, кроме игрушечных примеров.

Тем не менее, то, что у вас есть, будет работать для игрушечного примера, если вы начнете с правильного знака для склонов. По крайней мере, это работает, начиная с (1,1,-1,1) и (10,10,-10,10), но вам также необходимо знать их, учитывая ваше определение two_lines, так что я на самом деле не предполагаю ничего, чего вы не знали. Кроме того, вам нужно определить two_lines, чтобы он мог соответствовать (ваш оригинал имел нисходящий треугольник, а не восходящий, что также, вероятно, почему он работал для двух линий с «отрицательным наклоном» - не потому, что они имеют отрицательный наклон, а потому что они может соответствовать вашему исходному определению).

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

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

Очевидная альтернатива — просто двигаться вдоль кривой с помощью независимых кусочно-линейных подгонок. Этот подход прост, быстр, гибок и, вероятно, лучше соответствует вашей интуиции. Этот подход также легко расширяется до бесконечного числа сегментов (тогда как ваше глобальное решение, вероятно, достигает максимума в два), а также позволяет не путать подгонки к линейному участку с участками кривой, которые не являются идеально линейными (например, , закругленные переходы).

person tom10    schedule 01.05.2015
comment
Что, если у меня есть тысяча наборов разных линий? - person Tom Kurushingal; 02.05.2015
comment
Если они выглядят примерно так, я предполагаю, что это сработает. В целом, однако, вы задали здесь много вопросов по этой теме, и каждый раз, когда вы отвечаете на один из них, вы действуете так, как будто это не так, говоря, что ваша реальная проблема более сложна. Есть две вещи: 1) для SO, если вы хотите добиться прогресса и понравиться, вам нужно задать вопрос и относиться к вопросу, который вы задаете, как будто это ваш настоящий вопрос; 2) для разных задач подгонки требуются разные подходы, в основном зависящие от количества параметров и того, как выглядят подгонки в пространстве параметров (например, можно ли использовать достойный градиент?). - person tom10; 02.05.2015
comment
@nxkryptor: В целом, подбор со многими свободными параметрами затруднен. Рассмотрим три строки, поэтому 3 * 2 + 2 = 8 параметров, и скажем, вы хотите попробовать 100 значений для каждого параметра, чтобы отобразить пространство, тогда это 10 ** 16 значений выборки просто для начала. Это много вычислений, чтобы просто уместить три строки. - person tom10; 03.05.2015
comment
@nxkryptor: Чтобы было ясно, я предлагаю решить эту проблему совершенно по-другому, и я с удовольствием отвечу, как, по моему мнению, это должно быть решено. Это легко (хотя и менее элегантно). Детали будут иметь значение, поэтому я хотел бы, чтобы вопрос был сформулирован с реальной проблемой, которую вы пытаетесь решить, определенно упрощенной из-за постороннего беспорядка, но такой, чтобы, если бы на вопрос, который вы написали, был дан ответ, вы были бы удовлетворены ответом без других ограничения добавлены постфактум и т. д. Но путь, по которому вы сейчас идете, насколько я понимаю, совершенно неверен. - person tom10; 03.05.2015
comment
@nxkryptor: вопрос, который следует задать себе: если треугольник в вашем примере имеет закругленную вершину, хотите ли вы, чтобы подгонка к прямым сегментам была близкой (локальное решение), или вы хотите, чтобы подгонка к прямым сегментам была быть выключенным, чтобы подгонка также пыталась оптимизировать подгонку закругленной части, стягивая скаты вниз (глобальное решение)? - person tom10; 03.05.2015