@photonviz/core 0.3.2 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -5718,7 +5718,7 @@ var Plot = class {
5718
5718
  this.container.setAttribute("aria-label", this.ariaLabel ?? this.describe());
5719
5719
  }
5720
5720
  /**
5721
- * Register a layer built outside core (e.g. `@photonviz/map`). Use with
5721
+ * Register a layer built outside core (a custom WebGL2 `Layer`). Use with
5722
5722
  * {@link context} to construct the layer against this plot's WebGL2 context.
5723
5723
  */
5724
5724
  add(layer) {
@@ -6559,7 +6559,7 @@ var Plot = class {
6559
6559
  const out = [];
6560
6560
  for (const l of this.layers) {
6561
6561
  const a = l;
6562
- if (typeof a.name === "string" && a.name && typeof a.colorCss === "string") {
6562
+ if (typeof a.name === "string" && a.name && a.name !== a.id && typeof a.colorCss === "string") {
6563
6563
  out.push({ name: a.name, colorCss: a.colorCss });
6564
6564
  }
6565
6565
  }
@@ -11418,6 +11418,586 @@ function lttb(x, y, threshold) {
11418
11418
  return { x: sx, y: sy };
11419
11419
  }
11420
11420
 
11421
+ // src/ml/metrics.ts
11422
+ function confusionMatrix(yTrue, yPred, classes) {
11423
+ const n = Math.min(yTrue.length, yPred.length);
11424
+ let c = classes ?? 0;
11425
+ if (!classes) {
11426
+ for (let i = 0; i < n; i++) c = Math.max(c, (yTrue[i] | 0) + 1, (yPred[i] | 0) + 1);
11427
+ }
11428
+ c = Math.max(1, c);
11429
+ const counts = new Float64Array(c * c);
11430
+ for (let i = 0; i < n; i++) {
11431
+ const t = yTrue[i] | 0, p = yPred[i] | 0;
11432
+ if (t < 0 || t >= c || p < 0 || p >= c) continue;
11433
+ counts[t * c + p] += 1;
11434
+ }
11435
+ const support = new Float64Array(c);
11436
+ const normalized = new Float64Array(c * c);
11437
+ for (let t = 0; t < c; t++) {
11438
+ let s = 0;
11439
+ for (let p = 0; p < c; p++) s += counts[t * c + p];
11440
+ support[t] = s;
11441
+ if (s > 0) for (let p = 0; p < c; p++) normalized[t * c + p] = counts[t * c + p] / s;
11442
+ }
11443
+ return { counts, normalized, support, classes: c };
11444
+ }
11445
+ function argsortDesc(scores, n) {
11446
+ const idx = new Array(n);
11447
+ for (let i = 0; i < n; i++) idx[i] = i;
11448
+ idx.sort((a, b) => scores[b] - scores[a]);
11449
+ return idx;
11450
+ }
11451
+ function rocCurve(scores, labels) {
11452
+ const n = Math.min(scores.length, labels.length);
11453
+ let P = 0, N = 0;
11454
+ for (let i = 0; i < n; i++) labels[i] ? P++ : N++;
11455
+ const idx = argsortDesc(scores, n);
11456
+ const fpr = [0], tpr = [0], thr = [Infinity];
11457
+ let tp = 0, fp = 0, prevF = 0, prevT = 0, auc = 0;
11458
+ for (let i = 0; i < n; ) {
11459
+ const t = scores[idx[i]];
11460
+ while (i < n && scores[idx[i]] === t) {
11461
+ labels[idx[i]] ? tp++ : fp++;
11462
+ i++;
11463
+ }
11464
+ const f = N ? fp / N : 0, tr = P ? tp / P : 0;
11465
+ auc += (f - prevF) * (tr + prevT) / 2;
11466
+ fpr.push(f);
11467
+ tpr.push(tr);
11468
+ thr.push(t);
11469
+ prevF = f;
11470
+ prevT = tr;
11471
+ }
11472
+ return {
11473
+ fpr: Float64Array.from(fpr),
11474
+ tpr: Float64Array.from(tpr),
11475
+ thresholds: Float64Array.from(thr),
11476
+ auc: P && N ? auc : NaN
11477
+ };
11478
+ }
11479
+ function prCurve(scores, labels) {
11480
+ const n = Math.min(scores.length, labels.length);
11481
+ let P = 0;
11482
+ for (let i = 0; i < n; i++) if (labels[i]) P++;
11483
+ const idx = argsortDesc(scores, n);
11484
+ const recall = [0], precision = [1], thr = [Infinity];
11485
+ let tp = 0, fp = 0, prevR = 0, ap = 0;
11486
+ for (let i = 0; i < n; ) {
11487
+ const t = scores[idx[i]];
11488
+ while (i < n && scores[idx[i]] === t) {
11489
+ labels[idx[i]] ? tp++ : fp++;
11490
+ i++;
11491
+ }
11492
+ const r = P ? tp / P : 0, p = tp + fp ? tp / (tp + fp) : 1;
11493
+ ap += (r - prevR) * p;
11494
+ recall.push(r);
11495
+ precision.push(p);
11496
+ thr.push(t);
11497
+ prevR = r;
11498
+ }
11499
+ return {
11500
+ recall: Float64Array.from(recall),
11501
+ precision: Float64Array.from(precision),
11502
+ thresholds: Float64Array.from(thr),
11503
+ ap: P ? ap : NaN,
11504
+ baseline: n ? P / n : 0
11505
+ };
11506
+ }
11507
+ function calibrationCurve(scores, labels, bins = 10) {
11508
+ const b = Math.max(1, bins | 0);
11509
+ const n = Math.min(scores.length, labels.length);
11510
+ const sumScore = new Float64Array(b), sumLabel = new Float64Array(b), count = new Float64Array(b);
11511
+ for (let i = 0; i < n; i++) {
11512
+ const s = scores[i];
11513
+ if (!Number.isFinite(s)) continue;
11514
+ let k = Math.floor(s * b);
11515
+ if (k >= b) k = b - 1;
11516
+ if (k < 0) k = 0;
11517
+ sumScore[k] += s;
11518
+ sumLabel[k] += labels[i] ? 1 : 0;
11519
+ count[k] += 1;
11520
+ }
11521
+ const meanPredicted = new Float64Array(b), fractionPositive = new Float64Array(b);
11522
+ let ece = 0;
11523
+ for (let k = 0; k < b; k++) {
11524
+ if (count[k] > 0) {
11525
+ const conf = sumScore[k] / count[k], acc = sumLabel[k] / count[k];
11526
+ meanPredicted[k] = conf;
11527
+ fractionPositive[k] = acc;
11528
+ ece += count[k] / Math.max(1, n) * Math.abs(acc - conf);
11529
+ } else {
11530
+ meanPredicted[k] = NaN;
11531
+ fractionPositive[k] = NaN;
11532
+ }
11533
+ }
11534
+ return { meanPredicted, fractionPositive, binCount: count, ece };
11535
+ }
11536
+ function emaSmooth(values, weight = 0.6) {
11537
+ const w = Math.min(0.999999, Math.max(0, weight));
11538
+ const n = values.length;
11539
+ const out = new Float64Array(n);
11540
+ let last = 0, num = 0;
11541
+ for (let i = 0; i < n; i++) {
11542
+ const v = values[i];
11543
+ if (!Number.isFinite(v)) {
11544
+ out[i] = v;
11545
+ continue;
11546
+ }
11547
+ last = last * w + (1 - w) * v;
11548
+ num++;
11549
+ out[i] = last / (1 - Math.pow(w, num));
11550
+ }
11551
+ return out;
11552
+ }
11553
+
11554
+ // src/ml/reduce.ts
11555
+ function standardize(data, n, d) {
11556
+ const out = new Float64Array(n * d);
11557
+ for (let j = 0; j < d; j++) {
11558
+ let mean = 0;
11559
+ for (let i = 0; i < n; i++) mean += data[i * d + j];
11560
+ mean /= Math.max(1, n);
11561
+ let varsum = 0;
11562
+ for (let i = 0; i < n; i++) {
11563
+ const dv = data[i * d + j] - mean;
11564
+ varsum += dv * dv;
11565
+ }
11566
+ const sd = Math.sqrt(varsum / Math.max(1, n - 1)) || 1;
11567
+ for (let i = 0; i < n; i++) out[i * d + j] = (data[i * d + j] - mean) / sd;
11568
+ }
11569
+ return out;
11570
+ }
11571
+ function pca(data, n, d, k = 2) {
11572
+ k = Math.max(1, Math.min(k, d));
11573
+ const mean = new Float64Array(d);
11574
+ for (let i = 0; i < n; i++) for (let j = 0; j < d; j++) mean[j] += data[i * d + j];
11575
+ for (let j = 0; j < d; j++) mean[j] /= Math.max(1, n);
11576
+ const Xc = new Float64Array(n * d);
11577
+ for (let i = 0; i < n; i++) for (let j = 0; j < d; j++) Xc[i * d + j] = data[i * d + j] - mean[j];
11578
+ const cov = new Float64Array(d * d);
11579
+ const denom = Math.max(1, n - 1);
11580
+ for (let i = 0; i < n; i++) {
11581
+ const row = i * d;
11582
+ for (let a = 0; a < d; a++) {
11583
+ const xa = Xc[row + a];
11584
+ if (xa === 0) continue;
11585
+ for (let b = a; b < d; b++) cov[a * d + b] += xa * Xc[row + b];
11586
+ }
11587
+ }
11588
+ let totalVar = 0;
11589
+ for (let a = 0; a < d; a++) {
11590
+ for (let b = a; b < d; b++) {
11591
+ const v2 = cov[a * d + b] / denom;
11592
+ cov[a * d + b] = v2;
11593
+ cov[b * d + a] = v2;
11594
+ }
11595
+ totalVar += cov[a * d + a];
11596
+ }
11597
+ const components = new Float64Array(k * d);
11598
+ const explained = new Float64Array(k);
11599
+ const v = new Float64Array(d), w = new Float64Array(d);
11600
+ for (let c = 0; c < k; c++) {
11601
+ for (let j = 0; j < d; j++) v[j] = Math.sin(1 + j * (c + 1)) + 0.5;
11602
+ normalize3(v, d);
11603
+ let eig = 0;
11604
+ for (let iter = 0; iter < 256; iter++) {
11605
+ for (let a = 0; a < d; a++) {
11606
+ let s = 0;
11607
+ const ra = a * d;
11608
+ for (let b = 0; b < d; b++) s += cov[ra + b] * v[b];
11609
+ w[a] = s;
11610
+ }
11611
+ const norm2 = normalize3(w, d);
11612
+ let dot = 0;
11613
+ for (let a = 0; a < d; a++) dot += w[a] * v[a];
11614
+ for (let a = 0; a < d; a++) v[a] = w[a];
11615
+ if (Math.abs(Math.abs(dot) - 1) < 1e-9) {
11616
+ eig = norm2;
11617
+ break;
11618
+ }
11619
+ eig = norm2;
11620
+ }
11621
+ for (let j = 0; j < d; j++) components[c * d + j] = v[j];
11622
+ explained[c] = totalVar > 0 ? eig / totalVar : 0;
11623
+ for (let a = 0; a < d; a++) {
11624
+ const va = eig * v[a];
11625
+ for (let b = 0; b < d; b++) cov[a * d + b] -= va * v[b];
11626
+ }
11627
+ }
11628
+ const scores = new Float64Array(n * k);
11629
+ for (let i = 0; i < n; i++) {
11630
+ const row = i * d;
11631
+ for (let c = 0; c < k; c++) {
11632
+ let s = 0;
11633
+ const cr = c * d;
11634
+ for (let j = 0; j < d; j++) s += Xc[row + j] * components[cr + j];
11635
+ scores[i * k + c] = s;
11636
+ }
11637
+ }
11638
+ return { scores, components, explained, mean, n, d, k };
11639
+ }
11640
+ function normalize3(vec, d) {
11641
+ let s = 0;
11642
+ for (let j = 0; j < d; j++) s += vec[j] * vec[j];
11643
+ const norm2 = Math.sqrt(s);
11644
+ if (norm2 > 0) for (let j = 0; j < d; j++) vec[j] /= norm2;
11645
+ return norm2;
11646
+ }
11647
+
11648
+ // src/ml/charts.ts
11649
+ var ML_PALETTE = [
11650
+ "#4e79a7",
11651
+ "#f28e2b",
11652
+ "#e15759",
11653
+ "#76b7b2",
11654
+ "#59a14f",
11655
+ "#edc948",
11656
+ "#b07aa1",
11657
+ "#ff9da7",
11658
+ "#9c755f",
11659
+ "#bab0ac"
11660
+ ];
11661
+ function addConfusionMatrix(plot, opts) {
11662
+ const cm = confusionMatrix(opts.yTrue, opts.yPred, opts.classes);
11663
+ const C = cm.classes;
11664
+ const src = opts.normalize ? cm.normalized : cm.counts;
11665
+ const values = new Float64Array(C * C);
11666
+ for (let hr = 0; hr < C; hr++) {
11667
+ const t = C - 1 - hr;
11668
+ for (let p = 0; p < C; p++) values[hr * C + p] = src[t * C + p];
11669
+ }
11670
+ const heatmap = plot.addHeatmap({
11671
+ values,
11672
+ cols: C,
11673
+ rows: C,
11674
+ extent: { x: [0, C], y: [0, C] },
11675
+ colormap: opts.colormap ?? "viridis"
11676
+ });
11677
+ if (opts.annotate ?? true) {
11678
+ let maxV = 0;
11679
+ for (let i = 0; i < src.length; i++) if (src[i] > maxV) maxV = src[i];
11680
+ for (let hr = 0; hr < C; hr++) {
11681
+ const t = C - 1 - hr;
11682
+ for (let p = 0; p < C; p++) {
11683
+ const text = opts.normalize ? src[t * C + p].toFixed(2) : String(cm.counts[t * C + p]);
11684
+ const hot = maxV ? src[t * C + p] / maxV > 0.55 : false;
11685
+ plot.addAnnotation({ type: "label", x: p + 0.5, y: hr + 0.5, text, align: "center", color: hot ? "#0b1020" : "#e5e7eb" });
11686
+ }
11687
+ }
11688
+ }
11689
+ return { heatmap, classes: C };
11690
+ }
11691
+ function addRocCurve(plot, opts) {
11692
+ const roc = rocCurve(opts.scores, opts.labels);
11693
+ const color = opts.color ?? "#60a5fa";
11694
+ const area2 = opts.fill ? plot.addArea({ x: roc.fpr, y: roc.tpr, base: 0, color: withAlpha(color, 0.18) }) : void 0;
11695
+ if (opts.showChance ?? true) {
11696
+ plot.addAnnotation({ type: "line", x0: 0, y0: 0, x1: 1, y1: 1, color: "#8b93a7", width: 1, dash: [6, 6] });
11697
+ }
11698
+ const line = plot.addLine({
11699
+ x: roc.fpr,
11700
+ y: roc.tpr,
11701
+ color,
11702
+ width: 2,
11703
+ name: opts.name ?? `ROC \xB7 AUC ${Number.isFinite(roc.auc) ? roc.auc.toFixed(3) : "\u2014"}`
11704
+ });
11705
+ return { line, area: area2, auc: roc.auc };
11706
+ }
11707
+ function addPrCurve(plot, opts) {
11708
+ const pr = prCurve(opts.scores, opts.labels);
11709
+ const color = opts.color ?? "#f472b6";
11710
+ const area2 = opts.fill ? plot.addArea({ x: pr.recall, y: pr.precision, base: 0, color: withAlpha(color, 0.18) }) : void 0;
11711
+ if (opts.showBaseline ?? true) {
11712
+ plot.addAnnotation({ type: "line", x0: 0, y0: pr.baseline, x1: 1, y1: pr.baseline, color: "#8b93a7", width: 1, dash: [6, 6] });
11713
+ }
11714
+ const line = plot.addLine({
11715
+ x: pr.recall,
11716
+ y: pr.precision,
11717
+ color,
11718
+ width: 2,
11719
+ name: opts.name ?? `PR \xB7 AP ${Number.isFinite(pr.ap) ? pr.ap.toFixed(3) : "\u2014"}`
11720
+ });
11721
+ return { line, area: area2, ap: pr.ap };
11722
+ }
11723
+ function addCalibration(plot, opts) {
11724
+ const cal = calibrationCurve(opts.scores, opts.labels, opts.bins ?? 10);
11725
+ const xs = [], ys = [];
11726
+ for (let i = 0; i < cal.meanPredicted.length; i++) {
11727
+ if (Number.isFinite(cal.meanPredicted[i])) {
11728
+ xs.push(cal.meanPredicted[i]);
11729
+ ys.push(cal.fractionPositive[i]);
11730
+ }
11731
+ }
11732
+ plot.addAnnotation({ type: "line", x0: 0, y0: 0, x1: 1, y1: 1, color: "#8b93a7", width: 1, dash: [6, 6] });
11733
+ const color = opts.color ?? "#34d399";
11734
+ const line = plot.addLine({ x: xs, y: ys, color, width: 2, name: opts.name ?? `reliability \xB7 ECE ${cal.ece.toFixed(3)}` });
11735
+ const points = plot.addScatter({ x: xs, y: ys, color, size: 7 });
11736
+ return { line, points, ece: cal.ece };
11737
+ }
11738
+ function addEmbedding(plot, opts) {
11739
+ const size = opts.size ?? 4;
11740
+ if (opts.labels) {
11741
+ return { layers: scatterByClass(plot, opts.x, opts.y, opts.labels, opts.classNames, opts.palette, size, opts.text, opts.renderType) };
11742
+ }
11743
+ if (opts.colorBy) {
11744
+ return {
11745
+ layers: [plot.addScatter({
11746
+ x: opts.x,
11747
+ y: opts.y,
11748
+ size,
11749
+ name: opts.name,
11750
+ labels: opts.text,
11751
+ renderType: opts.renderType,
11752
+ colorBy: { values: opts.colorBy, colormap: opts.colormap ?? "viridis" }
11753
+ })]
11754
+ };
11755
+ }
11756
+ return { layers: [plot.addScatter({ x: opts.x, y: opts.y, size, color: (opts.palette ?? ML_PALETTE)[0], name: opts.name, labels: opts.text, renderType: opts.renderType })] };
11757
+ }
11758
+ function addDecisionBoundary(plot, opts) {
11759
+ const heatmap = plot.addHeatmap({
11760
+ values: opts.values,
11761
+ cols: opts.cols,
11762
+ rows: opts.rows,
11763
+ extent: opts.extent,
11764
+ colormap: opts.colormap ?? "viridis",
11765
+ domain: opts.domain
11766
+ });
11767
+ const p = opts.points;
11768
+ const points = p ? scatterByClass(plot, p.x, p.y, p.labels, p.classNames, p.palette, p.size ?? 5, void 0, void 0) : [];
11769
+ return { heatmap, points };
11770
+ }
11771
+ function addFeatureImportance(plot, opts) {
11772
+ const n = Math.min(opts.names.length, opts.values.length);
11773
+ let order = Array.from({ length: n }, (_, i) => i);
11774
+ if (opts.sort ?? true) order.sort((a, b) => Math.abs(opts.values[b]) - Math.abs(opts.values[a]));
11775
+ if (opts.top && opts.top < order.length) order = order.slice(0, opts.top);
11776
+ const m = order.length;
11777
+ const ypos = new Float64Array(m), vals = new Float64Array(m);
11778
+ for (let j = 0; j < m; j++) {
11779
+ ypos[j] = m - 1 - j;
11780
+ vals[j] = opts.values[order[j]];
11781
+ }
11782
+ const bars = plot.addBar({
11783
+ x: ypos,
11784
+ y: vals,
11785
+ orientation: "h",
11786
+ base: 0,
11787
+ width: 0.72,
11788
+ color: opts.color ?? "#60a5fa",
11789
+ name: opts.name,
11790
+ renderType: opts.renderType
11791
+ });
11792
+ if (opts.annotate ?? true) {
11793
+ for (let j = 0; j < m; j++) {
11794
+ plot.addAnnotation({ type: "label", x: 0, y: m - 1 - j, text: ` ${opts.names[order[j]]}`, align: "left", color: "#e5e7eb" });
11795
+ }
11796
+ }
11797
+ return { bars, order };
11798
+ }
11799
+ function addShapBeeswarm(plot, opts) {
11800
+ const f = Math.min(opts.values.length, opts.names.length);
11801
+ const meanAbs = (row) => {
11802
+ let s = 0;
11803
+ for (const v of row) s += Math.abs(v);
11804
+ return row.length ? s / row.length : 0;
11805
+ };
11806
+ const order = Array.from({ length: f }, (_, i) => i).sort((a, b) => meanAbs(opts.values[b]) - meanAbs(opts.values[a]));
11807
+ const spread = opts.spread ?? 0.8;
11808
+ const xs = [], ys = [], cs = [];
11809
+ let minX = Infinity;
11810
+ for (let band = 0; band < f; band++) {
11811
+ const feat = order[f - 1 - band];
11812
+ const row = opts.values[feat];
11813
+ const offsets = beeswarmLayout(row, { spread });
11814
+ const fv = opts.featureValues?.[feat];
11815
+ for (let i = 0; i < row.length; i++) {
11816
+ xs.push(row[i]);
11817
+ ys.push(band + offsets[i]);
11818
+ cs.push(fv ? fv[i] : 0);
11819
+ if (row[i] < minX) minX = row[i];
11820
+ }
11821
+ }
11822
+ const scatter = plot.addScatter({
11823
+ x: xs,
11824
+ y: ys,
11825
+ size: opts.size ?? 4,
11826
+ name: opts.name,
11827
+ colorBy: { values: cs, colormap: opts.colormap ?? "coolwarm" }
11828
+ });
11829
+ if (Number.isFinite(minX)) {
11830
+ for (let band = 0; band < f; band++) {
11831
+ plot.addAnnotation({ type: "label", x: minX, y: band, text: `${opts.names[order[f - 1 - band]]} `, align: "right", color: "#e5e7eb" });
11832
+ }
11833
+ }
11834
+ return { scatter, order };
11835
+ }
11836
+ function addPartialDependence(plot, opts) {
11837
+ const ice = (opts.ice ?? []).map((row) => plot.addLine({ x: opts.x, y: row, color: opts.iceColor ?? "rgba(148,163,184,0.25)", width: 1, renderType: opts.renderType }));
11838
+ const pd = plot.addLine({ x: opts.x, y: opts.pd, color: opts.color ?? "#f59e0b", width: 2.5, name: opts.name ?? "PDP", renderType: opts.renderType });
11839
+ return { pd, ice };
11840
+ }
11841
+ function addAttentionMap(plot, opts) {
11842
+ let flat, Q, K;
11843
+ if (Array.isArray(opts.weights) && Array.isArray(opts.weights[0])) {
11844
+ const w = opts.weights;
11845
+ Q = w.length;
11846
+ K = w[0].length;
11847
+ flat = new Float64Array(Q * K);
11848
+ for (let q = 0; q < Q; q++) for (let k = 0; k < K; k++) flat[q * K + k] = w[q][k];
11849
+ } else {
11850
+ Q = opts.queries ?? 0;
11851
+ K = opts.keys ?? 0;
11852
+ flat = Float64Array.from(opts.weights);
11853
+ }
11854
+ const values = new Float64Array(Q * K);
11855
+ for (let hr = 0; hr < Q; hr++) for (let k = 0; k < K; k++) values[hr * K + k] = flat[(Q - 1 - hr) * K + k];
11856
+ const heatmap = plot.addHeatmap({
11857
+ values,
11858
+ cols: K,
11859
+ rows: Q,
11860
+ extent: { x: [0, K], y: [0, Q] },
11861
+ colormap: opts.colormap ?? "viridis"
11862
+ });
11863
+ if (opts.annotate) {
11864
+ for (let hr = 0; hr < Q; hr++) for (let k = 0; k < K; k++) {
11865
+ plot.addAnnotation({ type: "label", x: k + 0.5, y: hr + 0.5, text: flat[(Q - 1 - hr) * K + k].toFixed(2), align: "center", color: "#e5e7eb" });
11866
+ }
11867
+ }
11868
+ return { heatmap, queries: Q, keys: K };
11869
+ }
11870
+ function addTrainingCurves(plot, opts) {
11871
+ const palette = opts.palette ?? ML_PALETTE;
11872
+ const smoothing = opts.smoothing ?? 0.6;
11873
+ const width = opts.width ?? 2;
11874
+ const raw = [], smoothed = [];
11875
+ opts.series.forEach((s, i) => {
11876
+ const n = s.y.length;
11877
+ const x = s.x ?? Float64Array.from({ length: n }, (_, j) => j);
11878
+ const color = s.color ?? palette[i % palette.length];
11879
+ if (smoothing > 0) {
11880
+ if (opts.showRaw ?? true) raw.push(plot.addLine({ x, y: s.y, color: withAlpha(color, 0.25), width: 1, renderType: opts.renderType }));
11881
+ smoothed.push(plot.addLine({ x, y: emaSmooth(s.y, smoothing), color, width, name: s.name, renderType: opts.renderType }));
11882
+ } else {
11883
+ smoothed.push(plot.addLine({ x, y: s.y, color, width, name: s.name, renderType: opts.renderType }));
11884
+ }
11885
+ if (opts.best) {
11886
+ let bi = -1, bv = opts.best === "min" ? Infinity : -Infinity;
11887
+ for (let j = 0; j < n; j++) {
11888
+ const v = s.y[j];
11889
+ if (!Number.isFinite(v)) continue;
11890
+ if (opts.best === "min" ? v < bv : v > bv) {
11891
+ bv = v;
11892
+ bi = j;
11893
+ }
11894
+ }
11895
+ if (bi >= 0) plot.addAnnotation({ type: "label", x: x[bi], y: bv, text: `${s.name ? s.name + " " : ""}${bv.toFixed(3)}`, align: "center", color });
11896
+ }
11897
+ });
11898
+ return { raw, smoothed };
11899
+ }
11900
+ function addRidgeline(plot, opts) {
11901
+ const g = opts.groups;
11902
+ let lo = opts.range?.[0] ?? Infinity, hi = opts.range?.[1] ?? -Infinity;
11903
+ if (!opts.range) {
11904
+ for (const grp of g) for (let i = 0; i < grp.values.length; i++) {
11905
+ const v = grp.values[i];
11906
+ if (v < lo) lo = v;
11907
+ if (v > hi) hi = v;
11908
+ }
11909
+ }
11910
+ if (!Number.isFinite(lo) || !Number.isFinite(hi) || lo === hi) {
11911
+ lo = (lo || 0) - 1;
11912
+ hi = (hi || 0) + 1;
11913
+ }
11914
+ const points = opts.points ?? 96;
11915
+ const palette = opts.palette ?? ML_PALETTE;
11916
+ const height = 1 + Math.max(0, opts.overlap ?? 1);
11917
+ const areas = [], lines = [];
11918
+ g.forEach((grp, i) => {
11919
+ const dens = kde(grp.values, lo, hi, points);
11920
+ let peak = 0;
11921
+ for (let j = 0; j < dens.ys.length; j++) if (dens.ys[j] > peak) peak = dens.ys[j];
11922
+ const scale = peak > 0 ? height / peak : 0;
11923
+ const y = new Float64Array(dens.ys.length);
11924
+ for (let j = 0; j < y.length; j++) y[j] = i + dens.ys[j] * scale;
11925
+ const color = palette[i % palette.length];
11926
+ if (opts.fill ?? true) areas.push(plot.addArea({ x: dens.xs, y, base: i, color: withAlpha(color, 0.55) }));
11927
+ lines.push(plot.addLine({ x: dens.xs, y, color, width: 1.5 }));
11928
+ if (grp.label) plot.addAnnotation({ type: "label", x: lo, y: i + 0.05, text: `${grp.label} `, align: "right", color: "#e5e7eb" });
11929
+ });
11930
+ return { areas, lines };
11931
+ }
11932
+ function beeswarmLayout(x, opts = {}) {
11933
+ const n = x.length;
11934
+ const spread = opts.spread ?? 0.8;
11935
+ const y = new Array(n).fill(0);
11936
+ if (!n) return y;
11937
+ let lo = Infinity, hi = -Infinity;
11938
+ for (let i = 0; i < n; i++) {
11939
+ const v = x[i];
11940
+ if (v < lo) lo = v;
11941
+ if (v > hi) hi = v;
11942
+ }
11943
+ const width = hi - lo || 1;
11944
+ const bins = Math.max(1, opts.bins ?? Math.min(64, Math.round(Math.sqrt(n))));
11945
+ const buckets = Array.from({ length: bins }, () => []);
11946
+ for (let i = 0; i < n; i++) {
11947
+ let b = Math.floor((x[i] - lo) / width * bins);
11948
+ if (b >= bins) b = bins - 1;
11949
+ if (b < 0) b = 0;
11950
+ buckets[b].push(i);
11951
+ }
11952
+ let maxCount = 1;
11953
+ for (const arr of buckets) if (arr.length > maxCount) maxCount = arr.length;
11954
+ const denom = Math.max(1, maxCount);
11955
+ for (const arr of buckets) {
11956
+ const m = arr.length;
11957
+ for (let j = 0; j < m; j++) {
11958
+ const off = j - (m - 1) / 2;
11959
+ y[arr[j]] = off / denom * spread;
11960
+ }
11961
+ }
11962
+ return y;
11963
+ }
11964
+ function scatterByClass(plot, x, y, labels, classNames, palette = ML_PALETTE, size = 4, text, renderType) {
11965
+ const n = Math.min(x.length, y.length);
11966
+ if (!labels) return [plot.addScatter({ x, y, size, color: palette[0], labels: text, renderType })];
11967
+ const keys = [];
11968
+ const seen = /* @__PURE__ */ new Set();
11969
+ for (let i = 0; i < n; i++) {
11970
+ const k = labels[i];
11971
+ const sk = String(k);
11972
+ if (!seen.has(sk)) {
11973
+ seen.add(sk);
11974
+ keys.push(k);
11975
+ }
11976
+ }
11977
+ if (keys.every((k) => typeof k === "number")) keys.sort((a, b) => a - b);
11978
+ const layers = [];
11979
+ keys.forEach((key, ci) => {
11980
+ const sx = [], sy = [], st = [];
11981
+ for (let i = 0; i < n; i++) {
11982
+ if (String(labels[i]) !== String(key)) continue;
11983
+ sx.push(x[i]);
11984
+ sy.push(y[i]);
11985
+ if (text) st.push(String(text[i]));
11986
+ }
11987
+ const name = classNames && typeof key === "number" ? classNames[key] ?? String(key) : String(key);
11988
+ layers.push(plot.addScatter({ x: sx, y: sy, size, color: palette[ci % palette.length], name, labels: text ? st : void 0, renderType }));
11989
+ });
11990
+ return layers;
11991
+ }
11992
+ function withAlpha(color, alpha) {
11993
+ const hex = /^#([0-9a-f]{6})$/i.exec(color);
11994
+ if (hex) {
11995
+ const v = parseInt(hex[1], 16);
11996
+ return `rgba(${v >> 16 & 255}, ${v >> 8 & 255}, ${v & 255}, ${alpha})`;
11997
+ }
11998
+ return color;
11999
+ }
12000
+
11421
12001
  // src/charts/treemap.ts
11422
12002
  var DEFAULT_EXTENT = { x: [0, 1], y: [0, 1] };
11423
12003
  var TREEMAP_PALETTE = [
@@ -12041,7 +12621,7 @@ function addChord(plot, opts) {
12041
12621
  const color = (i) => palette[i % palette.length];
12042
12622
  const patches = [];
12043
12623
  for (const rb of ribbons) {
12044
- patches.push({ x: rb.x, y: rb.y, color: withAlpha(color(rb.i), opacity) });
12624
+ patches.push({ x: rb.x, y: rb.y, color: withAlpha2(color(rb.i), opacity) });
12045
12625
  }
12046
12626
  for (const arc of groupArcs) {
12047
12627
  patches.push({ x: arc.x, y: arc.y, color: color(arc.i) });
@@ -12082,7 +12662,7 @@ function addChord(plot, opts) {
12082
12662
  }
12083
12663
  return layer;
12084
12664
  }
12085
- function withAlpha(color, alpha) {
12665
+ function withAlpha2(color, alpha) {
12086
12666
  const hex = /^#([0-9a-f]{6})$/i.exec(color);
12087
12667
  if (hex) {
12088
12668
  const v = parseInt(hex[1], 16);
@@ -12164,7 +12744,7 @@ function addParallelCoordinates(plot, opts) {
12164
12744
  (ln, r) => plot.addLine({
12165
12745
  x: ln.x,
12166
12746
  y: ln.y,
12167
- color: withAlpha2(rowColor(r), opacity),
12747
+ color: withAlpha3(rowColor(r), opacity),
12168
12748
  width,
12169
12749
  name: r === 0 ? opts.name : void 0,
12170
12750
  renderType: opts.renderType
@@ -12176,7 +12756,7 @@ function addParallelCoordinates(plot, opts) {
12176
12756
  }
12177
12757
  return { lines: layers };
12178
12758
  }
12179
- function withAlpha2(color, alpha) {
12759
+ function withAlpha3(color, alpha) {
12180
12760
  const hex = /^#([0-9a-f]{6})$/i.exec(color);
12181
12761
  if (hex) {
12182
12762
  const v = parseInt(hex[1], 16);
@@ -12206,6 +12786,7 @@ export {
12206
12786
  LineLayer,
12207
12787
  LinearScale,
12208
12788
  LogScale,
12789
+ ML_PALETTE,
12209
12790
  OhlcLayer,
12210
12791
  OrdinalTimeScale,
12211
12792
  PARALLEL_PALETTE,
@@ -12225,28 +12806,43 @@ export {
12225
12806
  TREEMAP_PALETTE,
12226
12807
  TimeScale,
12227
12808
  VolumeLayer,
12809
+ addAttentionMap,
12228
12810
  addBollinger,
12811
+ addCalibration,
12229
12812
  addChord,
12813
+ addConfusionMatrix,
12814
+ addDecisionBoundary,
12230
12815
  addDepth,
12816
+ addEmbedding,
12817
+ addFeatureImportance,
12231
12818
  addFunnel,
12232
12819
  addGauge,
12233
12820
  addHeikinAshi,
12234
12821
  addParallelCoordinates,
12822
+ addPartialDependence,
12823
+ addPrCurve,
12235
12824
  addRenko,
12825
+ addRidgeline,
12826
+ addRocCurve,
12236
12827
  addSankey,
12828
+ addShapBeeswarm,
12237
12829
  addSunburst,
12830
+ addTrainingCurves,
12238
12831
  addTreemap,
12239
12832
  addVolumeProfile,
12240
12833
  adx,
12241
12834
  atr,
12242
12835
  autoTicks,
12836
+ beeswarmLayout,
12243
12837
  bollinger,
12244
12838
  boxStats,
12245
12839
  bufferUsage,
12840
+ calibrationCurve,
12246
12841
  canvasToBlob,
12247
12842
  chordLayout,
12248
12843
  colormap,
12249
12844
  colormapLUT,
12845
+ confusionMatrix,
12250
12846
  copyCanvasToClipboard,
12251
12847
  createProgram,
12252
12848
  createToolbar,
@@ -12256,6 +12852,7 @@ export {
12256
12852
  downloadCanvas,
12257
12853
  earcut,
12258
12854
  ema,
12855
+ emaSmooth,
12259
12856
  fft,
12260
12857
  fibRetracements,
12261
12858
  firstFinite,
@@ -12278,17 +12875,21 @@ export {
12278
12875
  parallelLayout,
12279
12876
  parseCSV,
12280
12877
  parseColor,
12878
+ pca,
12281
12879
  pointAndFigure,
12880
+ prCurve,
12282
12881
  quantileSorted,
12283
12882
  renko,
12284
12883
  resolveAxisStyle,
12285
12884
  resolveTicks,
12885
+ rocCurve,
12286
12886
  rollingStd,
12287
12887
  rsi,
12288
12888
  sankeyLayout,
12289
12889
  setTransformUniforms,
12290
12890
  sma,
12291
12891
  spectrogram,
12892
+ standardize,
12292
12893
  stochastic,
12293
12894
  sunburstLayout,
12294
12895
  superTrend,