Skip to content

Make BAS evaluation runnable: four defects, none of which allowed a single file to load - #23

Merged
AtomScott merged 1 commit into
fix/gsr-evaluatorfrom
fix/bas-evaluator
Aug 11, 2026
Merged

Make BAS evaluation runnable: four defects, none of which allowed a single file to load#23
AtomScott merged 1 commit into
fix/gsr-evaluatorfrom
fix/bas-evaluator

Conversation

@AtomScott

Copy link
Copy Markdown
Owner

Companion to #21. BAS evaluation was as non-functional as GS-HOTA, for a different set of reasons: not one of the ten released BAS files could be read by the loader that ships to read them.

1. Wrong top-level key

_parse_bas reads data["annotations"]; the files key the event array actions. KeyError on the first line of every file. Now accepts either.

2. Wrong label case

BAS_LABELS declares Title Case ("High Pass"); the data is UPPER CASE ("HIGH PASS"). Unknown labels raise rather than being skipped, so this fails on the first event of every file once (1) is past.

The label sets are identical — purely casing. Labels are now matched case-insensitively and canonicalised to the documented Title Case spelling. A genuinely unrecognised label still raises; silently dropping events would understate recall.

3. A third period in a different format

117092, 132831 and 132877 each carry a further block of events whose gameTime omits the half prefix ("90:01" rather than "1 - 0:01") and whose position continues past 5,400,000 ms:

match half 1 half 2 extra block
117092 1077 1058 1007
132831 1279 1192 691
132877 1204 1096 431

2,129 events that crashed the gameTime split with "not enough values to unpack". 117092's metadata does declare EXTRA_FIRST_HALF and EXTRA_SECOND_HALF, so these look like genuine extra time. The period is now inferred from position rather than the events being dropped.

4. position is absolute, not per-half — and the docs say otherwise

Verified on 117093:

half 1:  position       680 .. 2,694,000   gameTime "1 - 0:01"  .. "1 - 44:55"
half 2:  position 2,700,760 .. 5,506,280   gameTime "2 - 45:00" .. "2 - 91:46"

Half 2 does not restart near zero. Both the clock and position are absolute from the start of the match.

docs/format-bas.md and sections/02_dataset.tex both state "a position field giving milliseconds from kickoff of the half indicated in gameTime", and the paper gives the cross-reference formula ⌊position/40⌋. Following either misaligns every half-2 event by 45 minutes.

Event.image_id used t_ms directly, so it placed every half-2 event 67,500 frames late. It now derives from a new Event.t_ms_in_half, documented as approximate: it subtracts a nominal 45-minute half, while real halves run over — half-2 in-half times reach ~48 minutes here, and a few events sit slightly before the nominal boundary, giving small negative values. Frame-exact work should use the per-period frameStart in <match>_tracker_box_metadata.xml.

I have not changed the docs or the paper. Which of the two is authoritative is your call — the data could be relabelled to per-half, or the documentation corrected. tests/test_bas_map_identity.py pins the observed behaviour so that a future data release switching to per-half timing fails loudly rather than silently.

Verification

make bas-map-test:

23,663 events parse across all ten matches      (previously: zero)
mAP@1s = 1.0000   mAP@5s = 1.0000              scoring ground truth against itself
all 12 classes at 1.0 individually

As with GS-HOTA, a perfect-prediction score validates parsing, matching and the AP computation. It does not exercise the partial-match path — that needs real predictions.

Note on the test

The test pins sys.path to its own checkout. Without that it silently exercises whatever working tree the venv's editable install points at — which bit me while writing it, and would bite anyone running tests from a second clone.

🤖 Generated with Claude Code

…ingle file to load

BAS evaluation was as non-functional as GS-HOTA was, for a different set of reasons. Not
one of the ten released BAS files could be read by the loader that ships to read them.

1. WRONG TOP-LEVEL KEY
   _parse_bas reads data["annotations"]; the files key the event array "actions".
   KeyError on the first line of every file. Now accepts either.

2. WRONG LABEL CASE
   BAS_LABELS declares Title Case ("High Pass"); the data is UPPER CASE ("HIGH PASS").
   Unknown labels *raise* rather than being skipped, so this would have failed on the
   first event of every file once (1) was past. The label sets are identical, so this is
   purely casing: labels are now matched case-insensitively and canonicalised to the
   documented Title Case spelling. An genuinely unrecognised label still raises --
   silently dropping events would understate recall.

3. A THIRD PERIOD IN A DIFFERENT FORMAT
   117092, 132831 and 132877 each carry a further block of events whose gameTime omits
   the half prefix entirely ("90:01" rather than "1 - 0:01") and whose position continues
   past 5,400,000 ms. That is 1007, 691 and 431 events respectively -- 2,129 events, all
   of which crashed the split with "not enough values to unpack". 117092's metadata does
   declare EXTRA_FIRST_HALF and EXTRA_SECOND_HALF, so these look like genuine extra time.
   The period is now inferred from position instead of dropping the events.

4. `position` IS ABSOLUTE, NOT PER-HALF -- AND THE DOCS SAY OTHERWISE
   Verified on 117093: half-2 events run 2,700,760..5,506,280 ms with gameTime
   "2 - 45:00".."2 - 91:46". They do not restart near zero; both the clock and position
   are absolute from the start of the match.

   docs/format-bas.md and the paper both state "milliseconds from kickoff of the half
   indicated in gameTime" and give a cross-reference formula of floor(position/40).
   Following either misaligns every half-2 event by 45 minutes.

   Event.image_id used t_ms directly, so it put every half-2 event 67,500 frames late. It
   now derives from a new Event.t_ms_in_half. That property is explicitly documented as
   APPROXIMATE: it subtracts a nominal 45-minute half, whereas real halves run over -- in
   this data half-2 in-half times reach ~48 minutes and a few sit slightly before the
   nominal boundary, giving small negative values. Frame-exact work should use the
   per-period frameStart in <match>_tracker_box_metadata.xml.

   The docs/paper wording is NOT changed here; which of the two is authoritative is a call
   for the author. tests/test_bas_map_identity.py pins the observed behaviour so a future
   data release that switches to per-half timing fails loudly instead of silently.

VERIFICATION -- tests/test_bas_map_identity.py, or `make bas-map-test`:
    23,663 events parse across all ten matches (previously zero)
    mAP@1s = 1.0000, mAP@5s = 1.0000 scoring ground truth against itself
    all 12 classes at 1.0 individually

As with GS-HOTA, a perfect-prediction score validates parsing, matching and the AP
computation. It does not exercise the partial-match path, which needs real predictions.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@AtomScott
AtomScott changed the base branch from main to fix/gsr-evaluator August 10, 2026 18:17
@AtomScott
AtomScott merged commit 4b7461c into fix/gsr-evaluator Aug 11, 2026
0 of 2 checks passed
@AtomScott
AtomScott deleted the fix/bas-evaluator branch August 11, 2026 05:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant