JSXGraph logo
JSXGraph
JSXGraph share

Share

Least-squares line fitting
QR code
<iframe 
    src="https://jsxgraph.org/share/iframe/least-squares-line-fitting" 
    style="border: 1px solid black; overflow: hidden; width: 550px; aspect-ratio: 55 / 65;" 
    name="JSXGraph example: Least-squares line fitting" 
    allowfullscreen
></iframe>
This code has to
<div id="board-0-wrapper" class="jxgbox-wrapper " style="width: 100%; ">
   <div id="board-0" class="jxgbox" style="aspect-ratio: 1 / 1; width: 100%;" data-ar="1 / 1"></div>
</div>

<script type = "text/javascript"> 
    /*
    This example is licensed under a 
    Creative Commons Attribution ShareAlike 4.0 International License.
    https://creativecommons.org/licenses/by-sa/4.0/
    
    Please note you have to mention 
    The Center of Mobile Learning with Digital Technology
    in the credits.
    */
    
    const BOARDID = 'board-0';

    var board = JXG.JSXGraph.initBoard(BOARDID, { boundingbox: [-5, 5, 5, -5], keepaspectratio: true, axis: true });
    board.suspendUpdate();
    
    // Experiments with lines and circles:
    
    // 1) Plot random points on a line disturbed by a random factor
    var i, p1 = [], angle, xr, yr, delta = 0.1;
    
    // Random points are constructed which lie roughly on a line
    // defined by y = 0.3*x+1.
    // delta*0.5 is the maximal distance in y-direction of the random
    // points from the line.
    board.suspendUpdate();
    for (i = 0; i < 100; i++) {
        yr = 10 * (Math.random() - 0.5);
        xr = 0. * yr + delta * (Math.random() - 0.5);
        p1.push(board.create('point', [xr, yr], { withLabel: false }));
    }
    
    // 2) Plot random points on a circle disturbed by a random factor
    var i, p2 = [], angle, co, si, delta = 0.2;
    
    // Random points are constructed which lie roughly on a circle
    // of radius 4 having the origin as center.
    // delta*0.5 is the maximal distance in x- and y- direction of the random
    // points from the circle line.
    for (i = 0; i < 100; i++) {
        angle = Math.random() * 2 * Math.PI;
    
        co = 4 * Math.cos(angle) + delta * (Math.random() - 0.5);
        si = 4 * Math.sin(angle) + delta * (Math.random() - 0.5);
        p2.push(board.create('point', [co + 2, si - 1], { withLabel: false }));
    }
    board.unsuspendUpdate();
    
    //
    // bestFit, the best-fitting circle or line is found by least-squares fitting.
    //
    var bestFit = function(p) {
        var i, j, r = [], rbar = [], x = [], y = [], z = [], A = [[0, 0, 0], [0, 0, 0], [0, 0, 0]], n, d,
              eigen, minIndex, minE, ev, c, xm, ym, zm, radius;
        n = p.length;
        for (i = 0; i < n; i++) {
            r.push([1.0, p[i].X(), p[i].Y()]);
            d = r[i][0] * r[i][0] + r[i][1] * r[i][1] + r[i][2] * r[i][2];
            r[i][0] = 1.0 - r[i][0] / d;
            r[i][1] /= d;
            r[i][2] /= d;
        }
    
        for (j = 0; j < 3; j++) {
            for (i = 0, d = 0; i < n; i++) {
                d += r[i][j];
            }
            d /= n;
            rbar[j] = d;
            for (i = 0; i < n; i++) {
                r[i][j] -= d;
            }
        }
        for (i = 0; i < n; i++) {
            A[0][0] += r[i][0] * r[i][0];
            A[0][1] += r[i][0] * r[i][1];
            A[0][2] += r[i][0] * r[i][2];
            A[1][0] += r[i][1] * r[i][0];
            A[1][1] += r[i][1] * r[i][1];
            A[1][2] += r[i][1] * r[i][2];
            A[2][0] += r[i][2] * r[i][0];
            A[2][1] += r[i][2] * r[i][1];
            A[2][2] += r[i][2] * r[i][2];
        }
    
        eigen = JXG.Math.Numerics.Jacobi(A);
        minIndex = 0;
        minE = eigen[0][0][0];
        for (j = 1; j < 3; j++) {
            if (eigen[0][j][j] < minE) {
                minIndex = j;
                minE = eigen[0][j][j];
            }
        }
        ev = [eigen[1][0][minIndex], eigen[1][1][minIndex], eigen[1][2][minIndex]];
        c = -(rbar[0] * ev[0] + rbar[1] * ev[1] + rbar[2] * ev[2]);
    
        xm = -ev[1];
        ym = -ev[2];
        zm = 2.0 * (c + ev[0]);
        //console.log(c, c+ev[0]);
    
        // If c is close to zero, the best fittting object is a line.
        // The best threshold parameter has yet to be determined.
        // At the moment it is set to 0.01.
        if (Math.abs(c) < 0.01) {
            board.create('line', [zm, xm, ym], { strokeColor: 'green' });
        } else {
            radius = Math.sqrt((xm * xm + ym * ym - 2 * c * zm) / (zm * zm));
            board.create('circle', [[zm, xm, ym], radius]);
        }
    }; // end of bestFit()
    
    bestFit(p1);
    bestFit(p2);
 </script> 
/*
This example is licensed under a 
Creative Commons Attribution ShareAlike 4.0 International License.
https://creativecommons.org/licenses/by-sa/4.0/

Please note you have to mention 
The Center of Mobile Learning with Digital Technology
in the credits.
*/

const BOARDID = 'your_div_id'; // Insert your id here!

var board = JXG.JSXGraph.initBoard(BOARDID, { boundingbox: [-5, 5, 5, -5], keepaspectratio: true, axis: true });
board.suspendUpdate();

// Experiments with lines and circles:

// 1) Plot random points on a line disturbed by a random factor
var i, p1 = [], angle, xr, yr, delta = 0.1;

// Random points are constructed which lie roughly on a line
// defined by y = 0.3*x+1.
// delta*0.5 is the maximal distance in y-direction of the random
// points from the line.
board.suspendUpdate();
for (i = 0; i < 100; i++) {
    yr = 10 * (Math.random() - 0.5);
    xr = 0. * yr + delta * (Math.random() - 0.5);
    p1.push(board.create('point', [xr, yr], { withLabel: false }));
}

// 2) Plot random points on a circle disturbed by a random factor
var i, p2 = [], angle, co, si, delta = 0.2;

// Random points are constructed which lie roughly on a circle
// of radius 4 having the origin as center.
// delta*0.5 is the maximal distance in x- and y- direction of the random
// points from the circle line.
for (i = 0; i < 100; i++) {
    angle = Math.random() * 2 * Math.PI;

    co = 4 * Math.cos(angle) + delta * (Math.random() - 0.5);
    si = 4 * Math.sin(angle) + delta * (Math.random() - 0.5);
    p2.push(board.create('point', [co + 2, si - 1], { withLabel: false }));
}
board.unsuspendUpdate();

//
// bestFit, the best-fitting circle or line is found by least-squares fitting.
//
var bestFit = function(p) {
    var i, j, r = [], rbar = [], x = [], y = [], z = [], A = [[0, 0, 0], [0, 0, 0], [0, 0, 0]], n, d,
          eigen, minIndex, minE, ev, c, xm, ym, zm, radius;
    n = p.length;
    for (i = 0; i < n; i++) {
        r.push([1.0, p[i].X(), p[i].Y()]);
        d = r[i][0] * r[i][0] + r[i][1] * r[i][1] + r[i][2] * r[i][2];
        r[i][0] = 1.0 - r[i][0] / d;
        r[i][1] /= d;
        r[i][2] /= d;
    }

    for (j = 0; j < 3; j++) {
        for (i = 0, d = 0; i < n; i++) {
            d += r[i][j];
        }
        d /= n;
        rbar[j] = d;
        for (i = 0; i < n; i++) {
            r[i][j] -= d;
        }
    }
    for (i = 0; i < n; i++) {
        A[0][0] += r[i][0] * r[i][0];
        A[0][1] += r[i][0] * r[i][1];
        A[0][2] += r[i][0] * r[i][2];
        A[1][0] += r[i][1] * r[i][0];
        A[1][1] += r[i][1] * r[i][1];
        A[1][2] += r[i][1] * r[i][2];
        A[2][0] += r[i][2] * r[i][0];
        A[2][1] += r[i][2] * r[i][1];
        A[2][2] += r[i][2] * r[i][2];
    }

    eigen = JXG.Math.Numerics.Jacobi(A);
    minIndex = 0;
    minE = eigen[0][0][0];
    for (j = 1; j < 3; j++) {
        if (eigen[0][j][j] < minE) {
            minIndex = j;
            minE = eigen[0][j][j];
        }
    }
    ev = [eigen[1][0][minIndex], eigen[1][1][minIndex], eigen[1][2][minIndex]];
    c = -(rbar[0] * ev[0] + rbar[1] * ev[1] + rbar[2] * ev[2]);

    xm = -ev[1];
    ym = -ev[2];
    zm = 2.0 * (c + ev[0]);
    //console.log(c, c+ev[0]);

    // If c is close to zero, the best fittting object is a line.
    // The best threshold parameter has yet to be determined.
    // At the moment it is set to 0.01.
    if (Math.abs(c) < 0.01) {
        board.create('line', [zm, xm, ym], { strokeColor: 'green' });
    } else {
        radius = Math.sqrt((xm * xm + ym * ym - 2 * c * zm) / (zm * zm));
        board.create('circle', [[zm, xm, ym], radius]);
    }
}; // end of bestFit()

bestFit(p1);
bestFit(p2);
<jsxgraph width="100%" aspect-ratio="1 / 1" title="Least-squares line fitting" description="This construction was copied from JSXGraph examples database: BTW HERE SHOULD BE A GENERATED LINKuseGlobalJS="false">
   /*
   This example is licensed under a 
   Creative Commons Attribution ShareAlike 4.0 International License.
   https://creativecommons.org/licenses/by-sa/4.0/
   
   Please note you have to mention 
   The Center of Mobile Learning with Digital Technology
   in the credits.
   */
   
   var board = JXG.JSXGraph.initBoard(BOARDID, { boundingbox: [-5, 5, 5, -5], keepaspectratio: true, axis: true });
   board.suspendUpdate();
   
   // Experiments with lines and circles:
   
   // 1) Plot random points on a line disturbed by a random factor
   var i, p1 = [], angle, xr, yr, delta = 0.1;
   
   // Random points are constructed which lie roughly on a line
   // defined by y = 0.3*x+1.
   // delta*0.5 is the maximal distance in y-direction of the random
   // points from the line.
   board.suspendUpdate();
   for (i = 0; i < 100; i++) {
       yr = 10 * (Math.random() - 0.5);
       xr = 0. * yr + delta * (Math.random() - 0.5);
       p1.push(board.create('point', [xr, yr], { withLabel: false }));
   }
   
   // 2) Plot random points on a circle disturbed by a random factor
   var i, p2 = [], angle, co, si, delta = 0.2;
   
   // Random points are constructed which lie roughly on a circle
   // of radius 4 having the origin as center.
   // delta*0.5 is the maximal distance in x- and y- direction of the random
   // points from the circle line.
   for (i = 0; i < 100; i++) {
       angle = Math.random() * 2 * Math.PI;
   
       co = 4 * Math.cos(angle) + delta * (Math.random() - 0.5);
       si = 4 * Math.sin(angle) + delta * (Math.random() - 0.5);
       p2.push(board.create('point', [co + 2, si - 1], { withLabel: false }));
   }
   board.unsuspendUpdate();
   
   //
   // bestFit, the best-fitting circle or line is found by least-squares fitting.
   //
   var bestFit = function(p) {
       var i, j, r = [], rbar = [], x = [], y = [], z = [], A = [[0, 0, 0], [0, 0, 0], [0, 0, 0]], n, d,
             eigen, minIndex, minE, ev, c, xm, ym, zm, radius;
       n = p.length;
       for (i = 0; i < n; i++) {
           r.push([1.0, p[i].X(), p[i].Y()]);
           d = r[i][0] * r[i][0] + r[i][1] * r[i][1] + r[i][2] * r[i][2];
           r[i][0] = 1.0 - r[i][0] / d;
           r[i][1] /= d;
           r[i][2] /= d;
       }
   
       for (j = 0; j < 3; j++) {
           for (i = 0, d = 0; i < n; i++) {
               d += r[i][j];
           }
           d /= n;
           rbar[j] = d;
           for (i = 0; i < n; i++) {
               r[i][j] -= d;
           }
       }
       for (i = 0; i < n; i++) {
           A[0][0] += r[i][0] * r[i][0];
           A[0][1] += r[i][0] * r[i][1];
           A[0][2] += r[i][0] * r[i][2];
           A[1][0] += r[i][1] * r[i][0];
           A[1][1] += r[i][1] * r[i][1];
           A[1][2] += r[i][1] * r[i][2];
           A[2][0] += r[i][2] * r[i][0];
           A[2][1] += r[i][2] * r[i][1];
           A[2][2] += r[i][2] * r[i][2];
       }
   
       eigen = JXG.Math.Numerics.Jacobi(A);
       minIndex = 0;
       minE = eigen[0][0][0];
       for (j = 1; j < 3; j++) {
           if (eigen[0][j][j] < minE) {
               minIndex = j;
               minE = eigen[0][j][j];
           }
       }
       ev = [eigen[1][0][minIndex], eigen[1][1][minIndex], eigen[1][2][minIndex]];
       c = -(rbar[0] * ev[0] + rbar[1] * ev[1] + rbar[2] * ev[2]);
   
       xm = -ev[1];
       ym = -ev[2];
       zm = 2.0 * (c + ev[0]);
       //console.log(c, c+ev[0]);
   
       // If c is close to zero, the best fittting object is a line.
       // The best threshold parameter has yet to be determined.
       // At the moment it is set to 0.01.
       if (Math.abs(c) < 0.01) {
           board.create('line', [zm, xm, ym], { strokeColor: 'green' });
       } else {
           radius = Math.sqrt((xm * xm + ym * ym - 2 * c * zm) / (zm * zm));
           board.create('circle', [[zm, xm, ym], radius]);
       }
   }; // end of bestFit()
   
   bestFit(p1);
   bestFit(p2);
</jsxgraph>

Least-squares line fitting

This little JXSGraph application finds the line or the circle which is the best fit for given set of points. If the resulting line is green, it is a straight line. If the line is blue, it is a circle.
// Define the id of your board in BOARDID

var board = JXG.JSXGraph.initBoard(BOARDID, { boundingbox: [-5, 5, 5, -5], keepaspectratio: true, axis: true });
board.suspendUpdate();

// Experiments with lines and circles:

// 1) Plot random points on a line disturbed by a random factor
var i, p1 = [], angle, xr, yr, delta = 0.1;

// Random points are constructed which lie roughly on a line
// defined by y = 0.3*x+1.
// delta*0.5 is the maximal distance in y-direction of the random
// points from the line.
board.suspendUpdate();
for (i = 0; i < 100; i++) {
    yr = 10 * (Math.random() - 0.5);
    xr = 0. * yr + delta * (Math.random() - 0.5);
    p1.push(board.create('point', [xr, yr], { withLabel: false }));
}

// 2) Plot random points on a circle disturbed by a random factor
var i, p2 = [], angle, co, si, delta = 0.2;

// Random points are constructed which lie roughly on a circle
// of radius 4 having the origin as center.
// delta*0.5 is the maximal distance in x- and y- direction of the random
// points from the circle line.
for (i = 0; i < 100; i++) {
    angle = Math.random() * 2 * Math.PI;

    co = 4 * Math.cos(angle) + delta * (Math.random() - 0.5);
    si = 4 * Math.sin(angle) + delta * (Math.random() - 0.5);
    p2.push(board.create('point', [co + 2, si - 1], { withLabel: false }));
}
board.unsuspendUpdate();

//
// bestFit, the best-fitting circle or line is found by least-squares fitting.
//
var bestFit = function(p) {
    var i, j, r = [], rbar = [], x = [], y = [], z = [], A = [[0, 0, 0], [0, 0, 0], [0, 0, 0]], n, d,
          eigen, minIndex, minE, ev, c, xm, ym, zm, radius;
    n = p.length;
    for (i = 0; i < n; i++) {
        r.push([1.0, p[i].X(), p[i].Y()]);
        d = r[i][0] * r[i][0] + r[i][1] * r[i][1] + r[i][2] * r[i][2];
        r[i][0] = 1.0 - r[i][0] / d;
        r[i][1] /= d;
        r[i][2] /= d;
    }

    for (j = 0; j < 3; j++) {
        for (i = 0, d = 0; i < n; i++) {
            d += r[i][j];
        }
        d /= n;
        rbar[j] = d;
        for (i = 0; i < n; i++) {
            r[i][j] -= d;
        }
    }
    for (i = 0; i < n; i++) {
        A[0][0] += r[i][0] * r[i][0];
        A[0][1] += r[i][0] * r[i][1];
        A[0][2] += r[i][0] * r[i][2];
        A[1][0] += r[i][1] * r[i][0];
        A[1][1] += r[i][1] * r[i][1];
        A[1][2] += r[i][1] * r[i][2];
        A[2][0] += r[i][2] * r[i][0];
        A[2][1] += r[i][2] * r[i][1];
        A[2][2] += r[i][2] * r[i][2];
    }

    eigen = JXG.Math.Numerics.Jacobi(A);
    minIndex = 0;
    minE = eigen[0][0][0];
    for (j = 1; j < 3; j++) {
        if (eigen[0][j][j] < minE) {
            minIndex = j;
            minE = eigen[0][j][j];
        }
    }
    ev = [eigen[1][0][minIndex], eigen[1][1][minIndex], eigen[1][2][minIndex]];
    c = -(rbar[0] * ev[0] + rbar[1] * ev[1] + rbar[2] * ev[2]);

    xm = -ev[1];
    ym = -ev[2];
    zm = 2.0 * (c + ev[0]);
    //console.log(c, c+ev[0]);

    // If c is close to zero, the best fittting object is a line.
    // The best threshold parameter has yet to be determined.
    // At the moment it is set to 0.01.
    if (Math.abs(c) < 0.01) {
        board.create('line', [zm, xm, ym], { strokeColor: 'green' });
    } else {
        radius = Math.sqrt((xm * xm + ym * ym - 2 * c * zm) / (zm * zm));
        board.create('circle', [[zm, xm, ym], radius]);
    }
}; // end of bestFit()

bestFit(p1);
bestFit(p2);

license

This example is licensed under a Creative Commons Attribution ShareAlike 4.0 International License.
Please note you have to mention The Center of Mobile Learning with Digital Technology in the credits.