# Can one force a linear regression fit to go through an arbitrary point?

I have some data. I want to find the linear regression of the data, but force it to include a certain point. (This question is a corollary to this old question; in that question, the user wished to force the fit to include the origin $(0, 0)$.)

Here is my data, and I want the linear regression to go through the point $(0, 100)$.

data = {{0, 100}, {4, 86}, {6, 80}, {7, 73}, {8, 66}, {13, 49}, {14, 44},
{15, 41}, {16, 33}, {17, 28}};


I know that I can fit the data to a line using

Fit[data, {1, x}, x]


or

Normal[LinearModelFit[data, x, x]]


which both yield 102.167 - 4.21667 x, which of course does not go through $(0,100)$.

fit = Fit[data, {1, x}, x];
Show[{
ListPlot[data, PlotRange -> All, Joined -> True, Mesh -> Full,
Frame -> True],
Plot[fit, {x, 0, 25}, PlotRange -> All, PlotStyle -> Red]
}, AxesOrigin -> {0, 0}] I thought that perhaps Fit[data, {100, x}, x] would force Fit to use a y-intercept of 100, but instead Fit simply scales the constant term, yielding the same result:

102.167 - 4.21667 x

• The fixedLine[] routine in my answer should be able to do this. Anyway: pt = {100, 0}; \[FormalA] \[FormalX] + {-\[FormalA], 1}.pt /. FindFit[TranslationTransform[-pt][data], \[FormalA] \[FormalX], \[FormalA], \[FormalX]] – J. M.'s torpor Mar 10 '18 at 22:38
• Not Mathematica specific, but I've used bespoke regularization terms to do impose some pretty weird priors on linear regression models. You could, for example, penalize models which don't pass through the point you want very heavily. An even weirder thing I've done is penalize models in which the coefficients themselves were nonlinear. (I.e. I had a prior that w1,w2,w3 should be ~1,.6,.3 and I wanted to force the regression to justify deviation from that prior.) If Mathematica has a way to allow for regularization (which I expect it does), then this would be a way to do what you want. – Tyler Mar 11 '18 at 0:10
• Almost always when forcing a line through (0,0) there really isn't a data point or a measurement at (0,0). Is there really a measurement at (100,0) ? – JimB Mar 11 '18 at 2:13
• if you're foring it to go though a point it's not a linear regression any more, it's just some sort of fit (possibly least-squares) for the slope of the line. – Jasen Mar 11 '18 at 4:00
• Add a single observation dummy variable. – Sean O'Farrell Mar 11 '18 at 4:07

## tl;dr

Make your data go through $(0,0)$ by subtracting the required point and fit a line through the origin, then add back the required point:

datap = # - {0, 100} & /@ data;
Fit[ datap, {x}, x] + 100


## Annoying maths:

You have to include the condition as an external constraint and the standard Fit[...] does not allow for that. A linear regression is simply trying to minimize the expression (assuming identical uncertainties $\sigma_k$ for ease of notation)

$$\sum_k (a x_k + b - y_k)^2$$

wrt the parameters $a$ and $b$. Your external constraint has the form

$$x_0 a + b - y_0 = 0$$

with $x_0=0$ and $y_0 = 100$ in your special case. The constraint, being zero, can be added the above expression to minimize

$$\sum_k (a x_k + b - y_k)^2 - \lambda ( a x_0 + b - y_0 )$$

This addition will couple the (otherwise independent) derivatives wrt $a$ and $b$

$$\sum_k 2 x_k (a x_k + b - y_k) = \lambda x_0$$ $$\sum_k 2 (a x_k + b - y_k) = \lambda$$

which, together with the constraint, can be easily (and analytically!) solved for a and b. Use the constraint to express $b$

$$b = -a x_0 + y_0$$

which leads to

$$\sum_k 2 x_k (a (x_k-x_0) - (y_k-y_0) ) = \lambda x_0$$ $$\sum_k 2 (a (x_k-x_0) - (y_k-y_0) ) = \lambda$$

or

$$\sum_k 2 (x_k-x_0) (a (x_k-x_0) - (y_k-y_0) ) = 0$$

which is, somewhat unsurprisingly, exactly the equation you would have gotten if you had started with the dataset $x'_k = x_k-x_0, y'_k = y_k-y_0$ and the model $$y= a x$$

So you should get the required result using

datap = # - {0, 100} & /@ data;
Fit[ datap, {x}, x] + 100


which indeed results in

100 - 4.05 x


Use IncludeConstantBasis -> False to go through $(100,0)$:

Normal[LinearModelFit[data, x - 100, x, IncludeConstantBasis -> False]]
(*  -0.679766 (-100 + x)  *)


Or LinearOffsetFunction -> (100 &) for $(0, 100)$:

Normal[LinearModelFit[data, x, x, IncludeConstantBasis -> False,
LinearOffsetFunction -> (100 &)]]
(*  100 - 4.05 x  *)


Or both for an arbitary point:

SeedRandom;
pt = RandomChoice[data]  (* pick a data point *)
(*  {15, 41}  *)

Normal[LinearModelFit[data, x - pt[], x,
IncludeConstantBasis -> False, LinearOffsetFunction -> (pt[] &)]]
(*  41 - 4.02727 (-15 + x)  *)


Use NonlinearModelFit (everything that is linear is also nonlinear, you know...):

model = m x + b;
model = model /. Solve[m x + b == 0 /. x -> 100, b];
f = NonlinearModelFit[data, {model}, {m}, x]
f["BestFitParameters"]

{m -> -0.679766}

Show[
ListLinePlot[data],
Plot[f[x], {x, 0, 100}, PlotStyle -> ColorData],
PlotRange -> All
] I suppose you meant $(0,100)$ as a point to go through but you only get what you ask for ;)

• "You are technically correct - the best kind of correct" – Chris K Mar 10 '18 at 23:21
• But you could be completely correct with adding something like {x0, y0} = {0, 100}; model = model /. Solve[(model /. x -> x0) == y0, b]. – JimB Mar 11 '18 at 2:10

Here's a hacky solution: use the Weights option in LinearModelFit to give the point at (0,100) a huge weight.

fit2 = Normal[LinearModelFit[data, x, x,
Weights -> Table[If[i == 1, 10^20, 1], {i, Length[data]}]]]

(* 100. - 4.05 x *)

Show[{ListPlot[data, PlotRange -> All, Joined -> True, Mesh -> Full,
Frame -> True],
Plot[fit2, {x, 0, 25}, PlotRange -> All, PlotStyle -> Red]},
AxesOrigin -> {0, 0}] We can simply add any constraint in the second argument of NonlinearModelFit:

ClearAll[a, b, model]
model[x_] := a + b x;


Unconstrained model:

nlmf0 = Normal @ NonlinearModelFit[data, model[x], {a, b}, x]

102.167 - 4.21667 x


Force the fit to go through {0, 100}:

nlmf1 = Normal @ NonlinearModelFit[data, {model[x], model == 100}, {a, b}, x]

100. - 4.05 x


Force the fit to go through {5, 75}:

nlmf2 = Normal @ NonlinearModelFit[data, {model[x], model == 75}, {a, b}, x]

93.3182 - 3.66364 x Show[Plot[{nlmf0, nlmf1, nlmf2}, {x, 0, 20},
Epilog -> {Red, PointSize[Large], Point[{{0, 100}, {5, 75}}]},
PlotLegends -> {nlmf0, nlmf1, nlmf2}], ListPlot[data]]