confusion
js/ml/metrics/confusion.ts
Confusion matrix and the per-class rates derived from it.
Classes
class ConfusionMatrix {
Counts of predicted versus true labels, and the metrics derived from them.
A matrix owns a fixed label universe and a dense count grid indexed
[trueLabel][predictedLabel]. Build one from paired arrays with
ConfusionMatrix.from, or construct an empty one over a known label set and
accumulate with observe/observeAll — the latter is what makes this the
aggregation primitive behind StreamingConfusionMatrix, since merge
combines matrices computed independently over different shards.
Every rate is derived, not stored, so a matrix is the cheapest way to report accuracy, precision, recall, F-scores, kappa, and MCC together rather than re-scanning the labels once per metric.
Undefined rates are reported as 0: a class with no predictions has zero
precision, and a class with no support has zero recall.
import { ConfusionMatrix } from 'fino:ml/metrics';
const cm = ConfusionMatrix.from(
['cat', 'dog', 'cat', 'bird'],
['cat', 'dog', 'dog', 'bird'],
);
console.log(cm.accuracy()); // 0.75
console.log(cm.recall('cat')); // 0.5
console.log(cm.macroF1().toFixed(4)); // 0.7778Constructors
constructor(labels: readonly Label[])
Create an empty matrix over a fixed label universe.
Duplicate labels are rejected. Observing a label outside this set throws, which is what keeps sharded accumulation honest about its schema.
Static Methods
static from(
yTrue: ArrayLike<Label>,
yPred: ArrayLike<Label>,
options: { labels?: readonly Label[] } = {},
): ConfusionMatrix
Tabulate paired true and predicted labels.
import { ConfusionMatrix } from 'fino:ml/metrics';
const cm = ConfusionMatrix.from([1, 0, 1, 1], [1, 0, 0, 1]);
console.log(cm.count(1, 0)); // 1Getters
get labels(): readonly Label[]
The label universe, in report order.
get total(): number
Total number of observations tabulated.
Methods
observe(trueLabel: Label, predictedLabel: Label, weight = 1): void
Record one observation, optionally with a fractional weight.
observeAll(yTrue: ArrayLike<Label>, yPred: ArrayLike<Label>): void
Record a batch of paired observations.
merge(other: ConfusionMatrix): ConfusionMatrix
Combine with another matrix, unioning both label universes.
Neither input is modified. Shards that only saw some of the classes merge cleanly, which is why worker-parallel evaluation can tabulate locally and reduce at the end.
import { ConfusionMatrix } from 'fino:ml/metrics';
const shardA = ConfusionMatrix.from(['a', 'b'], ['a', 'a']);
const shardB = ConfusionMatrix.from(['c'], ['c']);
console.log(shardA.merge(shardB).labels); // ['a', 'b', 'c']count(trueLabel: Label, predictedLabel: Label): number
Count of observations with the given true and predicted labels.
toArray(): number[][]
Dense count grid indexed [trueLabel][predictedLabel], in label order.
support(label: Label): number
Number of observations whose true label is label.
predictedCount(label: Label): number
Number of observations predicted as label.
truePositives(label: Label): number
Correct predictions of label.
falsePositives(label: Label): number
Observations wrongly predicted as label.
falseNegatives(label: Label): number
Observations of label predicted as something else.
trueNegatives(label: Label): number
Observations that are neither label nor predicted as label.
accuracy(): number
Fraction of observations predicted correctly.
precision(label: Label): number
TP / (TP + FP) for label — how often a positive prediction is right.
recall(label: Label): number
TP / (TP + FN) for label — how much of the class was recovered.
specificity(label: Label): number
TN / (TN + FP) for label — recall of the negative class.
f1(label: Label): number
Harmonic mean of precision and recall for label.
fBeta(label: Label, beta: number): number
Weighted harmonic mean of precision and recall for label.
beta sets how much more recall matters than precision: beta < 1
favors precision, beta > 1 favors recall.
macroPrecision(): number
Unweighted mean precision across classes.
macroRecall(): number
Unweighted mean recall across classes.
macroF1(): number
Unweighted mean F1 across classes.
weightedPrecision(): number
Support-weighted mean precision across classes.
weightedRecall(): number
Support-weighted mean recall across classes.
weightedF1(): number
Support-weighted mean F1 across classes.
microPrecision(): number
Precision over pooled counts. Equals accuracy for single-label problems.
microRecall(): number
Recall over pooled counts. Equals accuracy for single-label problems.
microF1(): number
F1 over pooled counts. Equals accuracy for single-label problems.
balancedAccuracy(): number
Mean recall across classes that have support.
Unlike accuracy, a majority-class predictor cannot score well here, so
this is the honest headline number for imbalanced data.
matthewsCorrCoef(): number
Matthews correlation coefficient over all classes, in [-1, 1].
Reports 0 for a degenerate matrix where the coefficient is undefined.
cohenKappa(): number
Cohen's kappa: accuracy corrected for agreement expected by chance.
Reports 0 when chance agreement is total and the statistic is undefined.
report(): Array<{ label: Label; precision: number; recall: number; f1: number; support: number }>
Per-class precision, recall, F1, and support, in label order.
format(): string
Render the count grid as an aligned text table, true labels down the rows.
import { ConfusionMatrix } from 'fino:ml/metrics';
console.log(ConfusionMatrix.from(['a', 'b'], ['a', 'a']).format());toJSON(): { labels: Label[]; counts: number[][] }
Plain structure suitable for JSON.stringify and cross-realm transfer.