Я шукаю бібліотеку Python, яка може виконувати сегментовану регресію (також кусочно регресію) .
Приклад :
Я шукаю бібліотеку Python, яка може виконувати сегментовану регресію (також кусочно регресію) .
Приклад :
Відповіді:
numpy.piecewise
може це зробити.
кусочно (x, умовний список, функціональний список, * 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]: Муггео, В. М. (2003). Оцінка регресійних моделей з невідомими точками переривання. Статистика в медицині, 22 (19), 3055-3071.
Я шукав те саме, і, на жаль, здається, що наразі його немає. Деякі пропозиції щодо того, як діяти, можна знайти в цьому попередньому запитанні .
Крім того, ви можете заглянути в деякі R бібліотеки, наприклад, сегментовані, SiZer, strucchange, і якщо щось там працює, ви спробуйте вставити R-код у python з rpy2 .
Редагуючи, щоб додати посилання на py-землю , "Реалізація Python у багатоваріантних адаптивних регресійних сплайсах Джерома Фрідмана".
Є повідомлення в блозі з рекурсивною реалізацією кускової регресії. Це рішення підходить до переривчастої регресії.
Якщо ви не задоволені переривчастою моделлю і хочете безперервного розміщення, я б запропонував шукати вашу криву на основі кривих у формі k
Г, використовуючи Lasso для розрідженості:
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
(можливо, налаштуйте його за допомогою перехресної перевірки) і оцінити точку перерви досить точно.