thibaud frere commited on
Commit
a76e825
·
1 Parent(s): ce67e6b

update charts

Browse files
app/src/components/Accordion.astro CHANGED
@@ -101,6 +101,11 @@ const wrapperClass = ["accordion", className].filter(Boolean).join(" ");
101
  position: relative;
102
  }
103
 
 
 
 
 
 
104
  /* Remove conditional padding to avoid jump on close */
105
 
106
  /* Remove native marker */
@@ -160,6 +165,7 @@ const wrapperClass = ["accordion", className].filter(Boolean).join(" ");
160
  padding: 0;
161
  }
162
 
 
163
  /* Separator between header and content when open (edge-to-edge) */
164
  .accordion[open] .accordion__content-wrapper::before {
165
  content: "";
@@ -178,6 +184,7 @@ const wrapperClass = ["accordion", className].filter(Boolean).join(" ");
178
  outline-offset: 3px;
179
  border-radius: 8px;
180
  }
 
181
 
182
 
183
  </style>
 
101
  position: relative;
102
  }
103
 
104
+ .accordion[size="big"] .accordion__summary {
105
+ padding: 16px;
106
+ }
107
+
108
+
109
  /* Remove conditional padding to avoid jump on close */
110
 
111
  /* Remove native marker */
 
165
  padding: 0;
166
  }
167
 
168
+
169
  /* Separator between header and content when open (edge-to-edge) */
170
  .accordion[open] .accordion__content-wrapper::before {
171
  content: "";
 
184
  outline-offset: 3px;
185
  border-radius: 8px;
186
  }
187
+
188
 
189
 
190
  </style>
app/src/content/article.mdx CHANGED
@@ -67,7 +67,7 @@ Projects like The Cauldron, LLaVa and Cambrian aim to provide such datasets, but
67
  We manually collect **over 180** image-text datasets from the recent literature and create new subsets in lacking domains.
68
 
69
  <Wide>
70
- <Accordion title="FineVision Subsets">
71
  |Subset Name |Total Images|Total Samples|Total Turns|Total Question Tokens|Total Answer Tokens|Category |Source |
72
  |--------------------------------------|------------|-------------|-----------|---------------------|-------------------|----------------------|------- |
73
  |coco_colors |118,287 |118,287 |118,287 |1,301,157 |6,376,672 |Captioning & Knowledge|[@noauthor_hazal-karakusmscoco-controlnet] |
 
67
  We manually collect **over 180** image-text datasets from the recent literature and create new subsets in lacking domains.
68
 
69
  <Wide>
70
+ <Accordion size="big" title="FineVision Subsets">
71
  |Subset Name |Total Images|Total Samples|Total Turns|Total Question Tokens|Total Answer Tokens|Category |Source |
72
  |--------------------------------------|------------|-------------|-----------|---------------------|-------------------|----------------------|------- |
73
  |coco_colors |118,287 |118,287 |118,287 |1,301,157 |6,376,672 |Captioning & Knowledge|[@noauthor_hazal-karakusmscoco-controlnet] |
app/src/content/embeds/against-baselines-deduplicated.html CHANGED
@@ -180,6 +180,7 @@
180
  const gRoot = svg.append('g');
181
  const gGrid = gRoot.append('g').attr('class', 'grid');
182
  const gAxes = gRoot.append('g').attr('class', 'axes');
 
183
  const gLines = gRoot.append('g').attr('class', 'lines');
184
  const gPoints = gRoot.append('g').attr('class', 'points');
185
  const gHover = gRoot.append('g').attr('class', 'hover');
@@ -428,9 +429,27 @@
428
  values: (map[r]||[])
429
  .slice()
430
  .sort((a,b)=>a.step-b.step)
431
- .map(pt => isRankStrict ? { step: pt.step, value: Math.round(pt.value) } : pt)
432
  }));
433
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
434
  // Draw lines
435
  const paths = gLines.selectAll('path.run-line').data(series, d=>d.run);
436
  paths.enter().append('path').attr('class','run-line').attr('fill','none').attr('stroke-width',2)
@@ -531,11 +550,12 @@
531
  // Tooltip content
532
  let html = `<div><strong>${getMetricDisplayName(metricKey)}</strong></div><div><strong>step</strong> ${nearest}</div>`;
533
  series.forEach(s=>{
534
- const m = new Map(s.values.map(v=>[v.step, v.value]));
535
- const val = m.has(nearest) ? m.get(nearest) : null;
536
- if (val != null) {
537
  const formatVal = (vv) => (isRankStrict ? d3.format('d')(vv) : (+vv).toFixed(4));
538
- html += `<div><span style=\"display:inline-block;width:10px;height:10px;background:${s.color};border-radius:50%;margin-right:6px;\"></span><strong>${s.run}</strong> ${formatVal(val)}</div>`;
 
539
  }
540
  });
541
  tipInner.innerHTML = html;
@@ -552,7 +572,13 @@
552
  (async () => {
553
  try {
554
  const text = await fetchFirstAvailable(CSV_PATHS);
555
- const rows = d3.csvParse(text, d => ({ run: (d.run||'').trim(), step: +d.step, metric: (d.metric||'').trim(), value: +d.value }));
 
 
 
 
 
 
556
  metricList = Array.from(new Set(rows.map(r=>r.metric))).sort();
557
  runList = Array.from(new Set(rows.map(r=>r.run))).sort();
558
  runOrder = runList;
@@ -560,7 +586,7 @@
560
  metricList.forEach(m => {
561
  const map = {};
562
  runList.forEach(r => { map[r] = []; });
563
- rows.filter(r=>r.metric===m).forEach(r => { if (!isNaN(r.step) && !isNaN(r.value)) map[r.run].push({ step:r.step, value:r.value }); });
564
  dataByMetric.set(m, map);
565
  });
566
 
 
180
  const gRoot = svg.append('g');
181
  const gGrid = gRoot.append('g').attr('class', 'grid');
182
  const gAxes = gRoot.append('g').attr('class', 'axes');
183
+ const gAreas = gRoot.append('g').attr('class', 'areas');
184
  const gLines = gRoot.append('g').attr('class', 'lines');
185
  const gPoints = gRoot.append('g').attr('class', 'points');
186
  const gHover = gRoot.append('g').attr('class', 'hover');
 
429
  values: (map[r]||[])
430
  .slice()
431
  .sort((a,b)=>a.step-b.step)
432
+ .map(pt => isRankStrict ? { step: pt.step, value: Math.round(pt.value), stderr: pt.stderr } : pt)
433
  }));
434
 
435
+ // Uncertainty area (± stderr) for non-rank metrics
436
+ gAreas.selectAll('*').remove();
437
+ if (!isRank) {
438
+ series.forEach((s) => {
439
+ const withErr = s.values.filter(v => v && v.stderr != null && isFinite(v.stderr) && v.stderr > 0 && isFinite(v.value));
440
+ if (withErr.length === 0) return;
441
+ const upper = withErr.map(d => [xScale(d.step), yScale(d.value + d.stderr)]);
442
+ const lower = withErr.slice().reverse().map(d => [xScale(d.step), yScale(d.value - d.stderr)]);
443
+ const coords = upper.concat(lower);
444
+ const pathData = d3.line().x(d=>d[0]).y(d=>d[1]).curve(d3.curveLinearClosed)(coords);
445
+ gAreas.append('path')
446
+ .attr('d', pathData)
447
+ .attr('fill', s.color)
448
+ .attr('opacity', 0.15)
449
+ .attr('stroke', 'none');
450
+ });
451
+ }
452
+
453
  // Draw lines
454
  const paths = gLines.selectAll('path.run-line').data(series, d=>d.run);
455
  paths.enter().append('path').attr('class','run-line').attr('fill','none').attr('stroke-width',2)
 
550
  // Tooltip content
551
  let html = `<div><strong>${getMetricDisplayName(metricKey)}</strong></div><div><strong>step</strong> ${nearest}</div>`;
552
  series.forEach(s=>{
553
+ const m = new Map(s.values.map(v=>[v.step, v]));
554
+ const pt = m.get(nearest);
555
+ if (pt && pt.value != null) {
556
  const formatVal = (vv) => (isRankStrict ? d3.format('d')(vv) : (+vv).toFixed(4));
557
+ const errTxt = (pt.stderr != null && isFinite(pt.stderr) && pt.stderr > 0) ? ` ± ${formatVal(pt.stderr)}` : '';
558
+ html += `<div><span style=\"display:inline-block;width:10px;height:10px;background:${s.color};border-radius:50%;margin-right:6px;\"></span><strong>${s.run}</strong> ${formatVal(pt.value)}${errTxt}</div>`;
559
  }
560
  });
561
  tipInner.innerHTML = html;
 
572
  (async () => {
573
  try {
574
  const text = await fetchFirstAvailable(CSV_PATHS);
575
+ const rows = d3.csvParse(text, d => ({
576
+ run: (d.run||'').trim(),
577
+ step: +d.step,
578
+ metric: (d.metric||'').trim(),
579
+ value: +d.value,
580
+ stderr: (d.stderr != null && d.stderr !== '') ? +d.stderr : null
581
+ }));
582
  metricList = Array.from(new Set(rows.map(r=>r.metric))).sort();
583
  runList = Array.from(new Set(rows.map(r=>r.run))).sort();
584
  runOrder = runList;
 
586
  metricList.forEach(m => {
587
  const map = {};
588
  runList.forEach(r => { map[r] = []; });
589
+ rows.filter(r=>r.metric===m).forEach(r => { if (!isNaN(r.step) && !isNaN(r.value)) map[r.run].push({ step:r.step, value:r.value, stderr:r.stderr }); });
590
  dataByMetric.set(m, map);
591
  });
592
 
app/src/content/embeds/against-baselines.html CHANGED
@@ -177,7 +177,9 @@
177
  const gRoot = svg.append('g');
178
  const gGrid = gRoot.append('g').attr('class', 'grid');
179
  const gAxes = gRoot.append('g').attr('class', 'axes');
 
180
  const gLines = gRoot.append('g').attr('class', 'lines');
 
181
  const gPoints = gRoot.append('g').attr('class', 'points');
182
  const gHover = gRoot.append('g').attr('class', 'hover');
183
  const gLegend = gRoot.append('foreignObject').attr('class', 'legend');
@@ -425,9 +427,28 @@
425
  values: (map[r]||[])
426
  .slice()
427
  .sort((a,b)=>a.step-b.step)
428
- .map(pt => isRankStrict ? { step: pt.step, value: Math.round(pt.value) } : pt)
429
  }));
430
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
431
  // Draw lines
432
  const paths = gLines.selectAll('path.run-line').data(series, d=>d.run);
433
  paths.enter().append('path').attr('class','run-line').attr('fill','none').attr('stroke-width',2)
@@ -439,6 +460,9 @@
439
  .attr('d', d=>lineGen(d.values));
440
  paths.exit().remove();
441
 
 
 
 
442
  // Draw markers for each data point
443
  gPoints.selectAll('*').remove();
444
  series.forEach((s, seriesIndex) => {
@@ -528,11 +552,12 @@
528
  // Tooltip content
529
  let html = `<div><strong>${getMetricDisplayName(metricKey)}</strong></div><div><strong>step</strong> ${nearest}</div>`;
530
  series.forEach(s=>{
531
- const m = new Map(s.values.map(v=>[v.step, v.value]));
532
- const val = m.has(nearest) ? m.get(nearest) : null;
533
- if (val != null) {
534
  const formatVal = (vv) => (isRankStrict ? d3.format('d')(vv) : (+vv).toFixed(4));
535
- html += `<div><span style=\"display:inline-block;width:10px;height:10px;background:${s.color};border-radius:50%;margin-right:6px;\"></span><strong>${s.run}</strong> ${formatVal(val)}</div>`;
 
536
  }
537
  });
538
  tipInner.innerHTML = html;
@@ -549,7 +574,13 @@
549
  (async () => {
550
  try {
551
  const text = await fetchFirstAvailable(CSV_PATHS);
552
- const rows = d3.csvParse(text, d => ({ run: (d.run||'').trim(), step: +d.step, metric: (d.metric||'').trim(), value: +d.value }));
 
 
 
 
 
 
553
  metricList = Array.from(new Set(rows.map(r=>r.metric))).sort();
554
  runList = Array.from(new Set(rows.map(r=>r.run))).sort();
555
  runOrder = runList;
@@ -557,7 +588,7 @@
557
  metricList.forEach(m => {
558
  const map = {};
559
  runList.forEach(r => { map[r] = []; });
560
- rows.filter(r=>r.metric===m).forEach(r => { if (!isNaN(r.step) && !isNaN(r.value)) map[r.run].push({ step:r.step, value:r.value }); });
561
  dataByMetric.set(m, map);
562
  });
563
 
 
177
  const gRoot = svg.append('g');
178
  const gGrid = gRoot.append('g').attr('class', 'grid');
179
  const gAxes = gRoot.append('g').attr('class', 'axes');
180
+ const gAreas = gRoot.append('g').attr('class', 'areas');
181
  const gLines = gRoot.append('g').attr('class', 'lines');
182
+ const gErrors = gRoot.append('g').attr('class', 'errors');
183
  const gPoints = gRoot.append('g').attr('class', 'points');
184
  const gHover = gRoot.append('g').attr('class', 'hover');
185
  const gLegend = gRoot.append('foreignObject').attr('class', 'legend');
 
427
  values: (map[r]||[])
428
  .slice()
429
  .sort((a,b)=>a.step-b.step)
430
+ .map(pt => isRankStrict ? { step: pt.step, value: Math.round(pt.value), stderr: pt.stderr } : pt)
431
  }));
432
 
433
+ // Uncertainty area (± stderr) for non-rank metrics
434
+ gAreas.selectAll('*').remove();
435
+ if (!isRank) {
436
+ series.forEach((s) => {
437
+ const withErr = s.values.filter(v => v && v.stderr != null && isFinite(v.stderr) && v.stderr > 0 && isFinite(v.value));
438
+ if (withErr.length === 0) return;
439
+ // Build polygon path going forward on upper bound and back on lower bound
440
+ const upper = withErr.map(d => [xScale(d.step), yScale(d.value + d.stderr)]);
441
+ const lower = withErr.slice().reverse().map(d => [xScale(d.step), yScale(d.value - d.stderr)]);
442
+ const coords = upper.concat(lower);
443
+ const pathData = d3.line().x(d=>d[0]).y(d=>d[1]).curve(d3.curveLinearClosed)(coords);
444
+ gAreas.append('path')
445
+ .attr('d', pathData)
446
+ .attr('fill', s.color)
447
+ .attr('opacity', 0.15)
448
+ .attr('stroke', 'none');
449
+ });
450
+ }
451
+
452
  // Draw lines
453
  const paths = gLines.selectAll('path.run-line').data(series, d=>d.run);
454
  paths.enter().append('path').attr('class','run-line').attr('fill','none').attr('stroke-width',2)
 
460
  .attr('d', d=>lineGen(d.values));
461
  paths.exit().remove();
462
 
463
+ // Remove candle-like error bars in favor of filled areas
464
+ gErrors.selectAll('*').remove();
465
+
466
  // Draw markers for each data point
467
  gPoints.selectAll('*').remove();
468
  series.forEach((s, seriesIndex) => {
 
552
  // Tooltip content
553
  let html = `<div><strong>${getMetricDisplayName(metricKey)}</strong></div><div><strong>step</strong> ${nearest}</div>`;
554
  series.forEach(s=>{
555
+ const m = new Map(s.values.map(v=>[v.step, v]));
556
+ const pt = m.get(nearest);
557
+ if (pt && pt.value != null) {
558
  const formatVal = (vv) => (isRankStrict ? d3.format('d')(vv) : (+vv).toFixed(4));
559
+ const errTxt = (pt.stderr != null && isFinite(pt.stderr) && pt.stderr > 0) ? ` ± ${formatVal(pt.stderr)}` : '';
560
+ html += `<div><span style=\"display:inline-block;width:10px;height:10px;background:${s.color};border-radius:50%;margin-right:6px;\"></span><strong>${s.run}</strong> ${formatVal(pt.value)}${errTxt}</div>`;
561
  }
562
  });
563
  tipInner.innerHTML = html;
 
574
  (async () => {
575
  try {
576
  const text = await fetchFirstAvailable(CSV_PATHS);
577
+ const rows = d3.csvParse(text, d => ({
578
+ run: (d.run||'').trim(),
579
+ step: +d.step,
580
+ metric: (d.metric||'').trim(),
581
+ value: +d.value,
582
+ stderr: (d.stderr != null && d.stderr !== '') ? +d.stderr : null
583
+ }));
584
  metricList = Array.from(new Set(rows.map(r=>r.metric))).sort();
585
  runList = Array.from(new Set(rows.map(r=>r.run))).sort();
586
  runOrder = runList;
 
588
  metricList.forEach(m => {
589
  const map = {};
590
  runList.forEach(r => { map[r] = []; });
591
+ rows.filter(r=>r.metric===m).forEach(r => { if (!isNaN(r.step) && !isNaN(r.value)) map[r.run].push({ step:r.step, value:r.value, stderr:r.stderr }); });
592
  dataByMetric.set(m, map);
593
  });
594
 
app/src/content/embeds/all-ratings.html CHANGED
@@ -177,6 +177,7 @@
177
  const gRoot = svg.append('g');
178
  const gGrid = gRoot.append('g').attr('class', 'grid');
179
  const gAxes = gRoot.append('g').attr('class', 'axes');
 
180
  const gLines = gRoot.append('g').attr('class', 'lines');
181
  const gPoints = gRoot.append('g').attr('class', 'points');
182
  const gHover = gRoot.append('g').attr('class', 'hover');
@@ -425,9 +426,27 @@
425
  values: (map[r]||[])
426
  .slice()
427
  .sort((a,b)=>a.step-b.step)
428
- .map(pt => isRankStrict ? { step: pt.step, value: Math.round(pt.value) } : pt)
429
  }));
430
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
431
  // Draw lines
432
  const paths = gLines.selectAll('path.run-line').data(series, d=>d.run);
433
  paths.enter().append('path').attr('class','run-line').attr('fill','none').attr('stroke-width',2)
@@ -528,11 +547,12 @@
528
  // Tooltip content
529
  let html = `<div><strong>${getMetricDisplayName(metricKey)}</strong></div><div><strong>step</strong> ${nearest}</div>`;
530
  series.forEach(s=>{
531
- const m = new Map(s.values.map(v=>[v.step, v.value]));
532
- const val = m.has(nearest) ? m.get(nearest) : null;
533
- if (val != null) {
534
  const formatVal = (vv) => (isRankStrict ? d3.format('d')(vv) : (+vv).toFixed(4));
535
- html += `<div><span style=\"display:inline-block;width:10px;height:10px;background:${s.color};border-radius:50%;margin-right:6px;\"></span><strong>${s.run}</strong> ${formatVal(val)}</div>`;
 
536
  }
537
  });
538
  tipInner.innerHTML = html;
@@ -549,7 +569,13 @@
549
  (async () => {
550
  try {
551
  const text = await fetchFirstAvailable(CSV_PATHS);
552
- const rows = d3.csvParse(text, d => ({ run: (d.run||'').trim(), step: +d.step, metric: (d.metric||'').trim(), value: +d.value }));
 
 
 
 
 
 
553
  metricList = Array.from(new Set(rows.map(r=>r.metric))).sort();
554
  runList = Array.from(new Set(rows.map(r=>r.run))).sort();
555
  runOrder = runList;
@@ -557,7 +583,7 @@
557
  metricList.forEach(m => {
558
  const map = {};
559
  runList.forEach(r => { map[r] = []; });
560
- rows.filter(r=>r.metric===m).forEach(r => { if (!isNaN(r.step) && !isNaN(r.value)) map[r.run].push({ step:r.step, value:r.value }); });
561
  dataByMetric.set(m, map);
562
  });
563
 
 
177
  const gRoot = svg.append('g');
178
  const gGrid = gRoot.append('g').attr('class', 'grid');
179
  const gAxes = gRoot.append('g').attr('class', 'axes');
180
+ const gAreas = gRoot.append('g').attr('class', 'areas');
181
  const gLines = gRoot.append('g').attr('class', 'lines');
182
  const gPoints = gRoot.append('g').attr('class', 'points');
183
  const gHover = gRoot.append('g').attr('class', 'hover');
 
426
  values: (map[r]||[])
427
  .slice()
428
  .sort((a,b)=>a.step-b.step)
429
+ .map(pt => isRankStrict ? { step: pt.step, value: Math.round(pt.value), stderr: pt.stderr } : pt)
430
  }));
431
 
432
+ // Uncertainty area (± stderr) for non-rank metrics
433
+ gAreas.selectAll('*').remove();
434
+ if (!isRank) {
435
+ series.forEach((s) => {
436
+ const withErr = s.values.filter(v => v && v.stderr != null && isFinite(v.stderr) && v.stderr > 0 && isFinite(v.value));
437
+ if (withErr.length === 0) return;
438
+ const upper = withErr.map(d => [xScale(d.step), yScale(d.value + d.stderr)]);
439
+ const lower = withErr.slice().reverse().map(d => [xScale(d.step), yScale(d.value - d.stderr)]);
440
+ const coords = upper.concat(lower);
441
+ const pathData = d3.line().x(d=>d[0]).y(d=>d[1]).curve(d3.curveLinearClosed)(coords);
442
+ gAreas.append('path')
443
+ .attr('d', pathData)
444
+ .attr('fill', s.color)
445
+ .attr('opacity', 0.15)
446
+ .attr('stroke', 'none');
447
+ });
448
+ }
449
+
450
  // Draw lines
451
  const paths = gLines.selectAll('path.run-line').data(series, d=>d.run);
452
  paths.enter().append('path').attr('class','run-line').attr('fill','none').attr('stroke-width',2)
 
547
  // Tooltip content
548
  let html = `<div><strong>${getMetricDisplayName(metricKey)}</strong></div><div><strong>step</strong> ${nearest}</div>`;
549
  series.forEach(s=>{
550
+ const m = new Map(s.values.map(v=>[v.step, v]));
551
+ const pt = m.get(nearest);
552
+ if (pt && pt.value != null) {
553
  const formatVal = (vv) => (isRankStrict ? d3.format('d')(vv) : (+vv).toFixed(4));
554
+ const errTxt = (pt.stderr != null && isFinite(pt.stderr) && pt.stderr > 0) ? ` ± ${formatVal(pt.stderr)}` : '';
555
+ html += `<div><span style=\"display:inline-block;width:10px;height:10px;background:${s.color};border-radius:50%;margin-right:6px;\"></span><strong>${s.run}</strong> ${formatVal(pt.value)}${errTxt}</div>`;
556
  }
557
  });
558
  tipInner.innerHTML = html;
 
569
  (async () => {
570
  try {
571
  const text = await fetchFirstAvailable(CSV_PATHS);
572
+ const rows = d3.csvParse(text, d => ({
573
+ run: (d.run||'').trim(),
574
+ step: +d.step,
575
+ metric: (d.metric||'').trim(),
576
+ value: +d.value,
577
+ stderr: (d.stderr != null && d.stderr !== '') ? +d.stderr : null
578
+ }));
579
  metricList = Array.from(new Set(rows.map(r=>r.metric))).sort();
580
  runList = Array.from(new Set(rows.map(r=>r.run))).sort();
581
  runOrder = runList;
 
583
  metricList.forEach(m => {
584
  const map = {};
585
  runList.forEach(r => { map[r] = []; });
586
+ rows.filter(r=>r.metric===m).forEach(r => { if (!isNaN(r.step) && !isNaN(r.value)) map[r.run].push({ step:r.step, value:r.value, stderr:r.stderr }); });
587
  dataByMetric.set(m, map);
588
  });
589
 
app/src/content/embeds/filters-quad.html CHANGED
@@ -90,6 +90,7 @@
90
  const gRoot = svg.append('g');
91
  const gGrid = gRoot.append('g').attr('class','grid');
92
  const gAxes = gRoot.append('g').attr('class','axes');
 
93
  const gLines = gRoot.append('g').attr('class','lines');
94
  const gPoints = gRoot.append('g').attr('class','points');
95
  const gHover = gRoot.append('g').attr('class','hover');
@@ -186,7 +187,21 @@
186
 
187
  const { innerWidth, innerHeight } = updateScales();
188
 
189
- const series = runs.map((r, i) => ({ run:r, color: pool[i % pool.length], marker: markerShapes[i % markerShapes.length], values:(map[r]||[]).slice().sort((a,b)=>a.step-b.step).map(pt => isRankStrict ? { step: pt.step, value: Math.round(pt.value) } : pt) }));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
190
 
191
  const paths = gLines.selectAll('path.run-line').data(series, d=>d.run);
192
  paths.enter().append('path').attr('class','run-line').attr('fill','none').attr('stroke-width',2).attr('opacity',0.9)
@@ -216,7 +231,7 @@
216
  const hoverLine = gHover.append('line').attr('stroke','rgba(0,0,0,0.25)').attr('stroke-width',1).attr('y1',0).attr('y2',innerHeight).style('display','none');
217
  const stepSet = new Set(); series.forEach(s=>s.values.forEach(v=>stepSet.add(v.step))); const steps = Array.from(stepSet).sort((a,b)=>a-b);
218
  function onMove(ev){ const [mx,my]=d3.pointer(ev, overlay.node()); const nearest = steps.reduce((best,s)=> Math.abs(s - xScale.invert(mx)) < Math.abs(best - xScale.invert(mx)) ? s : best, steps[0]); const xpx = xScale(nearest); hoverLine.attr('x1',xpx).attr('x2',xpx).style('display',null);
219
- let html = `<div><strong>${titleText}</strong></div><div><strong>step</strong> ${nearest}</div>`; series.forEach(s=>{ const m = new Map(s.values.map(v=>[v.step, v.value])); const val = m.get(nearest); if (val!=null){ html+=`<div><span style=\"display:inline-block;width:10px;height:10px;background:${s.color};border-radius:50%;margin-right:6px;\"></span><strong>${s.run}</strong> ${isRankStrictFlag ? d3.format('d')(val) : (+val).toFixed(4)}</div>`; }});
220
  tipInner.innerHTML = html; const offsetX=12, offsetY=12; tip.style.opacity='1'; tip.style.transform=`translate(${Math.round(mx+offsetX+margin.left)}px, ${Math.round(my+offsetY+margin.top)}px)`; }
221
  function onLeave(){ tip.style.opacity='0'; tip.style.transform='translate(-9999px, -9999px)'; hoverLine.style('display','none'); }
222
  overlay.on('mousemove', onMove).on('mouseleave', onLeave);
@@ -237,10 +252,10 @@
237
  try { const r = await fetch(p, { cache:'no-cache' }); if (r.ok) { text = await r.text(); break; } } catch(e){}
238
  }
239
  if (text == null) throw new Error(`CSV not found: ${file}`);
240
- const rows = d3.csvParse(text, d => ({ run:(d.run||'').trim(), step:+d.step, metric:(d.metric||'').trim(), value:+d.value }));
241
  metricList = Array.from(new Set(rows.map(r=>r.metric))).sort();
242
  runList = Array.from(new Set(rows.map(r=>r.run))).sort(); runOrder = runList;
243
- metricList.forEach(m => { const map={}; runList.forEach(r=>map[r]=[]); rows.filter(r=>r.metric===m).forEach(r=>{ if(!isNaN(r.step)&&!isNaN(r.value)) map[r.run].push({ step:r.step, value:r.value }); }); dataByMetric.set(m, map); });
244
  const def = metricList.find(m => /average_rank/i.test(m)) || metricList[0];
245
  renderMetric(def);
246
  const ro = window.ResizeObserver ? new ResizeObserver(()=>renderMetric(def)) : null; if (ro) ro.observe(cell);
 
90
  const gRoot = svg.append('g');
91
  const gGrid = gRoot.append('g').attr('class','grid');
92
  const gAxes = gRoot.append('g').attr('class','axes');
93
+ const gAreas = gRoot.append('g').attr('class','areas');
94
  const gLines = gRoot.append('g').attr('class','lines');
95
  const gPoints = gRoot.append('g').attr('class','points');
96
  const gHover = gRoot.append('g').attr('class','hover');
 
187
 
188
  const { innerWidth, innerHeight } = updateScales();
189
 
190
+ const series = runs.map((r, i) => ({ run:r, color: pool[i % pool.length], marker: markerShapes[i % markerShapes.length], values:(map[r]||[]).slice().sort((a,b)=>a.step-b.step).map(pt => isRankStrict ? { step: pt.step, value: Math.round(pt.value), stderr: pt.stderr } : pt) }));
191
+
192
+ // zones ± stderr (métriques non rank)
193
+ gAreas.selectAll('*').remove();
194
+ if (!isRank) {
195
+ series.forEach((s) => {
196
+ const withErr = s.values.filter(v => v && v.stderr != null && isFinite(v.stderr) && v.stderr > 0 && isFinite(v.value));
197
+ if (!withErr.length) return;
198
+ const upper = withErr.map(d => [xScale(d.step), yScale(d.value + d.stderr)]);
199
+ const lower = withErr.slice().reverse().map(d => [xScale(d.step), yScale(d.value - d.stderr)]);
200
+ const coords = upper.concat(lower);
201
+ const pathData = d3.line().x(d=>d[0]).y(d=>d[1]).curve(d3.curveLinearClosed)(coords);
202
+ gAreas.append('path').attr('d', pathData).attr('fill', s.color).attr('opacity', 0.15).attr('stroke', 'none');
203
+ });
204
+ }
205
 
206
  const paths = gLines.selectAll('path.run-line').data(series, d=>d.run);
207
  paths.enter().append('path').attr('class','run-line').attr('fill','none').attr('stroke-width',2).attr('opacity',0.9)
 
231
  const hoverLine = gHover.append('line').attr('stroke','rgba(0,0,0,0.25)').attr('stroke-width',1).attr('y1',0).attr('y2',innerHeight).style('display','none');
232
  const stepSet = new Set(); series.forEach(s=>s.values.forEach(v=>stepSet.add(v.step))); const steps = Array.from(stepSet).sort((a,b)=>a-b);
233
  function onMove(ev){ const [mx,my]=d3.pointer(ev, overlay.node()); const nearest = steps.reduce((best,s)=> Math.abs(s - xScale.invert(mx)) < Math.abs(best - xScale.invert(mx)) ? s : best, steps[0]); const xpx = xScale(nearest); hoverLine.attr('x1',xpx).attr('x2',xpx).style('display',null);
234
+ let html = `<div><strong>${titleText}</strong></div><div><strong>step</strong> ${nearest}</div>`; series.forEach(s=>{ const m = new Map(s.values.map(v=>[v.step, v])); const pt = m.get(nearest); if (pt && pt.value!=null){ const fmt = (vv)=> (isRankStrictFlag? d3.format('d')(vv) : (+vv).toFixed(4)); const err = (pt.stderr!=null && isFinite(pt.stderr) && pt.stderr>0) ? ` ± ${fmt(pt.stderr)}` : ''; html+=`<div><span style=\"display:inline-block;width:10px;height:10px;background:${s.color};border-radius:50%;margin-right:6px;\"></span><strong>${s.run}</strong> ${fmt(pt.value)}${err}</div>`; }});
235
  tipInner.innerHTML = html; const offsetX=12, offsetY=12; tip.style.opacity='1'; tip.style.transform=`translate(${Math.round(mx+offsetX+margin.left)}px, ${Math.round(my+offsetY+margin.top)}px)`; }
236
  function onLeave(){ tip.style.opacity='0'; tip.style.transform='translate(-9999px, -9999px)'; hoverLine.style('display','none'); }
237
  overlay.on('mousemove', onMove).on('mouseleave', onLeave);
 
252
  try { const r = await fetch(p, { cache:'no-cache' }); if (r.ok) { text = await r.text(); break; } } catch(e){}
253
  }
254
  if (text == null) throw new Error(`CSV not found: ${file}`);
255
+ const rows = d3.csvParse(text, d => ({ run:(d.run||'').trim(), step:+d.step, metric:(d.metric||'').trim(), value:+d.value, stderr: (d.stderr!=null && d.stderr!=='') ? +d.stderr : null }));
256
  metricList = Array.from(new Set(rows.map(r=>r.metric))).sort();
257
  runList = Array.from(new Set(rows.map(r=>r.run))).sort(); runOrder = runList;
258
+ metricList.forEach(m => { const map={}; runList.forEach(r=>map[r]=[]); rows.filter(r=>r.metric===m).forEach(r=>{ if(!isNaN(r.step)&&!isNaN(r.value)) map[r.run].push({ step:r.step, value:r.value, stderr:r.stderr }); }); dataByMetric.set(m, map); });
259
  const def = metricList.find(m => /average_rank/i.test(m)) || metricList[0];
260
  renderMetric(def);
261
  const ro = window.ResizeObserver ? new ResizeObserver(()=>renderMetric(def)) : null; if (ro) ro.observe(cell);
app/src/content/embeds/formatting-filters.html CHANGED
@@ -177,6 +177,7 @@
177
  const gRoot = svg.append('g');
178
  const gGrid = gRoot.append('g').attr('class', 'grid');
179
  const gAxes = gRoot.append('g').attr('class', 'axes');
 
180
  const gLines = gRoot.append('g').attr('class', 'lines');
181
  const gPoints = gRoot.append('g').attr('class', 'points');
182
  const gHover = gRoot.append('g').attr('class', 'hover');
@@ -425,9 +426,27 @@
425
  values: (map[r]||[])
426
  .slice()
427
  .sort((a,b)=>a.step-b.step)
428
- .map(pt => isRankStrict ? { step: pt.step, value: Math.round(pt.value) } : pt)
429
  }));
430
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
431
  // Draw lines
432
  const paths = gLines.selectAll('path.run-line').data(series, d=>d.run);
433
  paths.enter().append('path').attr('class','run-line').attr('fill','none').attr('stroke-width',2)
@@ -528,11 +547,12 @@
528
  // Tooltip content
529
  let html = `<div><strong>${getMetricDisplayName(metricKey)}</strong></div><div><strong>step</strong> ${nearest}</div>`;
530
  series.forEach(s=>{
531
- const m = new Map(s.values.map(v=>[v.step, v.value]));
532
- const val = m.has(nearest) ? m.get(nearest) : null;
533
- if (val != null) {
534
  const formatVal = (vv) => (isRankStrict ? d3.format('d')(vv) : (+vv).toFixed(4));
535
- html += `<div><span style=\"display:inline-block;width:10px;height:10px;background:${s.color};border-radius:50%;margin-right:6px;\"></span><strong>${s.run}</strong> ${formatVal(val)}</div>`;
 
536
  }
537
  });
538
  tipInner.innerHTML = html;
@@ -549,7 +569,13 @@
549
  (async () => {
550
  try {
551
  const text = await fetchFirstAvailable(CSV_PATHS);
552
- const rows = d3.csvParse(text, d => ({ run: (d.run||'').trim(), step: +d.step, metric: (d.metric||'').trim(), value: +d.value }));
 
 
 
 
 
 
553
  metricList = Array.from(new Set(rows.map(r=>r.metric))).sort();
554
  runList = Array.from(new Set(rows.map(r=>r.run))).sort();
555
  runOrder = runList;
@@ -557,7 +583,7 @@
557
  metricList.forEach(m => {
558
  const map = {};
559
  runList.forEach(r => { map[r] = []; });
560
- rows.filter(r=>r.metric===m).forEach(r => { if (!isNaN(r.step) && !isNaN(r.value)) map[r.run].push({ step:r.step, value:r.value }); });
561
  dataByMetric.set(m, map);
562
  });
563
 
 
177
  const gRoot = svg.append('g');
178
  const gGrid = gRoot.append('g').attr('class', 'grid');
179
  const gAxes = gRoot.append('g').attr('class', 'axes');
180
+ const gAreas = gRoot.append('g').attr('class', 'areas');
181
  const gLines = gRoot.append('g').attr('class', 'lines');
182
  const gPoints = gRoot.append('g').attr('class', 'points');
183
  const gHover = gRoot.append('g').attr('class', 'hover');
 
426
  values: (map[r]||[])
427
  .slice()
428
  .sort((a,b)=>a.step-b.step)
429
+ .map(pt => isRankStrict ? { step: pt.step, value: Math.round(pt.value), stderr: pt.stderr } : pt)
430
  }));
431
 
432
+ // Uncertainty area (± stderr) for non-rank metrics
433
+ gAreas.selectAll('*').remove();
434
+ if (!isRank) {
435
+ series.forEach((s) => {
436
+ const withErr = s.values.filter(v => v && v.stderr != null && isFinite(v.stderr) && v.stderr > 0 && isFinite(v.value));
437
+ if (withErr.length === 0) return;
438
+ const upper = withErr.map(d => [xScale(d.step), yScale(d.value + d.stderr)]);
439
+ const lower = withErr.slice().reverse().map(d => [xScale(d.step), yScale(d.value - d.stderr)]);
440
+ const coords = upper.concat(lower);
441
+ const pathData = d3.line().x(d=>d[0]).y(d=>d[1]).curve(d3.curveLinearClosed)(coords);
442
+ gAreas.append('path')
443
+ .attr('d', pathData)
444
+ .attr('fill', s.color)
445
+ .attr('opacity', 0.15)
446
+ .attr('stroke', 'none');
447
+ });
448
+ }
449
+
450
  // Draw lines
451
  const paths = gLines.selectAll('path.run-line').data(series, d=>d.run);
452
  paths.enter().append('path').attr('class','run-line').attr('fill','none').attr('stroke-width',2)
 
547
  // Tooltip content
548
  let html = `<div><strong>${getMetricDisplayName(metricKey)}</strong></div><div><strong>step</strong> ${nearest}</div>`;
549
  series.forEach(s=>{
550
+ const m = new Map(s.values.map(v=>[v.step, v]));
551
+ const pt = m.get(nearest);
552
+ if (pt && pt.value != null) {
553
  const formatVal = (vv) => (isRankStrict ? d3.format('d')(vv) : (+vv).toFixed(4));
554
+ const errTxt = (pt.stderr != null && isFinite(pt.stderr) && pt.stderr > 0) ? ` ± ${formatVal(pt.stderr)}` : '';
555
+ html += `<div><span style=\"display:inline-block;width:10px;height:10px;background:${s.color};border-radius:50%;margin-right:6px;\"></span><strong>${s.run}</strong> ${formatVal(pt.value)}${errTxt}</div>`;
556
  }
557
  });
558
  tipInner.innerHTML = html;
 
569
  (async () => {
570
  try {
571
  const text = await fetchFirstAvailable(CSV_PATHS);
572
+ const rows = d3.csvParse(text, d => ({
573
+ run: (d.run||'').trim(),
574
+ step: +d.step,
575
+ metric: (d.metric||'').trim(),
576
+ value: +d.value,
577
+ stderr: (d.stderr != null && d.stderr !== '') ? +d.stderr : null
578
+ }));
579
  metricList = Array.from(new Set(rows.map(r=>r.metric))).sort();
580
  runList = Array.from(new Set(rows.map(r=>r.run))).sort();
581
  runOrder = runList;
 
583
  metricList.forEach(m => {
584
  const map = {};
585
  runList.forEach(r => { map[r] = []; });
586
+ rows.filter(r=>r.metric===m).forEach(r => { if (!isNaN(r.step) && !isNaN(r.value)) map[r.run].push({ step:r.step, value:r.value, stderr:r.stderr }); });
587
  dataByMetric.set(m, map);
588
  });
589
 
app/src/content/embeds/image-correspondence-filters.html CHANGED
@@ -177,6 +177,7 @@
177
  const gRoot = svg.append('g');
178
  const gGrid = gRoot.append('g').attr('class', 'grid');
179
  const gAxes = gRoot.append('g').attr('class', 'axes');
 
180
  const gLines = gRoot.append('g').attr('class', 'lines');
181
  const gPoints = gRoot.append('g').attr('class', 'points');
182
  const gHover = gRoot.append('g').attr('class', 'hover');
@@ -425,9 +426,27 @@
425
  values: (map[r]||[])
426
  .slice()
427
  .sort((a,b)=>a.step-b.step)
428
- .map(pt => isRankStrict ? { step: pt.step, value: Math.round(pt.value) } : pt)
429
  }));
430
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
431
  // Draw lines
432
  const paths = gLines.selectAll('path.run-line').data(series, d=>d.run);
433
  paths.enter().append('path').attr('class','run-line').attr('fill','none').attr('stroke-width',2)
@@ -528,11 +547,12 @@
528
  // Tooltip content
529
  let html = `<div><strong>${getMetricDisplayName(metricKey)}</strong></div><div><strong>step</strong> ${nearest}</div>`;
530
  series.forEach(s=>{
531
- const m = new Map(s.values.map(v=>[v.step, v.value]));
532
- const val = m.has(nearest) ? m.get(nearest) : null;
533
- if (val != null) {
534
  const formatVal = (vv) => (isRankStrict ? d3.format('d')(vv) : (+vv).toFixed(4));
535
- html += `<div><span style=\"display:inline-block;width:10px;height:10px;background:${s.color};border-radius:50%;margin-right:6px;\"></span><strong>${s.run}</strong> ${formatVal(val)}</div>`;
 
536
  }
537
  });
538
  tipInner.innerHTML = html;
@@ -549,7 +569,13 @@
549
  (async () => {
550
  try {
551
  const text = await fetchFirstAvailable(CSV_PATHS);
552
- const rows = d3.csvParse(text, d => ({ run: (d.run||'').trim(), step: +d.step, metric: (d.metric||'').trim(), value: +d.value }));
 
 
 
 
 
 
553
  metricList = Array.from(new Set(rows.map(r=>r.metric))).sort();
554
  runList = Array.from(new Set(rows.map(r=>r.run))).sort();
555
  runOrder = runList;
@@ -557,7 +583,7 @@
557
  metricList.forEach(m => {
558
  const map = {};
559
  runList.forEach(r => { map[r] = []; });
560
- rows.filter(r=>r.metric===m).forEach(r => { if (!isNaN(r.step) && !isNaN(r.value)) map[r.run].push({ step:r.step, value:r.value }); });
561
  dataByMetric.set(m, map);
562
  });
563
 
 
177
  const gRoot = svg.append('g');
178
  const gGrid = gRoot.append('g').attr('class', 'grid');
179
  const gAxes = gRoot.append('g').attr('class', 'axes');
180
+ const gAreas = gRoot.append('g').attr('class', 'areas');
181
  const gLines = gRoot.append('g').attr('class', 'lines');
182
  const gPoints = gRoot.append('g').attr('class', 'points');
183
  const gHover = gRoot.append('g').attr('class', 'hover');
 
426
  values: (map[r]||[])
427
  .slice()
428
  .sort((a,b)=>a.step-b.step)
429
+ .map(pt => isRankStrict ? { step: pt.step, value: Math.round(pt.value), stderr: pt.stderr } : pt)
430
  }));
431
 
432
+ // Uncertainty area (± stderr) for non-rank metrics
433
+ gAreas.selectAll('*').remove();
434
+ if (!isRank) {
435
+ series.forEach((s) => {
436
+ const withErr = s.values.filter(v => v && v.stderr != null && isFinite(v.stderr) && v.stderr > 0 && isFinite(v.value));
437
+ if (withErr.length === 0) return;
438
+ const upper = withErr.map(d => [xScale(d.step), yScale(d.value + d.stderr)]);
439
+ const lower = withErr.slice().reverse().map(d => [xScale(d.step), yScale(d.value - d.stderr)]);
440
+ const coords = upper.concat(lower);
441
+ const pathData = d3.line().x(d=>d[0]).y(d=>d[1]).curve(d3.curveLinearClosed)(coords);
442
+ gAreas.append('path')
443
+ .attr('d', pathData)
444
+ .attr('fill', s.color)
445
+ .attr('opacity', 0.15)
446
+ .attr('stroke', 'none');
447
+ });
448
+ }
449
+
450
  // Draw lines
451
  const paths = gLines.selectAll('path.run-line').data(series, d=>d.run);
452
  paths.enter().append('path').attr('class','run-line').attr('fill','none').attr('stroke-width',2)
 
547
  // Tooltip content
548
  let html = `<div><strong>${getMetricDisplayName(metricKey)}</strong></div><div><strong>step</strong> ${nearest}</div>`;
549
  series.forEach(s=>{
550
+ const m = new Map(s.values.map(v=>[v.step, v]));
551
+ const pt = m.get(nearest);
552
+ if (pt && pt.value != null) {
553
  const formatVal = (vv) => (isRankStrict ? d3.format('d')(vv) : (+vv).toFixed(4));
554
+ const errTxt = (pt.stderr != null && isFinite(pt.stderr) && pt.stderr > 0) ? ` ± ${formatVal(pt.stderr)}` : '';
555
+ html += `<div><span style=\"display:inline-block;width:10px;height:10px;background:${s.color};border-radius:50%;margin-right:6px;\"></span><strong>${s.run}</strong> ${formatVal(pt.value)}${errTxt}</div>`;
556
  }
557
  });
558
  tipInner.innerHTML = html;
 
569
  (async () => {
570
  try {
571
  const text = await fetchFirstAvailable(CSV_PATHS);
572
+ const rows = d3.csvParse(text, d => ({
573
+ run: (d.run||'').trim(),
574
+ step: +d.step,
575
+ metric: (d.metric||'').trim(),
576
+ value: +d.value,
577
+ stderr: (d.stderr != null && d.stderr !== '') ? +d.stderr : null
578
+ }));
579
  metricList = Array.from(new Set(rows.map(r=>r.metric))).sort();
580
  runList = Array.from(new Set(rows.map(r=>r.run))).sort();
581
  runOrder = runList;
 
583
  metricList.forEach(m => {
584
  const map = {};
585
  runList.forEach(r => { map[r] = []; });
586
+ rows.filter(r=>r.metric===m).forEach(r => { if (!isNaN(r.step) && !isNaN(r.value)) map[r.run].push({ step:r.step, value:r.value, stderr:r.stderr }); });
587
  dataByMetric.set(m, map);
588
  });
589
 
app/src/content/embeds/internal-deduplication.html CHANGED
@@ -177,6 +177,7 @@
177
  const gRoot = svg.append('g');
178
  const gGrid = gRoot.append('g').attr('class', 'grid');
179
  const gAxes = gRoot.append('g').attr('class', 'axes');
 
180
  const gLines = gRoot.append('g').attr('class', 'lines');
181
  const gPoints = gRoot.append('g').attr('class', 'points');
182
  const gHover = gRoot.append('g').attr('class', 'hover');
@@ -425,9 +426,27 @@
425
  values: (map[r]||[])
426
  .slice()
427
  .sort((a,b)=>a.step-b.step)
428
- .map(pt => isRankStrict ? { step: pt.step, value: Math.round(pt.value) } : pt)
429
  }));
430
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
431
  // Draw lines
432
  const paths = gLines.selectAll('path.run-line').data(series, d=>d.run);
433
  paths.enter().append('path').attr('class','run-line').attr('fill','none').attr('stroke-width',2)
@@ -528,11 +547,12 @@
528
  // Tooltip content
529
  let html = `<div><strong>${getMetricDisplayName(metricKey)}</strong></div><div><strong>step</strong> ${nearest}</div>`;
530
  series.forEach(s=>{
531
- const m = new Map(s.values.map(v=>[v.step, v.value]));
532
- const val = m.has(nearest) ? m.get(nearest) : null;
533
- if (val != null) {
534
  const formatVal = (vv) => (isRankStrict ? d3.format('d')(vv) : (+vv).toFixed(4));
535
- html += `<div><span style=\"display:inline-block;width:10px;height:10px;background:${s.color};border-radius:50%;margin-right:6px;\"></span><strong>${s.run}</strong> ${formatVal(val)}</div>`;
 
536
  }
537
  });
538
  tipInner.innerHTML = html;
@@ -549,7 +569,13 @@
549
  (async () => {
550
  try {
551
  const text = await fetchFirstAvailable(CSV_PATHS);
552
- const rows = d3.csvParse(text, d => ({ run: (d.run||'').trim(), step: +d.step, metric: (d.metric||'').trim(), value: +d.value }));
 
 
 
 
 
 
553
  metricList = Array.from(new Set(rows.map(r=>r.metric))).sort();
554
  runList = Array.from(new Set(rows.map(r=>r.run))).sort();
555
  runOrder = runList;
@@ -557,7 +583,7 @@
557
  metricList.forEach(m => {
558
  const map = {};
559
  runList.forEach(r => { map[r] = []; });
560
- rows.filter(r=>r.metric===m).forEach(r => { if (!isNaN(r.step) && !isNaN(r.value)) map[r.run].push({ step:r.step, value:r.value }); });
561
  dataByMetric.set(m, map);
562
  });
563
 
 
177
  const gRoot = svg.append('g');
178
  const gGrid = gRoot.append('g').attr('class', 'grid');
179
  const gAxes = gRoot.append('g').attr('class', 'axes');
180
+ const gAreas = gRoot.append('g').attr('class', 'areas');
181
  const gLines = gRoot.append('g').attr('class', 'lines');
182
  const gPoints = gRoot.append('g').attr('class', 'points');
183
  const gHover = gRoot.append('g').attr('class', 'hover');
 
426
  values: (map[r]||[])
427
  .slice()
428
  .sort((a,b)=>a.step-b.step)
429
+ .map(pt => isRankStrict ? { step: pt.step, value: Math.round(pt.value), stderr: pt.stderr } : pt)
430
  }));
431
 
432
+ // Uncertainty area (± stderr) for non-rank metrics
433
+ gAreas.selectAll('*').remove();
434
+ if (!isRank) {
435
+ series.forEach((s) => {
436
+ const withErr = s.values.filter(v => v && v.stderr != null && isFinite(v.stderr) && v.stderr > 0 && isFinite(v.value));
437
+ if (withErr.length === 0) return;
438
+ const upper = withErr.map(d => [xScale(d.step), yScale(d.value + d.stderr)]);
439
+ const lower = withErr.slice().reverse().map(d => [xScale(d.step), yScale(d.value - d.stderr)]);
440
+ const coords = upper.concat(lower);
441
+ const pathData = d3.line().x(d=>d[0]).y(d=>d[1]).curve(d3.curveLinearClosed)(coords);
442
+ gAreas.append('path')
443
+ .attr('d', pathData)
444
+ .attr('fill', s.color)
445
+ .attr('opacity', 0.15)
446
+ .attr('stroke', 'none');
447
+ });
448
+ }
449
+
450
  // Draw lines
451
  const paths = gLines.selectAll('path.run-line').data(series, d=>d.run);
452
  paths.enter().append('path').attr('class','run-line').attr('fill','none').attr('stroke-width',2)
 
547
  // Tooltip content
548
  let html = `<div><strong>${getMetricDisplayName(metricKey)}</strong></div><div><strong>step</strong> ${nearest}</div>`;
549
  series.forEach(s=>{
550
+ const m = new Map(s.values.map(v=>[v.step, v]));
551
+ const pt = m.get(nearest);
552
+ if (pt && pt.value != null) {
553
  const formatVal = (vv) => (isRankStrict ? d3.format('d')(vv) : (+vv).toFixed(4));
554
+ const errTxt = (pt.stderr != null && isFinite(pt.stderr) && pt.stderr > 0) ? ` ± ${formatVal(pt.stderr)}` : '';
555
+ html += `<div><span style=\"display:inline-block;width:10px;height:10px;background:${s.color};border-radius:50%;margin-right:6px;\"></span><strong>${s.run}</strong> ${formatVal(pt.value)}${errTxt}</div>`;
556
  }
557
  });
558
  tipInner.innerHTML = html;
 
569
  (async () => {
570
  try {
571
  const text = await fetchFirstAvailable(CSV_PATHS);
572
+ const rows = d3.csvParse(text, d => ({
573
+ run: (d.run||'').trim(),
574
+ step: +d.step,
575
+ metric: (d.metric||'').trim(),
576
+ value: +d.value,
577
+ stderr: (d.stderr != null && d.stderr !== '') ? +d.stderr : null
578
+ }));
579
  metricList = Array.from(new Set(rows.map(r=>r.metric))).sort();
580
  runList = Array.from(new Set(rows.map(r=>r.run))).sort();
581
  runOrder = runList;
 
583
  metricList.forEach(m => {
584
  const map = {};
585
  runList.forEach(r => { map[r] = []; });
586
+ rows.filter(r=>r.metric===m).forEach(r => { if (!isNaN(r.step) && !isNaN(r.value)) map[r.run].push({ step:r.step, value:r.value, stderr:r.stderr }); });
587
  dataByMetric.set(m, map);
588
  });
589
 
app/src/content/embeds/relevance-filters.html CHANGED
@@ -177,6 +177,7 @@
177
  const gRoot = svg.append('g');
178
  const gGrid = gRoot.append('g').attr('class', 'grid');
179
  const gAxes = gRoot.append('g').attr('class', 'axes');
 
180
  const gLines = gRoot.append('g').attr('class', 'lines');
181
  const gPoints = gRoot.append('g').attr('class', 'points');
182
  const gHover = gRoot.append('g').attr('class', 'hover');
@@ -425,9 +426,27 @@
425
  values: (map[r]||[])
426
  .slice()
427
  .sort((a,b)=>a.step-b.step)
428
- .map(pt => isRankStrict ? { step: pt.step, value: Math.round(pt.value) } : pt)
429
  }));
430
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
431
  // Draw lines
432
  const paths = gLines.selectAll('path.run-line').data(series, d=>d.run);
433
  paths.enter().append('path').attr('class','run-line').attr('fill','none').attr('stroke-width',2)
@@ -528,11 +547,12 @@
528
  // Tooltip content
529
  let html = `<div><strong>${getMetricDisplayName(metricKey)}</strong></div><div><strong>step</strong> ${nearest}</div>`;
530
  series.forEach(s=>{
531
- const m = new Map(s.values.map(v=>[v.step, v.value]));
532
- const val = m.has(nearest) ? m.get(nearest) : null;
533
- if (val != null) {
534
  const formatVal = (vv) => (isRankStrict ? d3.format('d')(vv) : (+vv).toFixed(4));
535
- html += `<div><span style=\"display:inline-block;width:10px;height:10px;background:${s.color};border-radius:50%;margin-right:6px;\"></span><strong>${s.run}</strong> ${formatVal(val)}</div>`;
 
536
  }
537
  });
538
  tipInner.innerHTML = html;
@@ -549,7 +569,13 @@
549
  (async () => {
550
  try {
551
  const text = await fetchFirstAvailable(CSV_PATHS);
552
- const rows = d3.csvParse(text, d => ({ run: (d.run||'').trim(), step: +d.step, metric: (d.metric||'').trim(), value: +d.value }));
 
 
 
 
 
 
553
  metricList = Array.from(new Set(rows.map(r=>r.metric))).sort();
554
  runList = Array.from(new Set(rows.map(r=>r.run))).sort();
555
  runOrder = runList;
@@ -557,7 +583,7 @@
557
  metricList.forEach(m => {
558
  const map = {};
559
  runList.forEach(r => { map[r] = []; });
560
- rows.filter(r=>r.metric===m).forEach(r => { if (!isNaN(r.step) && !isNaN(r.value)) map[r.run].push({ step:r.step, value:r.value }); });
561
  dataByMetric.set(m, map);
562
  });
563
 
 
177
  const gRoot = svg.append('g');
178
  const gGrid = gRoot.append('g').attr('class', 'grid');
179
  const gAxes = gRoot.append('g').attr('class', 'axes');
180
+ const gAreas = gRoot.append('g').attr('class', 'areas');
181
  const gLines = gRoot.append('g').attr('class', 'lines');
182
  const gPoints = gRoot.append('g').attr('class', 'points');
183
  const gHover = gRoot.append('g').attr('class', 'hover');
 
426
  values: (map[r]||[])
427
  .slice()
428
  .sort((a,b)=>a.step-b.step)
429
+ .map(pt => isRankStrict ? { step: pt.step, value: Math.round(pt.value), stderr: pt.stderr } : pt)
430
  }));
431
 
432
+ // Uncertainty area (± stderr) for non-rank metrics
433
+ gAreas.selectAll('*').remove();
434
+ if (!isRank) {
435
+ series.forEach((s) => {
436
+ const withErr = s.values.filter(v => v && v.stderr != null && isFinite(v.stderr) && v.stderr > 0 && isFinite(v.value));
437
+ if (withErr.length === 0) return;
438
+ const upper = withErr.map(d => [xScale(d.step), yScale(d.value + d.stderr)]);
439
+ const lower = withErr.slice().reverse().map(d => [xScale(d.step), yScale(d.value - d.stderr)]);
440
+ const coords = upper.concat(lower);
441
+ const pathData = d3.line().x(d=>d[0]).y(d=>d[1]).curve(d3.curveLinearClosed)(coords);
442
+ gAreas.append('path')
443
+ .attr('d', pathData)
444
+ .attr('fill', s.color)
445
+ .attr('opacity', 0.15)
446
+ .attr('stroke', 'none');
447
+ });
448
+ }
449
+
450
  // Draw lines
451
  const paths = gLines.selectAll('path.run-line').data(series, d=>d.run);
452
  paths.enter().append('path').attr('class','run-line').attr('fill','none').attr('stroke-width',2)
 
547
  // Tooltip content
548
  let html = `<div><strong>${getMetricDisplayName(metricKey)}</strong></div><div><strong>step</strong> ${nearest}</div>`;
549
  series.forEach(s=>{
550
+ const m = new Map(s.values.map(v=>[v.step, v]));
551
+ const pt = m.get(nearest);
552
+ if (pt && pt.value != null) {
553
  const formatVal = (vv) => (isRankStrict ? d3.format('d')(vv) : (+vv).toFixed(4));
554
+ const errTxt = (pt.stderr != null && isFinite(pt.stderr) && pt.stderr > 0) ? ` ± ${formatVal(pt.stderr)}` : '';
555
+ html += `<div><span style=\"display:inline-block;width:10px;height:10px;background:${s.color};border-radius:50%;margin-right:6px;\"></span><strong>${s.run}</strong> ${formatVal(pt.value)}${errTxt}</div>`;
556
  }
557
  });
558
  tipInner.innerHTML = html;
 
569
  (async () => {
570
  try {
571
  const text = await fetchFirstAvailable(CSV_PATHS);
572
+ const rows = d3.csvParse(text, d => ({
573
+ run: (d.run||'').trim(),
574
+ step: +d.step,
575
+ metric: (d.metric||'').trim(),
576
+ value: +d.value,
577
+ stderr: (d.stderr != null && d.stderr !== '') ? +d.stderr : null
578
+ }));
579
  metricList = Array.from(new Set(rows.map(r=>r.metric))).sort();
580
  runList = Array.from(new Set(rows.map(r=>r.run))).sort();
581
  runOrder = runList;
 
583
  metricList.forEach(m => {
584
  const map = {};
585
  runList.forEach(r => { map[r] = []; });
586
+ rows.filter(r=>r.metric===m).forEach(r => { if (!isNaN(r.step) && !isNaN(r.value)) map[r.run].push({ step:r.step, value:r.value, stderr:r.stderr }); });
587
  dataByMetric.set(m, map);
588
  });
589
 
app/src/content/embeds/remove-ch.html CHANGED
@@ -177,6 +177,7 @@
177
  const gRoot = svg.append('g');
178
  const gGrid = gRoot.append('g').attr('class', 'grid');
179
  const gAxes = gRoot.append('g').attr('class', 'axes');
 
180
  const gLines = gRoot.append('g').attr('class', 'lines');
181
  const gPoints = gRoot.append('g').attr('class', 'points');
182
  const gHover = gRoot.append('g').attr('class', 'hover');
@@ -425,9 +426,27 @@
425
  values: (map[r]||[])
426
  .slice()
427
  .sort((a,b)=>a.step-b.step)
428
- .map(pt => isRankStrict ? { step: pt.step, value: Math.round(pt.value) } : pt)
429
  }));
430
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
431
  // Draw lines
432
  const paths = gLines.selectAll('path.run-line').data(series, d=>d.run);
433
  paths.enter().append('path').attr('class','run-line').attr('fill','none').attr('stroke-width',2)
@@ -528,11 +547,12 @@
528
  // Tooltip content
529
  let html = `<div><strong>${getMetricDisplayName(metricKey)}</strong></div><div><strong>step</strong> ${nearest}</div>`;
530
  series.forEach(s=>{
531
- const m = new Map(s.values.map(v=>[v.step, v.value]));
532
- const val = m.has(nearest) ? m.get(nearest) : null;
533
- if (val != null) {
534
  const formatVal = (vv) => (isRankStrict ? d3.format('d')(vv) : (+vv).toFixed(4));
535
- html += `<div><span style=\"display:inline-block;width:10px;height:10px;background:${s.color};border-radius:50%;margin-right:6px;\"></span><strong>${s.run}</strong> ${formatVal(val)}</div>`;
 
536
  }
537
  });
538
  tipInner.innerHTML = html;
@@ -549,7 +569,13 @@
549
  (async () => {
550
  try {
551
  const text = await fetchFirstAvailable(CSV_PATHS);
552
- const rows = d3.csvParse(text, d => ({ run: (d.run||'').trim(), step: +d.step, metric: (d.metric||'').trim(), value: +d.value }));
 
 
 
 
 
 
553
  metricList = Array.from(new Set(rows.map(r=>r.metric))).sort();
554
  runList = Array.from(new Set(rows.map(r=>r.run))).sort();
555
  runOrder = runList;
@@ -557,7 +583,7 @@
557
  metricList.forEach(m => {
558
  const map = {};
559
  runList.forEach(r => { map[r] = []; });
560
- rows.filter(r=>r.metric===m).forEach(r => { if (!isNaN(r.step) && !isNaN(r.value)) map[r.run].push({ step:r.step, value:r.value }); });
561
  dataByMetric.set(m, map);
562
  });
563
 
 
177
  const gRoot = svg.append('g');
178
  const gGrid = gRoot.append('g').attr('class', 'grid');
179
  const gAxes = gRoot.append('g').attr('class', 'axes');
180
+ const gAreas = gRoot.append('g').attr('class', 'areas');
181
  const gLines = gRoot.append('g').attr('class', 'lines');
182
  const gPoints = gRoot.append('g').attr('class', 'points');
183
  const gHover = gRoot.append('g').attr('class', 'hover');
 
426
  values: (map[r]||[])
427
  .slice()
428
  .sort((a,b)=>a.step-b.step)
429
+ .map(pt => isRankStrict ? { step: pt.step, value: Math.round(pt.value), stderr: pt.stderr } : pt)
430
  }));
431
 
432
+ // Uncertainty area (± stderr) for non-rank metrics
433
+ gAreas.selectAll('*').remove();
434
+ if (!isRank) {
435
+ series.forEach((s) => {
436
+ const withErr = s.values.filter(v => v && v.stderr != null && isFinite(v.stderr) && v.stderr > 0 && isFinite(v.value));
437
+ if (withErr.length === 0) return;
438
+ const upper = withErr.map(d => [xScale(d.step), yScale(d.value + d.stderr)]);
439
+ const lower = withErr.slice().reverse().map(d => [xScale(d.step), yScale(d.value - d.stderr)]);
440
+ const coords = upper.concat(lower);
441
+ const pathData = d3.line().x(d=>d[0]).y(d=>d[1]).curve(d3.curveLinearClosed)(coords);
442
+ gAreas.append('path')
443
+ .attr('d', pathData)
444
+ .attr('fill', s.color)
445
+ .attr('opacity', 0.15)
446
+ .attr('stroke', 'none');
447
+ });
448
+ }
449
+
450
  // Draw lines
451
  const paths = gLines.selectAll('path.run-line').data(series, d=>d.run);
452
  paths.enter().append('path').attr('class','run-line').attr('fill','none').attr('stroke-width',2)
 
547
  // Tooltip content
548
  let html = `<div><strong>${getMetricDisplayName(metricKey)}</strong></div><div><strong>step</strong> ${nearest}</div>`;
549
  series.forEach(s=>{
550
+ const m = new Map(s.values.map(v=>[v.step, v]));
551
+ const pt = m.get(nearest);
552
+ if (pt && pt.value != null) {
553
  const formatVal = (vv) => (isRankStrict ? d3.format('d')(vv) : (+vv).toFixed(4));
554
+ const errTxt = (pt.stderr != null && isFinite(pt.stderr) && pt.stderr > 0) ? ` ± ${formatVal(pt.stderr)}` : '';
555
+ html += `<div><span style=\"display:inline-block;width:10px;height:10px;background:${s.color};border-radius:50%;margin-right:6px;\"></span><strong>${s.run}</strong> ${formatVal(pt.value)}${errTxt}</div>`;
556
  }
557
  });
558
  tipInner.innerHTML = html;
 
569
  (async () => {
570
  try {
571
  const text = await fetchFirstAvailable(CSV_PATHS);
572
+ const rows = d3.csvParse(text, d => ({
573
+ run: (d.run||'').trim(),
574
+ step: +d.step,
575
+ metric: (d.metric||'').trim(),
576
+ value: +d.value,
577
+ stderr: (d.stderr != null && d.stderr !== '') ? +d.stderr : null
578
+ }));
579
  metricList = Array.from(new Set(rows.map(r=>r.metric))).sort();
580
  runList = Array.from(new Set(rows.map(r=>r.run))).sort();
581
  runOrder = runList;
 
583
  metricList.forEach(m => {
584
  const map = {};
585
  runList.forEach(r => { map[r] = []; });
586
+ rows.filter(r=>r.metric===m).forEach(r => { if (!isNaN(r.step) && !isNaN(r.value)) map[r.run].push({ step:r.step, value:r.value, stderr:r.stderr }); });
587
  dataByMetric.set(m, map);
588
  });
589
 
app/src/content/embeds/s25-ratings.html CHANGED
@@ -177,6 +177,7 @@
177
  const gRoot = svg.append('g');
178
  const gGrid = gRoot.append('g').attr('class', 'grid');
179
  const gAxes = gRoot.append('g').attr('class', 'axes');
 
180
  const gLines = gRoot.append('g').attr('class', 'lines');
181
  const gPoints = gRoot.append('g').attr('class', 'points');
182
  const gHover = gRoot.append('g').attr('class', 'hover');
@@ -425,9 +426,27 @@
425
  values: (map[r]||[])
426
  .slice()
427
  .sort((a,b)=>a.step-b.step)
428
- .map(pt => isRankStrict ? { step: pt.step, value: Math.round(pt.value) } : pt)
429
  }));
430
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
431
  // Draw lines
432
  const paths = gLines.selectAll('path.run-line').data(series, d=>d.run);
433
  paths.enter().append('path').attr('class','run-line').attr('fill','none').attr('stroke-width',2)
@@ -528,11 +547,12 @@
528
  // Tooltip content
529
  let html = `<div><strong>${getMetricDisplayName(metricKey)}</strong></div><div><strong>step</strong> ${nearest}</div>`;
530
  series.forEach(s=>{
531
- const m = new Map(s.values.map(v=>[v.step, v.value]));
532
- const val = m.has(nearest) ? m.get(nearest) : null;
533
- if (val != null) {
534
  const formatVal = (vv) => (isRankStrict ? d3.format('d')(vv) : (+vv).toFixed(4));
535
- html += `<div><span style=\"display:inline-block;width:10px;height:10px;background:${s.color};border-radius:50%;margin-right:6px;\"></span><strong>${s.run}</strong> ${formatVal(val)}</div>`;
 
536
  }
537
  });
538
  tipInner.innerHTML = html;
@@ -549,7 +569,13 @@
549
  (async () => {
550
  try {
551
  const text = await fetchFirstAvailable(CSV_PATHS);
552
- const rows = d3.csvParse(text, d => ({ run: (d.run||'').trim(), step: +d.step, metric: (d.metric||'').trim(), value: +d.value }));
 
 
 
 
 
 
553
  metricList = Array.from(new Set(rows.map(r=>r.metric))).sort();
554
  runList = Array.from(new Set(rows.map(r=>r.run))).sort();
555
  runOrder = runList;
@@ -557,7 +583,7 @@
557
  metricList.forEach(m => {
558
  const map = {};
559
  runList.forEach(r => { map[r] = []; });
560
- rows.filter(r=>r.metric===m).forEach(r => { if (!isNaN(r.step) && !isNaN(r.value)) map[r.run].push({ step:r.step, value:r.value }); });
561
  dataByMetric.set(m, map);
562
  });
563
 
 
177
  const gRoot = svg.append('g');
178
  const gGrid = gRoot.append('g').attr('class', 'grid');
179
  const gAxes = gRoot.append('g').attr('class', 'axes');
180
+ const gAreas = gRoot.append('g').attr('class', 'areas');
181
  const gLines = gRoot.append('g').attr('class', 'lines');
182
  const gPoints = gRoot.append('g').attr('class', 'points');
183
  const gHover = gRoot.append('g').attr('class', 'hover');
 
426
  values: (map[r]||[])
427
  .slice()
428
  .sort((a,b)=>a.step-b.step)
429
+ .map(pt => isRankStrict ? { step: pt.step, value: Math.round(pt.value), stderr: pt.stderr } : pt)
430
  }));
431
 
432
+ // Uncertainty area (± stderr) for non-rank metrics
433
+ gAreas.selectAll('*').remove();
434
+ if (!isRank) {
435
+ series.forEach((s) => {
436
+ const withErr = s.values.filter(v => v && v.stderr != null && isFinite(v.stderr) && v.stderr > 0 && isFinite(v.value));
437
+ if (withErr.length === 0) return;
438
+ const upper = withErr.map(d => [xScale(d.step), yScale(d.value + d.stderr)]);
439
+ const lower = withErr.slice().reverse().map(d => [xScale(d.step), yScale(d.value - d.stderr)]);
440
+ const coords = upper.concat(lower);
441
+ const pathData = d3.line().x(d=>d[0]).y(d=>d[1]).curve(d3.curveLinearClosed)(coords);
442
+ gAreas.append('path')
443
+ .attr('d', pathData)
444
+ .attr('fill', s.color)
445
+ .attr('opacity', 0.15)
446
+ .attr('stroke', 'none');
447
+ });
448
+ }
449
+
450
  // Draw lines
451
  const paths = gLines.selectAll('path.run-line').data(series, d=>d.run);
452
  paths.enter().append('path').attr('class','run-line').attr('fill','none').attr('stroke-width',2)
 
547
  // Tooltip content
548
  let html = `<div><strong>${getMetricDisplayName(metricKey)}</strong></div><div><strong>step</strong> ${nearest}</div>`;
549
  series.forEach(s=>{
550
+ const m = new Map(s.values.map(v=>[v.step, v]));
551
+ const pt = m.get(nearest);
552
+ if (pt && pt.value != null) {
553
  const formatVal = (vv) => (isRankStrict ? d3.format('d')(vv) : (+vv).toFixed(4));
554
+ const errTxt = (pt.stderr != null && isFinite(pt.stderr) && pt.stderr > 0) ? ` ± ${formatVal(pt.stderr)}` : '';
555
+ html += `<div><span style=\"display:inline-block;width:10px;height:10px;background:${s.color};border-radius:50%;margin-right:6px;\"></span><strong>${s.run}</strong> ${formatVal(pt.value)}${errTxt}</div>`;
556
  }
557
  });
558
  tipInner.innerHTML = html;
 
569
  (async () => {
570
  try {
571
  const text = await fetchFirstAvailable(CSV_PATHS);
572
+ const rows = d3.csvParse(text, d => ({
573
+ run: (d.run||'').trim(),
574
+ step: +d.step,
575
+ metric: (d.metric||'').trim(),
576
+ value: +d.value,
577
+ stderr: (d.stderr != null && d.stderr !== '') ? +d.stderr : null
578
+ }));
579
  metricList = Array.from(new Set(rows.map(r=>r.metric))).sort();
580
  runList = Array.from(new Set(rows.map(r=>r.run))).sort();
581
  runOrder = runList;
 
583
  metricList.forEach(m => {
584
  const map = {};
585
  runList.forEach(r => { map[r] = []; });
586
+ rows.filter(r=>r.metric===m).forEach(r => { if (!isNaN(r.step) && !isNaN(r.value)) map[r.run].push({ step:r.step, value:r.value, stderr:r.stderr }); });
587
  dataByMetric.set(m, map);
588
  });
589
 
app/src/content/embeds/ss-vs-s1.html CHANGED
@@ -177,6 +177,7 @@
177
  const gRoot = svg.append('g');
178
  const gGrid = gRoot.append('g').attr('class', 'grid');
179
  const gAxes = gRoot.append('g').attr('class', 'axes');
 
180
  const gLines = gRoot.append('g').attr('class', 'lines');
181
  const gPoints = gRoot.append('g').attr('class', 'points');
182
  const gHover = gRoot.append('g').attr('class', 'hover');
@@ -425,9 +426,27 @@
425
  values: (map[r]||[])
426
  .slice()
427
  .sort((a,b)=>a.step-b.step)
428
- .map(pt => isRankStrict ? { step: pt.step, value: Math.round(pt.value) } : pt)
429
  }));
430
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
431
  // Draw lines
432
  const paths = gLines.selectAll('path.run-line').data(series, d=>d.run);
433
  paths.enter().append('path').attr('class','run-line').attr('fill','none').attr('stroke-width',2)
@@ -528,11 +547,12 @@
528
  // Tooltip content
529
  let html = `<div><strong>${getMetricDisplayName(metricKey)}</strong></div><div><strong>step</strong> ${nearest}</div>`;
530
  series.forEach(s=>{
531
- const m = new Map(s.values.map(v=>[v.step, v.value]));
532
- const val = m.has(nearest) ? m.get(nearest) : null;
533
- if (val != null) {
534
  const formatVal = (vv) => (isRankStrict ? d3.format('d')(vv) : (+vv).toFixed(4));
535
- html += `<div><span style=\"display:inline-block;width:10px;height:10px;background:${s.color};border-radius:50%;margin-right:6px;\"></span><strong>${s.run}</strong> ${formatVal(val)}</div>`;
 
536
  }
537
  });
538
  tipInner.innerHTML = html;
@@ -549,7 +569,13 @@
549
  (async () => {
550
  try {
551
  const text = await fetchFirstAvailable(CSV_PATHS);
552
- const rows = d3.csvParse(text, d => ({ run: (d.run||'').trim(), step: +d.step, metric: (d.metric||'').trim(), value: +d.value }));
 
 
 
 
 
 
553
  metricList = Array.from(new Set(rows.map(r=>r.metric))).sort();
554
  runList = Array.from(new Set(rows.map(r=>r.run))).sort();
555
  runOrder = runList;
@@ -557,7 +583,7 @@
557
  metricList.forEach(m => {
558
  const map = {};
559
  runList.forEach(r => { map[r] = []; });
560
- rows.filter(r=>r.metric===m).forEach(r => { if (!isNaN(r.step) && !isNaN(r.value)) map[r.run].push({ step:r.step, value:r.value }); });
561
  dataByMetric.set(m, map);
562
  });
563
 
 
177
  const gRoot = svg.append('g');
178
  const gGrid = gRoot.append('g').attr('class', 'grid');
179
  const gAxes = gRoot.append('g').attr('class', 'axes');
180
+ const gAreas = gRoot.append('g').attr('class', 'areas');
181
  const gLines = gRoot.append('g').attr('class', 'lines');
182
  const gPoints = gRoot.append('g').attr('class', 'points');
183
  const gHover = gRoot.append('g').attr('class', 'hover');
 
426
  values: (map[r]||[])
427
  .slice()
428
  .sort((a,b)=>a.step-b.step)
429
+ .map(pt => isRankStrict ? { step: pt.step, value: Math.round(pt.value), stderr: pt.stderr } : pt)
430
  }));
431
 
432
+ // Uncertainty area (± stderr) for non-rank metrics
433
+ gAreas.selectAll('*').remove();
434
+ if (!isRank) {
435
+ series.forEach((s) => {
436
+ const withErr = s.values.filter(v => v && v.stderr != null && isFinite(v.stderr) && v.stderr > 0 && isFinite(v.value));
437
+ if (withErr.length === 0) return;
438
+ const upper = withErr.map(d => [xScale(d.step), yScale(d.value + d.stderr)]);
439
+ const lower = withErr.slice().reverse().map(d => [xScale(d.step), yScale(d.value - d.stderr)]);
440
+ const coords = upper.concat(lower);
441
+ const pathData = d3.line().x(d=>d[0]).y(d=>d[1]).curve(d3.curveLinearClosed)(coords);
442
+ gAreas.append('path')
443
+ .attr('d', pathData)
444
+ .attr('fill', s.color)
445
+ .attr('opacity', 0.15)
446
+ .attr('stroke', 'none');
447
+ });
448
+ }
449
+
450
  // Draw lines
451
  const paths = gLines.selectAll('path.run-line').data(series, d=>d.run);
452
  paths.enter().append('path').attr('class','run-line').attr('fill','none').attr('stroke-width',2)
 
547
  // Tooltip content
548
  let html = `<div><strong>${getMetricDisplayName(metricKey)}</strong></div><div><strong>step</strong> ${nearest}</div>`;
549
  series.forEach(s=>{
550
+ const m = new Map(s.values.map(v=>[v.step, v]));
551
+ const pt = m.get(nearest);
552
+ if (pt && pt.value != null) {
553
  const formatVal = (vv) => (isRankStrict ? d3.format('d')(vv) : (+vv).toFixed(4));
554
+ const errTxt = (pt.stderr != null && isFinite(pt.stderr) && pt.stderr > 0) ? ` ± ${formatVal(pt.stderr)}` : '';
555
+ html += `<div><span style=\"display:inline-block;width:10px;height:10px;background:${s.color};border-radius:50%;margin-right:6px;\"></span><strong>${s.run}</strong> ${formatVal(pt.value)}${errTxt}</div>`;
556
  }
557
  });
558
  tipInner.innerHTML = html;
 
569
  (async () => {
570
  try {
571
  const text = await fetchFirstAvailable(CSV_PATHS);
572
+ const rows = d3.csvParse(text, d => ({
573
+ run: (d.run||'').trim(),
574
+ step: +d.step,
575
+ metric: (d.metric||'').trim(),
576
+ value: +d.value,
577
+ stderr: (d.stderr != null && d.stderr !== '') ? +d.stderr : null
578
+ }));
579
  metricList = Array.from(new Set(rows.map(r=>r.metric))).sort();
580
  runList = Array.from(new Set(rows.map(r=>r.run))).sort();
581
  runOrder = runList;
 
583
  metricList.forEach(m => {
584
  const map = {};
585
  runList.forEach(r => { map[r] = []; });
586
+ rows.filter(r=>r.metric===m).forEach(r => { if (!isNaN(r.step) && !isNaN(r.value)) map[r.run].push({ step:r.step, value:r.value, stderr:r.stderr }); });
587
  dataByMetric.set(m, map);
588
  });
589
 
app/src/content/embeds/visual-dependency-filters.html CHANGED
@@ -177,6 +177,7 @@
177
  const gRoot = svg.append('g');
178
  const gGrid = gRoot.append('g').attr('class', 'grid');
179
  const gAxes = gRoot.append('g').attr('class', 'axes');
 
180
  const gLines = gRoot.append('g').attr('class', 'lines');
181
  const gPoints = gRoot.append('g').attr('class', 'points');
182
  const gHover = gRoot.append('g').attr('class', 'hover');
@@ -425,9 +426,27 @@
425
  values: (map[r]||[])
426
  .slice()
427
  .sort((a,b)=>a.step-b.step)
428
- .map(pt => isRankStrict ? { step: pt.step, value: Math.round(pt.value) } : pt)
429
  }));
430
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
431
  // Draw lines
432
  const paths = gLines.selectAll('path.run-line').data(series, d=>d.run);
433
  paths.enter().append('path').attr('class','run-line').attr('fill','none').attr('stroke-width',2)
@@ -528,11 +547,12 @@
528
  // Tooltip content
529
  let html = `<div><strong>${getMetricDisplayName(metricKey)}</strong></div><div><strong>step</strong> ${nearest}</div>`;
530
  series.forEach(s=>{
531
- const m = new Map(s.values.map(v=>[v.step, v.value]));
532
- const val = m.has(nearest) ? m.get(nearest) : null;
533
- if (val != null) {
534
  const formatVal = (vv) => (isRankStrict ? d3.format('d')(vv) : (+vv).toFixed(4));
535
- html += `<div><span style=\"display:inline-block;width:10px;height:10px;background:${s.color};border-radius:50%;margin-right:6px;\"></span><strong>${s.run}</strong> ${formatVal(val)}</div>`;
 
536
  }
537
  });
538
  tipInner.innerHTML = html;
@@ -549,7 +569,13 @@
549
  (async () => {
550
  try {
551
  const text = await fetchFirstAvailable(CSV_PATHS);
552
- const rows = d3.csvParse(text, d => ({ run: (d.run||'').trim(), step: +d.step, metric: (d.metric||'').trim(), value: +d.value }));
 
 
 
 
 
 
553
  metricList = Array.from(new Set(rows.map(r=>r.metric))).sort();
554
  runList = Array.from(new Set(rows.map(r=>r.run))).sort();
555
  runOrder = runList;
@@ -557,7 +583,7 @@
557
  metricList.forEach(m => {
558
  const map = {};
559
  runList.forEach(r => { map[r] = []; });
560
- rows.filter(r=>r.metric===m).forEach(r => { if (!isNaN(r.step) && !isNaN(r.value)) map[r.run].push({ step:r.step, value:r.value }); });
561
  dataByMetric.set(m, map);
562
  });
563
 
 
177
  const gRoot = svg.append('g');
178
  const gGrid = gRoot.append('g').attr('class', 'grid');
179
  const gAxes = gRoot.append('g').attr('class', 'axes');
180
+ const gAreas = gRoot.append('g').attr('class', 'areas');
181
  const gLines = gRoot.append('g').attr('class', 'lines');
182
  const gPoints = gRoot.append('g').attr('class', 'points');
183
  const gHover = gRoot.append('g').attr('class', 'hover');
 
426
  values: (map[r]||[])
427
  .slice()
428
  .sort((a,b)=>a.step-b.step)
429
+ .map(pt => isRankStrict ? { step: pt.step, value: Math.round(pt.value), stderr: pt.stderr } : pt)
430
  }));
431
 
432
+ // Uncertainty area (± stderr) for non-rank metrics
433
+ gAreas.selectAll('*').remove();
434
+ if (!isRank) {
435
+ series.forEach((s) => {
436
+ const withErr = s.values.filter(v => v && v.stderr != null && isFinite(v.stderr) && v.stderr > 0 && isFinite(v.value));
437
+ if (withErr.length === 0) return;
438
+ const upper = withErr.map(d => [xScale(d.step), yScale(d.value + d.stderr)]);
439
+ const lower = withErr.slice().reverse().map(d => [xScale(d.step), yScale(d.value - d.stderr)]);
440
+ const coords = upper.concat(lower);
441
+ const pathData = d3.line().x(d=>d[0]).y(d=>d[1]).curve(d3.curveLinearClosed)(coords);
442
+ gAreas.append('path')
443
+ .attr('d', pathData)
444
+ .attr('fill', s.color)
445
+ .attr('opacity', 0.15)
446
+ .attr('stroke', 'none');
447
+ });
448
+ }
449
+
450
  // Draw lines
451
  const paths = gLines.selectAll('path.run-line').data(series, d=>d.run);
452
  paths.enter().append('path').attr('class','run-line').attr('fill','none').attr('stroke-width',2)
 
547
  // Tooltip content
548
  let html = `<div><strong>${getMetricDisplayName(metricKey)}</strong></div><div><strong>step</strong> ${nearest}</div>`;
549
  series.forEach(s=>{
550
+ const m = new Map(s.values.map(v=>[v.step, v]));
551
+ const pt = m.get(nearest);
552
+ if (pt && pt.value != null) {
553
  const formatVal = (vv) => (isRankStrict ? d3.format('d')(vv) : (+vv).toFixed(4));
554
+ const errTxt = (pt.stderr != null && isFinite(pt.stderr) && pt.stderr > 0) ? ` ± ${formatVal(pt.stderr)}` : '';
555
+ html += `<div><span style=\"display:inline-block;width:10px;height:10px;background:${s.color};border-radius:50%;margin-right:6px;\"></span><strong>${s.run}</strong> ${formatVal(pt.value)}${errTxt}</div>`;
556
  }
557
  });
558
  tipInner.innerHTML = html;
 
569
  (async () => {
570
  try {
571
  const text = await fetchFirstAvailable(CSV_PATHS);
572
+ const rows = d3.csvParse(text, d => ({
573
+ run: (d.run||'').trim(),
574
+ step: +d.step,
575
+ metric: (d.metric||'').trim(),
576
+ value: +d.value,
577
+ stderr: (d.stderr != null && d.stderr !== '') ? +d.stderr : null
578
+ }));
579
  metricList = Array.from(new Set(rows.map(r=>r.metric))).sort();
580
  runList = Array.from(new Set(rows.map(r=>r.run))).sort();
581
  runOrder = runList;
 
583
  metricList.forEach(m => {
584
  const map = {};
585
  runList.forEach(r => { map[r] = []; });
586
+ rows.filter(r=>r.metric===m).forEach(r => { if (!isNaN(r.step) && !isNaN(r.value)) map[r.run].push({ step:r.step, value:r.value, stderr:r.stderr }); });
587
  dataByMetric.set(m, map);
588
  });
589