Spaces:
Running
Running
Update index.html
Browse files- index.html +53 -18
index.html
CHANGED
|
@@ -586,28 +586,63 @@
|
|
| 586 |
}
|
| 587 |
|
| 588 |
// Helper to generate simple exponential/log trendline points
|
| 589 |
-
function generateTrendline(data, quantileOffset) {
|
| 590 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 591 |
|
| 592 |
-
|
| 593 |
-
|
| 594 |
-
|
| 595 |
-
|
|
|
|
| 596 |
|
| 597 |
-
|
| 598 |
-
|
| 599 |
-
|
| 600 |
-
|
| 601 |
-
|
| 602 |
-
|
| 603 |
-
if (quantileOffset > 0.8) estimatedY += 12; // Push top 10% higher
|
| 604 |
|
| 605 |
-
|
| 606 |
-
|
| 607 |
-
}
|
| 608 |
|
| 609 |
-
|
| 610 |
-
|
|
|
|
|
|
|
|
|
|
| 611 |
|
| 612 |
function initShareChart() {
|
| 613 |
const ctx = document.getElementById('shareChart').getContext('2d');
|
|
|
|
| 586 |
}
|
| 587 |
|
| 588 |
// Helper to generate simple exponential/log trendline points
|
| 589 |
+
function generateTrendline(data, quantileOffset = 0.5) {
|
| 590 |
+
if (!data || data.length < 2) return [];
|
| 591 |
+
|
| 592 |
+
// 1. Sort data by parameter count (X)
|
| 593 |
+
const sorted = [...data].sort((a, b) => a.x - b.x);
|
| 594 |
+
|
| 595 |
+
// 2. Compute Ordinary Least Squares (OLS) for Y = a * log10(X) + b
|
| 596 |
+
let sumLnX = 0, sumY = 0, sumLnX2 = 0, sumLnXY = 0;
|
| 597 |
+
const n = sorted.length;
|
| 598 |
+
|
| 599 |
+
sorted.forEach(p => {
|
| 600 |
+
const logX = Math.log10(p.x);
|
| 601 |
+
sumLnX += logX;
|
| 602 |
+
sumY += p.y;
|
| 603 |
+
sumLnX2 += logX * logX;
|
| 604 |
+
sumLnXY += logX * p.y;
|
| 605 |
+
});
|
| 606 |
+
|
| 607 |
+
const slope = (n * sumLnXY - sumLnX * sumY) / (n * sumLnX2 - sumLnX * sumLnX);
|
| 608 |
+
const intercept = (sumY - slope * sumLnX) / n;
|
| 609 |
+
|
| 610 |
+
// 3. Calculate residuals to find standard deviation (for accurate top-10% curve)
|
| 611 |
+
const residuals = sorted.map(p => p.y - (slope * Math.log10(p.x) + intercept));
|
| 612 |
+
const meanResidual = residuals.reduce((a, b) => a + b, 0) / n;
|
| 613 |
+
const stdDev = Math.sqrt(residuals.reduce((sq, r) => sq + Math.pow(r - meanResidual, 2), 0) / n);
|
| 614 |
+
|
| 615 |
+
// Determine vertical offset based on quantile
|
| 616 |
+
// quantileOffset = 0.5 -> Mean trend
|
| 617 |
+
// quantileOffset = 0.9 -> Top 10% (~1.28 standard deviations above mean)
|
| 618 |
+
let offset = 0;
|
| 619 |
+
if (quantileOffset > 0.5) {
|
| 620 |
+
// 1.28 * stdDev corresponds to the ~90th percentile in a normal distribution
|
| 621 |
+
const zScore = quantileOffset >= 0.9 ? 1.28 : (quantileOffset - 0.5) * 2.56;
|
| 622 |
+
offset = zScore * stdDev;
|
| 623 |
+
}
|
| 624 |
|
| 625 |
+
// 4. Generate smooth curve points across log space
|
| 626 |
+
const minLog = Math.log10(sorted[0].x);
|
| 627 |
+
const maxLog = Math.log10(sorted[sorted.length - 1].x);
|
| 628 |
+
const points = [];
|
| 629 |
+
const steps = 30;
|
| 630 |
|
| 631 |
+
for (let i = 0; i <= steps; i++) {
|
| 632 |
+
const currentLog = minLog + (i / steps) * (maxLog - minLog);
|
| 633 |
+
const currentX = Math.pow(10, currentLog);
|
| 634 |
+
|
| 635 |
+
// Y = fitted_line + quantile_offset
|
| 636 |
+
let estimatedY = (slope * currentLog + intercept) + offset;
|
|
|
|
| 637 |
|
| 638 |
+
// Clamp between 0% and 100%
|
| 639 |
+
estimatedY = Math.min(100, Math.max(0, estimatedY));
|
|
|
|
| 640 |
|
| 641 |
+
points.push({ x: currentX, y: estimatedY });
|
| 642 |
+
}
|
| 643 |
+
|
| 644 |
+
return points;
|
| 645 |
+
}
|
| 646 |
|
| 647 |
function initShareChart() {
|
| 648 |
const ctx = document.getElementById('shareChart').getContext('2d');
|