How do I fit a line?

There are three methods for fitting a line to a set of (x,y) points - these are

Spline and Smooth

The ability to generate a smooth line using cubic spline interpolations is provided by the Graph object and is available as a GSL function. There is also an interactive dialog box that allows you to access this GSL function. This dialog is accessed via the Interpolation menu of the DataManager.

If you have plotted your data as a line graph in Gsharp. e.g.

x = 1:4;
y = rnd(4);
create Viewport page_1.viewport_1;
create Domain page_1.viewport_1.domain_1;
create Graph page_1.viewport_1.domain_1.graph_1
( XuNgraphType = "line",
  XuNxData = "x",
  XuNyData = "y"
);

Then you can smooth the line by setting a smoothing factor of between 1 and 9 and the number of points between each input point to be one or more. These options can be found in the Chart bundle of resources of the graph object. In GSL you would use:

set page_1.viewport_1.domain_1.graph_1
 ( XuNsmoothingFactor = 4,
   XuNpointsPerSegment = 9
 );

The same smoothed line can be generated in GSL. First you must work out the x values to use:

newx = range(1//4,31);
# 31 = 4 original points plus 27 new ones

and then you must use the smooth function to calculate the y values.

newy = smooth(x,y,newx,4);

Now plot newy against newx instead of y against x. Note that the smoothed curve will represent the positions of the original points, but may not necessarily pass through them. 

An alternative to smooth() is the spline function which is only available as a GSL function (and from the interpolation menu of the DataManager). The curve created by spline will pass through the original points. The fourth argument to the spline function is the tension and it must be in the range 1 to 10:

x = 1:4;
y = rnd(4);
newx = range(1//4,31);
newy = spline(x,y,newx,4);
create Viewport page_1.viewport_1;
create Domain page_1.viewport_1.domain_1;
create Graph page_1.viewport_1.domain_1.graph_1
( XuNgraphType = "line",
  XuNxData = "newx",
  XuNyData = "newy"
);

Polyfit

polyfit() calculates a polynomial of up to degree five that best fits the input data. A polynomial of degree 1 is a line, of degree 2 is a parabola.

With polyfit(), there are no restrictions on the order of the input data.

As with all interpolation routines it is possible to specify the locations at which the interpolation is calculated. For most interpolations it is best to choose enough points to make a smooth curve, but for polyfit I choose only enough points so that I can calculate the co-efficients of the polynomial which I can then use in further calculations or to construct any required line. See more after the following example.

Example 1 - Linear Regression

x = rnd(9);
y = rnd(9);

nodes = 0//.5//1;
poly = polyfit(x,y,nodes,1);

create Viewport page_1.view
 ( XuNxRatio = 1,
   XuNyRatio = 1
 );
create Domain page_1.view.dom;
create Graph page_1.view.dom.graph
 ( XuNgraphType = "scatter",
   XuNsymbol = "x",
   XuNxData = "x",
   XuNyData = "y"
 );
create Graph page_1.view.dom.graph2
 ( XuNgraphType = "line",
   XuNxData = "nodes",
   XuNyData = "poly"
 );

The formula for a line is y = f(x) = mx + c

f(0) = 0*m + c = c therefore c = f(0)
f(1) = 1*m + c = m + f(0) therefore m = f(1) - c

In the above example we have calculate where x=0, .5 and 1.
So f(0) is poly[1], f(.5) is poly[2] and f(1) is poly[3]

c = poly[1];
m = poly[3] - c;

Once the co-efficients have been found it is possible to calculate the corresponding points on our fitted line to our input points and then calculate the standard deviation:

newy = m*x + c;
std_dev = std(y-newy);

Calculated values can then be displayed on the plot:

create Note page_1.view.dom.Note1
 ( XuNposition = (50,70),
   XuNtext = "'y = '+m+'*x + '+c"
 );
create Note page_1.view.dom.Note2
 ( XuNposition = (50,65),
   XuNtext = "'standard deviation = '+std_dev"
 );

Polynomials of degree 2

A similar technique can be used for polynomials of degree 2:

y = f(x) = a*x2 + b*x + c

f(0) = c
f(1) = a + b + c
f(-1) = a - b + c

c = f(0)
b = ( f(1) - f(-1) ) / 2
a = f(1) - b - c

which in GSL can be written as:

nodes = -1//0//1;
poly = polyfit(x,y,nodes,2);
c = poly[2];
b = (poly[3] - poly[1]) / 2;
a = poly[3] - b - c;

Polynomials of degree 3 or more

The effort to solve these equations increase as the degree of the polynomial increases. Luckily Gsharp includes a function, matsolve() which will do the work for us.

We have the following equations

x1^3*a + x1^2*b +x1*c + d = f(x1)
x2^3*a + x2^2*b +x2*c + d = f(x2)
x3^3*a + x3^2*b +x3*c + d = f(x3)
x4^3*a + x4^2*b +x4*c + d = f(x4)

which we need to solve for any x1, x2, x3 and x4.

This can be expressed in matrix notation as:

x1^3 x1^2 x1 1   a   f(x1)
x2^3 x2^2 x2 1 * b = f(x2)
x3^3 x3^2 x3 1   c   f(x3)
x4^3 x4^2 x4 1   d   f(x4)

if      a   f(x1)
    M * b = f(x2)
        c   f(x3)
        d   f(x4)

then  a          f(x1)
      b = M^-1 * f(x2)
      c          f(x3)
      d          f(x4)

or even coeffs = matsolve(M,f(x1)//f(x2)//f(x3)//f(x4))

The following GSL can be used to solve these equations (this function can be found in the library libgen.gsl):

function float PolyFit(float x, float y, float deg)
         float nodes, poly, coeffs, M;
         
         nodes = range(x,6);
         poly = polyfit(x,y,nodes,deg);
         
         nodes = transpose(nodes);
         M = transpose(nodes^5 // nodes^4 // nodes^3 // nodes^2 // nodes // (1,1,1,1,1,1));
         coeffs = matsolve(M,poly);
         
         return slicex(coeffs,6-deg:6);
         endfunction 

Example 2 - Polynomial of degree 5

include "$UNIDIR/lib/libgen.gsl";
x = rnd(9);
y = rnd(9);

coeffs = PolyFit(x,y,5);

newx = range(x,100);
newy = coeffs[1]*(newx^4) + coeffs[2]*(newx^3) + coeffs[3]*(newx^2) + coeffs[4]*newx + coeffs[5];

reset;
create Viewport page_1.view
 ( XuNxRatio = 1,
   XuNyRatio = 1
 );
create Domain page_1.view.dom;
create Graph page_1.view.dom.graph
 ( XuNgraphType = "scatter",
   XuNsymbol = "x",
   XuNxData = "x",
   XuNyData = "y"
 );
create Graph page_1.view.dom.graph2
 ( XuNgraphType = "line",
   XuNxData = "newx",
   XuNyData = "newy"
 );


Linear

It is also possible to perform a linear interpolation on (x,y) points. Calculate points will lie on a straight line connecting consecutive points. This form of interpolation is available throught the GSL function linear