Wolfram Research

Function Repository Resource:

OrthogonalLineFit

Source Notebook

Find the best fitting line with respect to orthogonal distance

Contributed by: Jan Mangaldan

ResourceFunction["OrthogonalLineFit"][data]

finds the best fitting orthogonal distance regression line to data.

Details

Orthogonal distance regression is also known as orthogonal least squares or total least squares.
The orthogonal distance regression line is the line for which its orthogonal distance from the given data is minimized.
ResourceFunction["OrthogonalLineFit"] returns an InfiniteLine object.

Examples

Basic Examples

Parameters for a 2D line:

In[1]:=
BlockRandom[SeedRandom[42, Method -> "ExtendedCA"];
 {p0, v0} = {RandomReal[{-2, 2}, 2], Normalize[RandomVariate[NormalDistribution[], 2]]}]
Out[1]=

Generate some points near the line:

In[2]:=
BlockRandom[SeedRandom[42, Method -> "ExtendedCA"];
 pts = Table[
   p0 + t v0 + RandomVariate[NormalDistribution[0, 1/15], 2], {t, RandomReal[1, 25]}]]
Out[2]=

Find the orthogonal distance regression line:

In[3]:=
ofit = ResourceFunction["OrthogonalLineFit"][pts]
Out[3]=

Compare the regression line and the true line with the data:

In[4]:=
Legended[Graphics[{{Directive[AbsolutePointSize[6], Brown], Point[pts]},
   {Directive[AbsoluteThickness[2], ColorData[97, 1]], ofit},
   {Directive[AbsoluteThickness[2], ColorData[97, 3]], InfiniteLine[p0, v0]}}, Frame -> True, PlotRangeClipping -> True],
 LineLegend[{ColorData[97, 1], ColorData[97, 3]}, {"orthogonal fit", "true line"}]]
Out[4]=

Parameters for a 3D line:

In[5]:=
BlockRandom[SeedRandom[42, Method -> "ExtendedCA"];
 {p0, v0} = {RandomReal[{-2, 2}, 3], Normalize[RandomVariate[NormalDistribution[], 3]]}]
Out[5]=

Generate some points near the line:

In[6]:=
BlockRandom[SeedRandom[42, Method -> "ExtendedCA"];
 pts = Table[
   p0 + t v0 + RandomVariate[NormalDistribution[0, 1/8], 3], {t, RandomReal[1, 25]}]]
Out[6]=

Find the orthogonal distance regression line:

In[7]:=
ofit = ResourceFunction["OrthogonalLineFit"][pts]
Out[7]=

Compare the regression line and the true line with the data:

In[8]:=
Legended[Graphics3D[{{Directive[AbsolutePointSize[6], Brown], Point[pts]},
   {Directive[AbsoluteThickness[2], ColorData[97, 1]], ofit},
   {Directive[AbsoluteThickness[2], ColorData[97, 3]], InfiniteLine[p0, v0]}}, Axes -> True],
 LineLegend[{ColorData[97, 1], ColorData[97, 3]}, {"orthogonal fit", "true line"}]]
Out[8]=

Scope

Here is a list of values:

In[9]:=
v = {1, 3, 7, 11, 19};

When coordinates are not given, OrthogonalLineFit assumes the values are to be paired up with 1, 2, …:

In[10]:=
ofit = ResourceFunction["OrthogonalLineFit"][v]
Out[10]=
In[11]:=
ListPlot[v, Epilog -> {ColorData[97, 3], ofit}]
Out[11]=

Properties and Relations

Here is some data:

In[12]:=
data = {{0., 1.}, {1., 0.}, {3., 2.}, {5., 4.}};

This computes the parameters for the orthogonal distance regression line using the definition:

In[13]:=
{pm, vm} = Partition[
  NArgMin[Total[
    RegionDistance[InfiniteLine[{px, py}, {vx, vy}], #]^2 & /@ data], {px, py, vx, vy}], 2]
Out[13]=

Use OrthogonalLineFit to get the best-fit line:

In[14]:=
ofit = ResourceFunction["OrthogonalLineFit"][data]
Out[14]=

Use RegionMember to compare the equations for the best fit line generated by both methods:

In[15]:=
Simplify[{RegionMember[InfiniteLine[pm, vm], {x, y}], RegionMember[ofit, {x, y}]}, {x, y} \[Element] Reals]
Out[15]=

Construct a function from the orthogonal fit:

In[16]:=
oFun[x_] = Expand[y /. First[Solve[Last[%], y]]]
Out[16]=

Compare the orthogonal fit with the conventional least-squares fit:

In[17]:=
lFun[x_] = Fit[data, {1, x}, x]
Out[17]=
In[18]:=
Plot[{oFun[x], lFun[x]}, {x, 0, 5}, Epilog -> {Directive[AbsolutePointSize[4], ColorData[97, 4]], Point[data]}, PlotLegends -> {"orthogonal fit", "least-squares fit"}]
Out[18]=

Resource History

Source Metadata

Related Resources

License Information