TypeScript · Course navigation

TypeScript: precision, recall, and F1 from a confusion matrix

Count true and false positives and negatives with a typed interface, then compute the metrics.

On this page
Before you start

TypeScript interfaces and arrays.

  • Count true and false positives and negatives with a typed interface, then compute the metrics.
  • Predict the sample output and explain the state changes.
  • Complete the practice task and check the boundary cases.

The concept

A binary classifier makes four kinds of outcome. True positives (TP) are positives correctly found, false positives (FP) are negatives wrongly flagged, false negatives (FN) are positives that were missed, and true negatives (TN) are negatives correctly ignored. Together they form the confusion matrix.

Accuracy is (TP + TN) divided by all cases, but it misleads when one class is rare. Precision, TP / (TP + FP), answers how many flagged items were really positive. Recall, TP / (TP + FN), answers how many positives were found. F1 is the harmonic mean of the two and is high only when both are. The interface keeps the four counts together so they cannot be mixed up.

Code example

TypeScript
interface Confusion {
  tp: number;
  fp: number;
  fn: number;
  tn: number;
}

function confusion(actual: number[], predicted: number[]): Confusion {
  const c: Confusion = { tp: 0, fp: 0, fn: 0, tn: 0 };
  for (let i = 0; i < actual.length; i++) {
    if (actual[i] === 1 && predicted[i] === 1) c.tp++;
    else if (actual[i] === 0 && predicted[i] === 1) c.fp++;
    else if (actual[i] === 1 && predicted[i] === 0) c.fn++;
    else c.tn++;
  }
  return c;
}

const actual = [1, 1, 1, 1, 0, 0, 0, 0, 0, 0];
const predicted = [1, 1, 1, 0, 1, 1, 0, 0, 0, 0];
const c = confusion(actual, predicted);

const precision = c.tp / (c.tp + c.fp);
const recall = c.tp / (c.tp + c.fn);
const f1 = (2 * precision * recall) / (precision + recall);
const accuracy = (c.tp + c.tn) / actual.length;

console.log("tp=" + c.tp + " fp=" + c.fp + " fn=" + c.fn + " tn=" + c.tn);
console.log("precision " + precision.toFixed(3) + ", recall " + recall.toFixed(3));
console.log("F1 " + f1.toFixed(3) + ", accuracy " + accuracy.toFixed(3));
Output
tp=3 fp=2 fn=1 tn=4
precision 0.600, recall 0.750
F1 0.667, accuracy 0.700

Follow the code

Open the walkthrough to inspect the example one step at a time.

Show guided walkthrough
Guided walkthroughReady
1interface Confusion {
2 tp: number;
3 fp: number;
4 fn: number;
5 tn: number;
6}
7
8function confusion(actual: number[], predicted: number[]): Confusion {
9 const c: Confusion = { tp: 0, fp: 0, fn: 0, tn: 0 };
10 for (let i = 0; i < actual.length; i++) {
11 if (actual[i] === 1 && predicted[i] === 1) c.tp++;
12 else if (actual[i] === 0 && predicted[i] === 1) c.fp++;
13 else if (actual[i] === 1 && predicted[i] === 0) c.fn++;
14 else c.tn++;
15 }
16 return c;
17}
18
19const actual = [1, 1, 1, 1, 0, 0, 0, 0, 0, 0];
20const predicted = [1, 1, 1, 0, 1, 1, 0, 0, 0, 0];
21const c = confusion(actual, predicted);
22
23const precision = c.tp / (c.tp + c.fp);
24const recall = c.tp / (c.tp + c.fn);
25const f1 = (2 * precision * recall) / (precision + recall);
26const accuracy = (c.tp + c.tn) / actual.length;
27
28console.log("tp=" + c.tp + " fp=" + c.fp + " fn=" + c.fn + " tn=" + c.tn);
29console.log("precision " + precision.toFixed(3) + ", recall " + recall.toFixed(3));
30console.log("F1 " + f1.toFixed(3) + ", accuracy " + accuracy.toFixed(3));

Follow the example

Start to inspect the source, values, and output. These prepared steps explain the published sample; grouped loop steps describe multiple iterations.

View the complete dry-run table
Prepared trace of the published program. Some rows group repeated iterations.
Step / lineWhat happensState after stepOutput so far
Four named counts instead of four loose numbers.—
—
A real positive that was predicted positive is a true positive.—
—
Three of the four real positives were found, and two negatives were wrongly flagged.tp=3, fp=2, fn=1, tn=4
—
The confusion matrix.tp=3, fp=2, fn=1, tn=4
tp=3 fp=2 fn=1 tn=4
Precision 3/5 and recall 3/4.tp=3, fp=2, fn=1, tn=4
tp=3 fp=2 fn=1 tn=4
precision 0.600, recall 0.750
F1 is the harmonic mean of 0.6 and 0.75. Accuracy is (3 + 4) / 10.tp=3, fp=2, fn=1, tn=4
tp=3 fp=2 fn=1 tn=4
precision 0.600, recall 0.750
F1 0.667, accuracy 0.700

Behind the scenes

How TypeScript processes this code

TypeScript checks types and emits JavaScript. Type annotations are removed before execution.

Cost and common mistakes

O(n) for n predictions.

  • If TP + FP is 0 (nothing predicted positive), precision divides by zero and gives NaN. Handle that case.
  • On data with 1% positives, a model that always predicts negative has 99% accuracy and zero recall.
  • Which metric matters depends on the cost of each error. A cancer screen favours recall, a spam filter favours precision.

Try it yourself

What are the recall and precision of a model that flags everything as positive?

Reveal answer and reasoning

Recall is 1 (nothing positive is missed) and precision equals the fraction of positives in the data, 0.4 here.

Further reading