Я ищу библиотеку Python, которая может выполнять сегментированную регрессию (так называемая кусочная регрессия) .
Пример :
Я ищу библиотеку Python, которая может выполнять сегментированную регрессию (так называемая кусочная регрессия) .
Пример :
Ответы:
numpy.piecewise
могу сделать это
кусочно (x, condlist, funclist, * args, ** kw)
Оценить кусочно-определенную функцию.
Учитывая набор условий и соответствующих функций, оцените каждую функцию на входных данных, где бы ее условие не выполнялось.
Пример приведен на SO здесь . Для полноты вот пример:
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], 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, 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))
Метод, предложенный Vito MR Muggeo [1], относительно прост и эффективен. Он работает для определенного количества сегментов и для непрерывной функции. Позиции точек останова итеративно оцениваются путем выполнения для каждой итерации сегментированной линейной регрессии, позволяющей выполнять скачки в точках останова. Из значений переходов выводятся следующие позиции точек останова до тех пор, пока не исчезнут разрывы (переходы).
«процесс повторяется до возможной конвергенции, что, как правило, не гарантируется»
В частности, сходимость или результат могут зависеть от первой оценки точек останова.
Этот метод используется в пакете R Segmented .
Вот реализация в Python:
import numpy as np
from numpy.linalg import lstsq
ramp = lambda u: np.maximum( u, 0 )
step = lambda u: ( u > 0 ).astype(float)
def SegmentedLinearReg( X, Y, breakpoints ):
nIterationMax = 10
breakpoints = np.sort( np.array(breakpoints) )
dt = np.min( np.diff(X) )
ones = np.ones_like(X)
for i in range( nIterationMax ):
# Linear regression: solve A*p = Y
Rk = [ramp( X - xk ) for xk in breakpoints ]
Sk = [step( X - xk ) for xk in breakpoints ]
A = np.array([ ones, X ] + Rk + Sk )
p = lstsq(A.transpose(), Y, rcond=None)[0]
# Parameters identification:
a, b = p[0:2]
ck = p[ 2:2+len(breakpoints) ]
dk = p[ 2+len(breakpoints): ]
# Estimation of the next break-points:
newBreakpoints = breakpoints - dk/ck
# Stop condition
if np.max(np.abs(newBreakpoints - breakpoints)) < dt/5:
break
breakpoints = newBreakpoints
else:
print( 'maximum iteration reached' )
# Compute the final segmented fit:
Xsolution = np.insert( np.append( breakpoints, max(X) ), 0, min(X) )
ones = np.ones_like(Xsolution)
Rk = [ c*ramp( Xsolution - x0 ) for x0, c in zip(breakpoints, ck) ]
Ysolution = a*ones + b*Xsolution + np.sum( Rk, axis=0 )
return Xsolution, Ysolution
Пример:
import matplotlib.pyplot as plt
X = np.linspace( 0, 10, 27 )
Y = 0.2*X - 0.3* ramp(X-2) + 0.3*ramp(X-6) + 0.05*np.random.randn(len(X))
plt.plot( X, Y, 'ok' );
initialBreakpoints = [1, 7]
plt.plot( *SegmentedLinearReg( X, Y, initialBreakpoints ), '-r' );
plt.xlabel('X'); plt.ylabel('Y');
[1]: Muggeo, VM (2003). Оценка регрессионных моделей с неизвестными контрольными точками. Статистика в медицине, 22 (19), 3055-3071.
Я искал то же самое, и, к сожалению, в настоящее время его нет. Некоторые предложения о том, как поступить, можно найти в этом предыдущем вопросе .
В качестве альтернативы вы можете посмотреть некоторые библиотеки R, например, сегментированные, SiZer, strucchange, и если что-то там работает, попробуйте встроить код R в python с помощью rpy2 .
Редактирование, чтобы добавить ссылку на py-earth , «Реализация многомерного адаптивного регрессии Джерома Фридмана на Python».
В блоге есть рекурсивная реализация кусочной регрессии. Это решение соответствует прерывистой регрессии.
Если вы недовольны прерывистой моделью и хотите непрерывную настройку, я бы предложил поискать вашу кривую на основе k
L-образных кривых, используя лассо для разреженности:
import numpy as np
import matplotlib.pyplot as plt
from sklearn.linear_model import Lasso
# generate data
np.random.seed(42)
x = np.sort(np.random.normal(size=100))
y_expected = 3 + 0.5 * x + 1.25 * x * (x>0)
y = y_expected + np.random.normal(size=x.size, scale=0.5)
# prepare a basis
k = 10
thresholds = np.percentile(x, np.linspace(0, 1, k+2)[1:-1]*100)
basis = np.hstack([x[:, np.newaxis], np.maximum(0, np.column_stack([x]*k)-thresholds)])
# fit a model
model = Lasso(0.03).fit(basis, y)
print(model.intercept_)
print(model.coef_.round(3))
plt.scatter(x, y)
plt.plot(x, y_expected, color = 'b')
plt.plot(x, model.predict(basis), color='k')
plt.legend(['true', 'predicted'])
plt.xlabel('x')
plt.ylabel('y')
plt.title('fitting segmented regression')
plt.show()
Этот код вернет вам вектор оценочных коэффициентов:
[ 0.57 0. 0. 0. 0. 0.825 0. 0. 0. 0. 0. ]
Из-за подхода Лассо он редок: модель нашла ровно одну точку останова среди 10 возможных. Числа 0,57 и 0,825 соответствуют 0,5 и 1,25 в истинном DGP. Хотя они не очень близки, подгонка кривых:
Такой подход не позволяет точно оценить точку останова. Но если ваш набор данных достаточно большой, вы можете поиграть с другим k
(возможно, настроить его путем перекрестной проверки) и достаточно точно оценить точку останова.