@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/README.md +8 -2
- package/dist/index.d.ts +383 -2
- package/dist/index.js +607 -6
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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 (
|
|
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:
|
|
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
|
|
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:
|
|
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
|
|
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,
|