You can use numpy.piecewise() to create the piecewise function and then use curve_fit(), Here is the code
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))
the output:

For an N parts fitting, please reference segments_fit.ipynb
Answer from HYRY on Stack OverflowYou can use numpy.piecewise() to create the piecewise function and then use curve_fit(), Here is the code
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))
the output:

For an N parts fitting, please reference segments_fit.ipynb
You can use pwlf to perform continuous piecewise linear regression in Python. This library can be installed using pip.
There are two approaches in pwlf to perform your fit:
- You can fit for a specified number of line segments.
- You can specify the x locations where the continuous piecewise lines should terminate.
Let's go with approach 1 since it's easier, and will recognize the 'gradient change point' that you are interested in.
I notice two distinct regions when looking at the data. Thus it makes sense to find the best possible continuous piecewise line using two line segments. This is approach 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. ]
The first line segment runs from [1., 5.99819559], while the second line segment runs from [5.99819559, 15.]. Thus the gradient change point you asked for would be 5.99819559.
We can plot these results using the predict function.
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()

Python library for segmented regression (a.k.a. piecewise regression) - Data Science Stack Exchange
How to do piecewise functions in python?
python - piecewise linear function with numpy.piecewise - Stack Overflow
python - Fit points with a continuous, piecewise linear function with minimum number of points for each segment - Cross Validated
Videos
ยป pip install pwlf
numpy.piecewise can do this.
piecewise(x, condlist, funclist, *args, **kw)
Evaluate a piecewise-defined function.
Given a set of conditions and corresponding functions, evaluate each function on the input data wherever its condition is true.
An example is given on SO here. For completeness, here is an example:
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))
The method proposed by Vito M. R. Muggeo[1] is relatively simple and efficient. It works for a specified number of segments, and for a continuous function. The positions of the breakpoints are iteratively estimated by performing, for each iteration, a segmented linear regression allowing jumps at the breakpoints. From the values of the jumps, the next breakpoint positions are deduced, until there are no more discontinuity (jumps).
"the process is iterated until possible convergence, which is not, in general, guaranteed"
In particular, the convergence or the result may depends on the first estimation of the breakpoints.
This is the method used in the R Segmented package.
Here is an implementation in 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
Example:
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, V. M. (2003). Estimating regression models with unknown breakpoints. Statistics in medicine, 22(19), 3055-3071.
Hi guys,
I'm using numpy and plotly to graph piecewise functions. I previously used matlab. I need help to essentially translate matlab piecewise functions into something I can use in python. Here's what I had in matlab:
Constant1 = 5; Constant2 = 10; Formula1 = @(x,y) x*Constant1; Formula2 = @(x,y) x*Constant2; MainFormula = @(x,y) [Formula1(x,y)*(y<=5) + Formula2(x,y)*(y>5)];
So what this would essentially do is make 'MainFormula' equal to 'Formula1' for all values of y less than or equal to 5, and it would make 'MainFormula' equal to 'Formula2' for all values of y greater than 5.
I would really appreciate any help with this.
Thanks
