Estoy buscando una biblioteca de Python que pueda realizar una regresión segmentada (también conocida como regresión por partes) .
Ejemplo :
Estoy buscando una biblioteca de Python que pueda realizar una regresión segmentada (también conocida como regresión por partes) .
Ejemplo :
Respuestas:
numpy.piecewise
puede hacer esto
por partes (x, condlist, funclist, * args, ** kw)
Evaluar una función definida por partes.
Dado un conjunto de condiciones y funciones correspondientes, evalúe cada función en los datos de entrada siempre que su condición sea verdadera.
Un ejemplo se da en SO aquí . Para completar, aquí hay un ejemplo:
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))
El método propuesto por Vito MR Muggeo [1] es relativamente simple y eficiente. Funciona para un número específico de segmentos y para una función continua. Las posiciones de los puntos de ruptura se estiman iterativamente realizando, para cada iteración, una regresión lineal segmentada que permite saltos en los puntos de ruptura. A partir de los valores de los saltos, se deducen las siguientes posiciones de punto de interrupción, hasta que no haya más discontinuidad (saltos).
"el proceso se repite hasta una posible convergencia, lo que, en general, no está garantizado"
En particular, la convergencia o el resultado puede depender de la primera estimación de los puntos de corte.
Este es el método utilizado en el paquete R Segmented .
Aquí hay una implementación en 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
Ejemplo:
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). Estimación de modelos de regresión con puntos de corte desconocidos. Estadísticas en medicina, 22 (19), 3055-3071.
He estado buscando lo mismo, y desafortunadamente parece que no hay ninguno en este momento. Algunas sugerencias sobre cómo proceder se pueden encontrar en esta pregunta anterior .
Alternativamente, puede buscar en algunas bibliotecas R, por ejemplo, segmentado, SiZer, strucchange, y si algo funciona, intente incrustar el código R en python con rpy2 .
Edición para agregar un enlace a py-earth , "Una implementación de Python de Splines de regresión adaptativa multivariante de Jerome Friedman".
Hay una publicación de blog con una implementación recursiva de regresión por partes. Esa solución se ajusta a la regresión discontinua.
Si no está satisfecho con el modelo discontinuo y desea un asentamiento continuo, le propondría buscar su curva en base a curvas en forma de k
L, utilizando Lasso para la dispersión:
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()
Este código le devolverá un vector de coeficientes estimados:
[ 0.57 0. 0. 0. 0. 0.825 0. 0. 0. 0. 0. ]
Debido al enfoque de Lasso, es escaso: el modelo encontró exactamente un punto de quiebre entre 10 posibles. Los números 0.57 y 0.825 corresponden a 0.5 y 1.25 en el verdadero DGP. Aunque no están muy cerca, las curvas ajustadas son:
Este enfoque no le permite estimar el punto de interrupción exactamente. Pero si su conjunto de datos es lo suficientemente grande, puede jugar con diferentes k
(tal vez ajustarlo mediante validación cruzada) y estimar el punto de interrupción con la suficiente precisión.