classification

js/ml/metrics/classification.ts

Classification metrics over discrete labels and over ranked scores.

Types

type Average = 'binary' | 'macro' | 'micro' | 'weighted' | 'none'

How per-class scores are combined into a single number.

binary reports the positive class alone. macro is the unweighted mean over classes, treating rare classes as equal to common ones. weighted takes the same per-class scores weighted by true-class support. micro pools every class's counts before dividing, which for single-label problems equals accuracy. none returns one score per class, in label order.

Interfaces

interface AverageOptions {

Options shared by the averaged classification metrics.

Properties

average?: Average

Averaging mode. Defaults to binary.

positiveLabel?: Label

Which label counts as positive under binary averaging. Inferred as 1 when the labels are {0, 1} and true when they are {false, true}; otherwise it must be given.

labels?: readonly Label[]

Label universe, in report order. Defaults to the sorted union of the observed true and predicted labels. Pass this to keep a class present in the report even when a batch never observes it.

interface ProbabilityOptions {

Options for probability-scored classification metrics.

Properties

positiveLabel?: Label

Which label the probabilities refer to. Inferred as 1 or true when the labels allow it.

interface RocCurve {

A receiver-operating-characteristic curve.

Properties

thresholds: number[]

Score thresholds, descending. The first entry is above every score, which is the origin point where nothing is predicted positive.

falsePositiveRate: number[]

False-positive rate at each threshold.

truePositiveRate: number[]

True-positive rate at each threshold.

interface PrecisionRecallCurve {

A precision-recall curve.

Properties

thresholds: number[]

Score thresholds, ascending, one per curve point except the final (recall 0, precision 1) endpoint.

precision: number[]

Precision at each threshold, ending at 1.

recall: number[]

Recall at each threshold, ending at 0.

Functions

function accuracy(yTrue: ArrayLike<Label>, yPred: ArrayLike<Label>): number

Fraction of predictions that match the true label.

import { accuracy } from 'fino:ml/metrics';

console.log(accuracy([1, 0, 1, 1], [1, 0, 0, 1])); // 0.75

function precision( yTrue: ArrayLike<Label>, yPred: ArrayLike<Label>, options?: AverageOptions & { average?: Exclude<Average, 'none'> }, ): number

function precision(
  yTrue: ArrayLike<Label>,
  yPred: ArrayLike<Label>,
  options?: AverageOptions & { average?: Exclude<Average, 'none'> },
): number
function precision(
  yTrue: ArrayLike<Label>,
  yPred: ArrayLike<Label>,
  options: AverageOptions & { average: 'none' },
): number[]

Precision: of everything predicted positive, how much really was.

import { precision } from 'fino:ml/metrics';

console.log(precision([1, 0, 1, 1], [1, 1, 0, 1])); // 0.6666666666666666
console.log(precision(['a', 'b', 'c'], ['a', 'b', 'b'], { average: 'macro' }));

function recall( yTrue: ArrayLike<Label>, yPred: ArrayLike<Label>, options?: AverageOptions & { average?: Exclude<Average, 'none'> }, ): number

function recall(
  yTrue: ArrayLike<Label>,
  yPred: ArrayLike<Label>,
  options?: AverageOptions & { average?: Exclude<Average, 'none'> },
): number
function recall(
  yTrue: ArrayLike<Label>,
  yPred: ArrayLike<Label>,
  options: AverageOptions & { average: 'none' },
): number[]

Recall: of everything that really was positive, how much was found.

import { recall } from 'fino:ml/metrics';

console.log(recall([1, 0, 1, 1], [1, 1, 0, 1])); // 0.6666666666666666

function f1Score( yTrue: ArrayLike<Label>, yPred: ArrayLike<Label>, options?: AverageOptions & { average?: Exclude<Average, 'none'> }, ): number

function f1Score(
  yTrue: ArrayLike<Label>,
  yPred: ArrayLike<Label>,
  options?: AverageOptions & { average?: Exclude<Average, 'none'> },
): number
function f1Score(
  yTrue: ArrayLike<Label>,
  yPred: ArrayLike<Label>,
  options: AverageOptions & { average: 'none' },
): number[]

Harmonic mean of precision and recall.

import { f1Score } from 'fino:ml/metrics';

console.log(f1Score([1, 0, 1, 1], [1, 1, 0, 1])); // 0.6666666666666666

function fBetaScore( yTrue: ArrayLike<Label>, yPred: ArrayLike<Label>, beta: number, options?: AverageOptions & { average?: Exclude<Average, 'none'> }, ): number

function fBetaScore(
  yTrue: ArrayLike<Label>,
  yPred: ArrayLike<Label>,
  beta: number,
  options?: AverageOptions & { average?: Exclude<Average, 'none'> },
): number
function fBetaScore(
  yTrue: ArrayLike<Label>,
  yPred: ArrayLike<Label>,
  beta: number,
  options: AverageOptions & { average: 'none' },
): number[]

F-score with beta weighting recall against precision.

beta below 1 favors precision, above 1 favors recall. beta = 2 is the usual choice when a miss costs more than a false alarm.

import { fBetaScore } from 'fino:ml/metrics';

console.log(fBetaScore([1, 0, 1, 1], [1, 1, 0, 1], 2));

function balancedAccuracy(yTrue: ArrayLike<Label>, yPred: ArrayLike<Label>): number

Mean per-class recall, the imbalance-resistant counterpart to accuracy.

import { balancedAccuracy } from 'fino:ml/metrics';

console.log(balancedAccuracy([0, 0, 0, 1], [0, 0, 0, 0])); // 0.5

function matthewsCorrCoef(yTrue: ArrayLike<Label>, yPred: ArrayLike<Label>): number

Matthews correlation coefficient, in [-1, 1].

1 is perfect agreement, 0 is chance, -1 is total disagreement. Unlike F1 it accounts for true negatives, so it does not flatter a model that only ever predicts the majority class.

import { matthewsCorrCoef } from 'fino:ml/metrics';

console.log(matthewsCorrCoef([1, 1, 0, 0], [1, 1, 0, 0])); // 1

function cohenKappa(yTrue: ArrayLike<Label>, yPred: ArrayLike<Label>): number

Cohen's kappa: agreement corrected for what chance would produce.

import { cohenKappa } from 'fino:ml/metrics';

console.log(cohenKappa([1, 1, 0, 0], [1, 0, 0, 0]).toFixed(4)); // 0.5000

function logLoss( yTrue: ArrayLike<Label>, probabilities: ArrayLike<number>, options: ProbabilityOptions & { eps?: number } = {}, ): number

Cross-entropy of predicted probabilities against true labels.

Lower is better and 0 is perfect. Probabilities are clamped away from the exact endpoints by eps so that a confident mistake costs a large finite amount rather than infinity.

import { logLoss } from 'fino:ml/metrics';

console.log(logLoss([1, 0, 1], [0.9, 0.1, 0.8]).toFixed(4)); // 0.1446

function rocCurve( yTrue: ArrayLike<Label>, scores: ArrayLike<number>, options: ProbabilityOptions = {}, ): RocCurve

Sweep every threshold and report the false/true positive rate at each.

Points are emitted once per distinct score, so tied scores collapse into a single point and the curve stays independent of input order.

import { rocCurve } from 'fino:ml/metrics';

const curve = rocCurve([0, 0, 1, 1], [0.1, 0.4, 0.35, 0.8]);
console.log(curve.truePositiveRate); // [0, 0.5, 0.5, 1, 1]

function rocAuc( yTrue: ArrayLike<Label>, scores: ArrayLike<number>, options: ProbabilityOptions = {}, ): number

Area under the ROC curve: the chance a random positive outranks a random negative.

0.5 is coin-flip performance and 1 is a perfect ranking. Computed from tie-corrected ranks, so it is exact rather than trapezoid-approximated, and unlike threshold metrics it does not depend on a decision cutoff.

Reports 0 when the labels are all positive or all negative, where the statistic is undefined.

import { rocAuc } from 'fino:ml/metrics';

console.log(rocAuc([0, 0, 1, 1], [0.1, 0.4, 0.35, 0.8])); // 0.75

function precisionRecallCurve( yTrue: ArrayLike<Label>, scores: ArrayLike<number>, options: ProbabilityOptions = {}, ): PrecisionRecallCurve

Precision and recall at every distinct threshold.

Prefer this over rocCurve when positives are rare: it ignores true negatives, so a large easy negative class cannot inflate the picture.

import { precisionRecallCurve } from 'fino:ml/metrics';

const curve = precisionRecallCurve([0, 0, 1, 1], [0.1, 0.4, 0.35, 0.8]);
console.log(curve.recall); // [1, 1, 0.5, 0.5, 0]

function averagePrecision( yTrue: ArrayLike<Label>, scores: ArrayLike<number>, options: ProbabilityOptions = {}, ): number

Average precision: the precision-recall curve summarized as one number.

Each threshold's precision is weighted by the recall it gains, which avoids the optimistic interpolation that trapezoid area under the same curve would introduce.

import { averagePrecision } from 'fino:ml/metrics';

console.log(averagePrecision([0, 0, 1, 1], [0.1, 0.4, 0.35, 0.8])); // 0.8333333333333333