# Fitting data with 3 linear fits

I have a dataset that can be easily approximated by a piecewise function composing of 3 linear functions, but i am unable to get an accurate fit once the third linear function is required. The model is highly accurate for two liner functions.

The solution is obvious, the code is difficult. Here is my attempt;

fitmodel[m_, x_, a_, c_, e_, n_, d_] :=
d + Piecewise[{{a (x - n), x < n}, {c x, n <= x < m}, {e (x - m),
m <= x}}]


This is the model i used, i plugged it in NonlinearModelFit with some reasonable starting values that will work for the other datasets i have;

sol = NonlinearModelFit[data3[[1]],
fitmodel[m, x, a, c, e, n,
d], {{a, -1}, {d, 500}, {c, 1}, {m, 100}, {e, -1}, {n, 300}}, x,
Method -> {NMinimize,
Method -> {"DifferentialEvolution", "ScalingFactor" -> 0.7,
"CrossProbability" -> 0.001,
"PostProcess" -> {FindMinimum, Method -> "QuasiNewton"}}}];


Everything after "Method" came from another stackexchange post that seemed to help while i was troubleshooting.

I would really appreciate a point in the right direction here, i have spent a while on this problem. I may also occasionally run into datasets that require 4 linear functions, and the middle region does not always have a slope of 1.

Thanks Will

• If you know whatever process generates the data has sudden shifts in slope, then piecewise linear regression works fine. But if you don't know that, then maybe quantile regression (mathematicaforprediction.wordpress.com/2014/01/01/…) by @AntonAntonov would be more robust for varying curve shapes.
– JimB
Commented Mar 5, 2019 at 22:27

The catch in piecewise linear regression is just making sure that the line segments intersect. One systematic approach is to define successive intercepts as the endpoint of the previous line segment.

Here we have 3 segments defined as

$$E(y|x $$E(y|c_1\leq x < c_2) = a_2 + b_2*(x-c_1)$$ $$E(y|x \geq c_2) = a_3 + b_3*(x-c_2)$$

Here is some made-up sample data:

data={{0, 422.808}, {5, 420.478}, {10, 419.885}, {15, 409.099}, {20, 401.991}, {25, 392.784}, {30, 393.007}, {35, 389.866}, {40, 387.422}, {45, 379.185}, {50, 372.092}, {55, 371.027}, {60, 366.818}, {65, 357.419}, {70, 354.197}, {75, 360.306}, {80, 344.244}, {85, 335.323}, {90, 341.295}, {95, 332.923}, {100,  324.843}, {105, 317.478}, {110, 327.442}, {115, 323.628}, {120, 318.41}, {125, 308.144}, {130, 301.034}, {135, 290.368}, {140, 297.193}, {145, 280.523}, {150, 280.346}, {155, 280.226}, {160, 275.515}, {165, 256.107}, {170, 265.278}, {175, 256.207}, {180, 258.653}, {185, 250.39}, {190, 248.119}, {195, 243.037}, {200,  229.993}, {205, 233.597}, {210, 225.621}, {215, 225.956}, {220, 225.194}, {225, 202.891}, {230, 205.367}, {235, 195.868}, {240, 198.521}, {245, 190.676}, {250, 190.554}, {255, 186.289}, {260, 179.3}, {265, 169.817}, {270, 176.787}, {275, 167.459}, {280, 159.373}, {285, 148.818}, {290, 151.855}, {295, 153.478}, {300, 134.617}, {305, 131.247}, {310, 124.339}, {315, 117.13}, {320, 112.433}, {325, 118.728}, {330, 120.256}, {335, 121.561}, {340, 121.885}, {345, 120.332}, {350, 119.723}, {355, 121.307}, {360, 118.366}, {365, 126.8}, {370, 112.267}, {375, 129.264}, {380, 120.966}, {385, 118.512}, {390, 128.474}, {395, 111.953}, {400, 121.195}, {405, 109.736}, {410, 122.929}, {415, 124.296}, {420, 114.717}, {425, 113.587}, {430, 127.551}, {435, 118.699}, {440, 113.722}, {445, 121.305}, {450, 119.947}, {455, 116.325}, {460, 112.804}, {465, 121.835}, {470, 113.748}, {475, 113.579}, {480, 124.282}, {485, 118.15}, {490, 117.994}, {495, 117.481}, {500, 121.803}, {505, 122.779}, {510, 117.996}, {515, 120.409}, {520, 111.087}, {525, 122.412}, {530, 117.491}, {535, 117.528}, {540, 114.733}, {545, 116.949}, {550, 120.648}, {555, 123.045}, {560, 114.907}, {565, 119.423}, {570, 126.76}, {575, 129.451}, {580, 117.285}, {585, 123.121}, {590, 118.529}, {595, 113.142}, {600, 114.442}, {605, 118.918}, {610, 125.887}, {615, 116.258}, {620, 111.013}, {625, 114.41}, {630, 96.3528}, {635, 104.718}, {640, 106.042}, {645, 96.0105}, {650, 88.3762}, {655, 87.1887}, {660, 82.0769}, {665, 79.3052}, {670, 76.4389}, {675, 76.9792}, {680, 70.0337}, {685, 71.3829}, {690, 67.4005}, {695, 50.7625}, {700, 43.9277}, {705, 50.1198}, {710, 44.768}, {715, 42.2054}, {720, 45.803}, {725, 38.1361}, {730, 20.7952}, {735, 26.0742}, {740, 25.5816}, {745, 18.6975}, {750, 13.4672}};


Choose intercepts a2 and a3 as the prediction at each cut-point c1 and c2. (This is so the line segments intersect at the cut-points.)

a2 = a1 + b1 c1
a3 = a2 + b2 c2

(* Piecewise function *)
f = (a1 + b1 x) Boole[x < c1] + (a2 + b2 (x - c1)) Boole[c1 <= x < c2] +
(a3 + b3 (x - c2)) Boole[x >= c2]

nlm = NonlinearModelFit[data,
f, {{a1, 400}, {b1, -1}, {b2, 0}, {b3, -1}, {c1, 300}, {c2, 600}}, x]
Show[ListPlot[data], Plot[nlm[x], {x, 0, 750}, PlotStyle -> Red]]