SMKBK1 commited on
Commit
3c4d62d
·
verified ·
1 Parent(s): 7b56641

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +42 -31
index.html CHANGED
@@ -520,41 +520,52 @@
520
 
521
  // --- MATHEMATICAL REgRESSION ENGINE ---
522
 
523
- // Linear regression
 
 
 
524
  function calculateLinearFit(points) {
525
  if (points.length < 2) return null;
526
- const n = points.length;
527
- let sumX = 0, sumY = 0, sumXY = 0, sumXX = 0;
528
- for (let p of points) {
529
- sumX += p.x;
530
- sumY += p.y;
531
- sumXY += p.x * p.y;
532
- sumXX += p.x * p.x;
533
- }
534
- let m, c;
535
- if (forceOrigin) {
536
- let sumXY_o = 0, sumXX_o = 0;
537
  for (let p of points) {
538
- sumXY_o += p.x * p.y;
539
- sumXX_o += p.x * p.x;
 
 
540
  }
541
- m = sumXX_o === 0 ? 0 : sumXY_o / sumXX_o;
542
- c = 0;
543
- } else {
544
- const denominator = (n * sumXX) - (sumX * sumX);
545
- if (denominator === 0) return null;
546
- m = ((n * sumXY) - (sumX * sumY)) / denominator;
547
- c = (sumY - (m * sumX)) / n;
548
- }
549
- const yMean = sumY / n;
550
- let ssTot = 0, ssRes = 0;
551
- for (let p of points) {
552
- ssTot += Math.pow(p.y - yMean, 2);
553
- const yPred = m * p.x + c;
554
- ssRes += Math.pow(p.y - yPred, 2);
555
- }
556
- const r2 = ssTot === 0 ? 1 : 1 - (ssRes / ssTot);
557
- return { m, c, r2, type: 'linear', label: 'Linear Fit', f: (x) => m * x + c };
 
 
 
 
 
 
 
 
 
 
 
 
 
558
  }
559
 
560
  // Quadratic regression
 
520
 
521
  // --- MATHEMATICAL REgRESSION ENGINE ---
522
 
523
+ // Linear regression.
524
+ // "Force scale through origin" only pins the intercept when that line
525
+ // still fits. A negative straight line that does not pass through (0,0)
526
+ // must stay a negative linear fit, not get replaced by a curve.
527
  function calculateLinearFit(points) {
528
  if (points.length < 2) return null;
529
+
530
+ const fit = (throughOrigin) => {
531
+ const n = points.length;
532
+ let sumX = 0, sumY = 0, sumXY = 0, sumXX = 0;
 
 
 
 
 
 
 
533
  for (let p of points) {
534
+ sumX += p.x;
535
+ sumY += p.y;
536
+ sumXY += p.x * p.y;
537
+ sumXX += p.x * p.x;
538
  }
539
+ let m, c;
540
+ if (throughOrigin) {
541
+ m = sumXX === 0 ? 0 : sumXY / sumXX;
542
+ c = 0;
543
+ } else {
544
+ const denominator = (n * sumXX) - (sumX * sumX);
545
+ if (denominator === 0) return null;
546
+ m = ((n * sumXY) - (sumX * sumY)) / denominator;
547
+ c = (sumY - (m * sumX)) / n;
548
+ }
549
+ const yMean = sumY / n;
550
+ let ssTot = 0, ssRes = 0;
551
+ for (let p of points) {
552
+ ssTot += Math.pow(p.y - yMean, 2);
553
+ const yPred = m * p.x + c;
554
+ ssRes += Math.pow(p.y - yPred, 2);
555
+ }
556
+ const r2 = ssTot === 0 ? 1 : 1 - (ssRes / ssTot);
557
+ return { m, c, r2, type: 'linear', label: 'Linear Fit', f: (x) => m * x + c };
558
+ };
559
+
560
+ const free = fit(false);
561
+ if (!free) return null;
562
+ if (!forceOrigin) return free;
563
+
564
+ const forced = fit(true);
565
+ // Origin constraint is only kept when it does not destroy a clearly linear trend
566
+ // (including a negative gradient whose intercept is not zero).
567
+ if (forced && forced.r2 >= 0.95 && forced.r2 >= free.r2 - 0.02) return forced;
568
+ return free;
569
  }
570
 
571
  // Quadratic regression