Bruised Apple Detector

EasymathAlso known as Precision, Recall and F1 Score

An orchard's packing line has a camera that flags apples it thinks are bruised, so they can be pulled off the belt. Most apples are fine, so a lazy detector that never flags anything would be right most of the time and still useless.

The orchard checks a test batch by hand and wants two honest numbers. Of the apples the camera flagged, how many were really bruised? Of the apples that were really bruised, how many did the camera catch? Then one score that balances both. These are the standard scores for judging any yes/no classifier, from spam filters to medical screening.

Your task

Write detectorScores(flagged, bruised). flagged[i] is the camera's answer for apple i, and bruised[i] is the truth. Count:

  • TP: flagged and bruised.
  • FP: flagged but fine.
  • FN: not flagged but bruised.

Return [precision, recall, f1] where precision = TP / (TP + FP), recall = TP / (TP + FN), and f1 = 2 · precision · recall / (precision + recall).

Whenever a denominator is 0, that score is 0. Empty lists give [0, 0, 0].

detectorScores(flagged: bool[], bruised: bool[]) → float[] Numbers are accepted within 1e-6.

Examples

Example 1
Input
flagged = [true, true, false, false, true, false]
bruised = [true, false, false, true, true, false]
Output
[0.6666666666666666, 0.6666666666666666, 0.6666666666666666]
Why

Apples 0 and 4 are caught (TP = 2). Apple 1 is a false alarm (FP = 1). Apple 3 was missed (FN = 1). Precision 2 / 3, recall 2 / 3, so f1 is also 2 / 3.

Example 2
Input
flagged = [true, true, true, true]
bruised = [true, false, false, false]
Output
[0.25, 1, 0.4]
Why

Flagging everything catches the one bruised apple, so recall is 1. But only 1 of 4 flags was right, so precision is 0.25. f1 = 2 · 0.25 · 1 / 1.25 = 0.4, much lower than the plain average 0.625. F1 drops sharply when either score is poor.

Example 3
Input
flagged = [false, false, false]
bruised = [false, true, false]
Output
[0, 0, 0]
Why

The camera flagged nothing. TP + FP = 0, so precision is 0 by the rule. Recall is 0 / 1 = 0. Both are 0, so f1 is 0 too.

Constraints

  • 0 ≤ flagged.length = bruised.length ≤ 10,000
  • Each value is true or false.
  • Answers within 0.000001 of the expected values are accepted.
Start with brute force

You could build lists of flagged positions and bruised positions, then intersect them to find TP. It works, but it is more machinery than needed.

The real trap is reaching for accuracy (correct answers divided by all apples). On a batch with few bruised apples, a camera that never flags anything scores very high accuracy while catching nothing. Counting the three boxes in one pass gives you all three honest scores.

Hints

0 of 4 shown

Walkthrough and solution

Unlocks when you pass every test. Stuck for a while? Looking is fine too, as long as you then close it and write the code yourself.

Ctrl+Enter runs

Run your code to see results here. Run checks the examples. Submit checks every test, including hidden edge cases.

Write code