AS '26
All Chapters

Tagging · SECTION 17

Chapter 17 — Event Data & the Common Representation

PBN formalization, rugby's missing schema, and the one-spine standards that make it all queryable

Reading time

29 min

17 Chapter 17 — Event Data & the Common Representation

PBN formalization, rugby's missing schema, and the one-spine standards that make it all queryable

17.1 Coordinates Are Not Analysis

A coach does not read 10,000 coordinate rows. The bridge from "where everyone was" to "what happened" is the event row — a typed, timestamped, evidence-linked statement about the game. The discipline of this chapter: the event row is a contract, and the contract is conservative. It says what it knows, marks what it does not, and refuses to invent. Everything here is source-backed (kloppy/SPADL/EPTS standards) or measured (the PBN validator ran on our own 11 rows).

Start with the object itself. What is an event? In pickleball, the atomic event is a shot — one player, one contact (or candidate contact), one ball state — and the tactical unit above it is the rally, the ordered span of shots from serve to fault or dead ball. In rugby league, the atomic event is the tackle — ball-carrier stopped, play-the-ball begun — and the tactical unit above it is the set: up to six tackles, conditioned by the count, terminated by a try, an error, a kick, or a handover. The two sports look different on video, yet every event in both answers the same five questions: who (athlete), what (action), when (time), where (location), and what came of it (outcome). A third-shot drop at frame 214 by the near-left player landing in the kitchen, and a hit-up on tackle 2 at 31 metres from the attacking line, are the same shape of fact. That shape is the common representation.

The book's own Pickleball Notation (PBN) schema is the reference implementation. Its 31-field data dictionary (written 2026-06-29, validated 2026-09-01) is a real, enforceable contract — and it carries the field's most important convention: "unknown" is a legal value. The schema does not force a shot label; it forces honesty. A PBN row is a structured evidence object: what is known, what is candidate, what is missing, and what a human must correct before it becomes stronger evidence.

PBN event row anatomy: contract bar with event_id rally_id source_clip frame_id timestamp player_slot shot_type contact_state confidence evidence_refs, shot_type unknown badge. Recreate: white background, burnt-orange underline.
Figure 17.1: The PBN Row as a Contract. Eleven of the thirty-one fields, with the honesty clause visible: shot_type carries an "unknown" badge, and that is legal.

17.2 The One-Spine Data Model

Chapter 5 established the one spine: tracks + events in one schema, one join key. The reason a practitioner needs a spine at all is fragmentation — every vendor ships its own coordinate system, its own event vocabulary, and its own file format. The proven answer, from the soccer ecosystem, is a provider-agnostic object model with one canonical coordinate space and one join key. kloppy is the working proof: load StatsBomb, Opta, Wyscout, Metrica, Sportec, Tracab, SkillCorner, or Second Spectrum through separate serializers into the same EventDataset/TrackingDataset objects, then .transform() to whatever coordinate system and orientation the analysis needs. The book extends that pattern in two directions the soccer tools never went: the join key reaches back to video frames, not just match time, and every row carries an evidence layer.

The standards that make it real:

Standard What It Gives License Integration
kloppy (PySport, 3.19.0) Data model + vendor serializers; periods, frames, events, players, pitch dimensions BSD-3-Clause (verified 2026-09-01 — corrects an earlier Apache-2.0 note) The ingestion spine — vendor feeds in, kloppy objects out
SPADL Atomic play-by-play actions (start/end/result) — the explicit PBN ancestor Apache-2.0 Event semantics: what an action row must contain
FIFA EPTS Standard tracking data format — the export contract clubs ingest FIFA docs Export lane: "book outputs are EPTS-readable"
MOTChallenge CSV Benchmark track format (1-based, id=-1 dets) challenge Evaluation lane: track exports comparable to benchmarks
SoccerNet GSR JSON Broadcast-derived game-state format challenge Reference for the rugby broadcast lane

Three of these shape the book's schema directly. kloppy splits data into EventDataset and TrackingDataset, both wrapped in metadata that declares coordinate system, pitch dimensions, teams, and players — the PBN rule at annotation scale: slot first, stable identity only when it survives review. SPADL contributes the row semantics: one row per action, with start and end coordinates, a separate action type vocabulary, and a separate result vocabulary. The design decisions the book inherits: actions are the atom; the action is a vector; result stays separate from type; provider converters map vendor feeds into the language. PBN is, in this book's framing, SPADL for racket sports with evidence fields. FIFA EPTS is the professional export contract: a metadata header plus one line per frame sample, per-person channels in centimeters in a center-origin metric frame, ball as its own channel — the vendor-neutral interchange IFAB mandated in 2015.

The join key is (clip_id, frame_idx, pts_s) — everything joins on it; nothing is matched on fuzzy timestamps. The triplet, not a pair, because the E06 review windows are time intervals but the review UI and dataset schema key frames. pts_s bridges the analyst's clock and the pipeline's frame index, and both must come from one authoritative frame_map.csv — never recomputed downstream. Spine rules: meters canonical, pixels evidence, feet presentation; every event row's evidence_refs resolves to existing (clip_id, frame_idx) pairs; MOTChallenge export is a projection (drop spine columns, fill x,y,z=-1), so TrackEval/HOTA is one SQL query away.

One-spine stack: kloppy SPADL EPTS MOTChallenge GSR chips converging into a DuckDB cylinder. Recreate: white background, burnt-orange converging arrows.
Figure 17.2: The One Spine and Its Standards. Five open formats feed one track+event table; the join key is (clip_id, frame_idx, pts_s).
Entity-relationship diagram: clip manifest, frame map, track rows, event rows, annotators, corrections all joined to a burnt-orange spine bar labeled with the join key (clip_id, frame_idx, pts_s).
Figure 17.3: The Spine as an Entity Graph. Six tables — clip manifest, frame map, track rows, PBN event rows, annotators, and the correction layer — each carrying the join key. The correction layer is first-class: first-pass generated rows are never overwritten in place; human corrections live alongside until a regeneration pass supersedes them.

17.3 Coordinate Conventions

A coordinate is meaningless without its tag. kloppy formalizes three degrees of freedom — Origin, VerticalOrientation, and PitchDimensions — plus the separate orientation problem. The practitioner meets four systems in the wild:

Convention Origin / units Used by Failure mode
Normalized [0..1] Corner origin, unit square kloppy default; event vendors projecting to 100×100 equivalents Distances meaningless until mapped back — all metric information is distorted in standardization
Own-center metric Pitch/court center, m or cm FIFA EPTS raw data (cm, signed — verified against FIFA's own example file) Needs declared dimensions to interpret in/out of bounds
Corner metric A corner, all-positive Most tracking vendors Origin/orientation varies per vendor — silent flips when merging feeds
Image pixels Top-left, y down, px Every detector; MOTChallenge bbox columns Useless for tactics until a homography; the book's rule: pixels are evidence, never canonical

The book's choice is metric everywhere, declared on the row: pickleball on the 13.41 × 6.10 m court, NVZ 2.13 m (C03-verified); rugby league on the 100 × 68 m field, 6–11 m in-goal (C03-verified). Three sport-specific consequences. First, all bounding lines are part of the NVZ, so zone-membership predicates use <=/>= on the line, never < — an off-by-one-pixel bug becomes a foot-fault mislabel. Second, the NVZ governs contact, not airspace, so a volley label needs foot position at contact. Third, rugby's 10 m stripes are a calibration gift: broken lines every ten metres make broadcast homography easier than soccer's sparse markings, which puts pitch-meter coordinates within reach for nrl-001. Every other space — pixels, feet, normalized — is a deterministic projection declared in the manifest. Never mix: the coordinate column declares its space and every query filters on it.

Four panels of the same pickleball court with a player and ball dot mapped through normalized, corner-metric, center-metric, and pixel coordinate systems; the center-metric panel highlighted as canonical.
Figure 17.4: One Point, Four Spaces. The same pickleball court position expressed in the four conventions a practitioner actually meets. The canonical store is court meters; the other three are projections with declared parameters.

17.4 The PBN Contract (Validated W4.1)

The 31 PBN fields group into five blocks, and the grouping is the teaching object: identity & spine (event_id, rally_id, sequence_id, source_clip, frame_id, timestamp, event_index), actor (player_slot, player_id, team_side, court_zone, bbox), action (shot_phase, shot_type, contact_state, ball_visible, paddle_visible, body_state, movement_before/after_contact), context (partner_spacing, opponent_spacing, cue, court_state, outcome), and evidence & governance (evidence_level, evidence_refs, confidence, human_correction, review_required, coaching_note). The data dictionary's rules, in the form that matters:

Rule Contract
Player identity Player slot (near_left/near_right/far_left/far_right/unknown/not_visible) is the unit; player_id is optional and blank until identity survives multi-frame review — never a name
Shot type unknown is expected unless contact/ball evidence supports the label
Contact contact_state ∈ {confirmed, candidate, not_visible, not_contact, unknown}; candidate means "might be contact, needs review" — not "contact"; do not infer ball path from body posture alone
Evidence evidence_level ∈ {real-experiment-output, real-experiment-output+manual-proxy, manual-proxy, concept-transfer}; evidence_refs point at disk artifacts (e.g. pb-003-frame-01; det_id=1; yolo_conf=0.8713), never claims
Review review_required=yes until a human pass completes; confidence ∈ {high, medium, medium-low, low}

The dictionary's safe-versus-unsafe note discipline is the chapter's ethics in one example. Safe: "This row can be used as a contact-review target. Shot type is not confirmed." Unsafe: "The player executed a poor speed-up and left the middle open" — a tactical claim from a row with no confirmed ball path, contact, outcome, or identity. The schema cannot enforce taste, but it makes the overreach visible: the unsafe sentence asserts fields the row marks unknown.

The action vocabulary is the pinned 12-event taxonomy from the dataset schema (E06, ch07 v1): serve, return, third_shot_drop, third_shot_drive, dink, speedup, lob, volley, fault, let, kitchen_foot_fault, contact, with actor ∈ {A1, A2, B1, B2, null}. Notice what the taxonomy encodes about the sport: the third shot gets two types (drop and drive) because it is pickleball's decisive tactical fork; the kitchen foot fault is an event type of its own because the NVZ rule (17.3) makes it detectable from foot-at-contact geometry; and contact exists as a bare type so the pipeline can log a confirmed contact before anyone commits to a shot label. That last entry is the honesty clause operating at the vocabulary level.

Pickleball court from above with twelve event-type chips (serve, return, third shot drop, third shot drive, dink, speedup, lob, volley, fault, let, NVZ fault, contact) pointing at the court locations where each occurs.
Figure 17.5: The PBN 12-Event Taxonomy on the Court. Each event type placed where it lives: serves and returns at the baseline, dinks and NVZ faults at the kitchen line, the third-shot fork in the transition zone. The taxonomy is sport-specific; the row structure carrying it is not.

17.4b The Honest Experiment: Validating the Contract

A schema nobody has run is a draft. Our lab loaded the actual canonical event rows — 11 rows × 31 fields, from the pb-003 clip with its manual contact-review correction layer — and validated every machine-checkable dictionary rule: enum membership for player_slot, contact_state, ball_visible, paddle_visible, evidence_level, confidence, human_correction, review_required, and team_side; non-empty evidence_refs; and the spine rule that every referenced frame resolves in the manifest. The measured result (transcript and metrics on disk at experiments/c17-event-data/outputs/metrics.json): 0 violations, verdict PASS. Mandatory-field coverage was 100%; player_id was correctly 0% filled — identity unresolved, which is chapter 13's problem, and the schema behaving exactly as designed by not guessing.

The validator is about 30 lines of Python and runs on every commit. Its lesson, verbatim from the lab output: "The PBN contract is conservative BY DESIGN: unknown is a legal value, candidate is not confirmed, human_correction=needed is expected on most rows. Validation is about catching typos, not forcing spec — the honesty gate is the 'unknown' bucket." This is the failure mode the chapter exists to prevent, stated positively: most event pipelines fail by being too confident — every detector guess promoted to a fact, every gap silently interpolated. A schema whose allowed values include unknown, candidate, and not_visible makes under-confidence representable, and the CI gate makes overconfidence a build failure. The validator catches typos; the taxonomy catches hubris.

17.5 The Rugby Schema: NRLSheet

The verified gap: rugby has no kloppy-equivalent open event pipeline and no public tracking corpus. The official NRL pipe is closed — Stats Perform/Opta is the official data provider (a decade-long partnership feeding NRL.com and broadcast), Hudl owns the club workflow, Catapult owns wearables. What exists openly is aggregate-level: nrlR (CRAN) scrapes NRL.com, Rugby League Project, and Zero Tackle into tidy tibbles of fixtures, ladders, and player stats; Rugby League Project provides the stable entity IDs that make entity resolution possible; UselessNRLStats ships cleaned CSVs of in-game stats. Whether any open source exposes true tackle-by-tackle event rows, rather than match aggregates, is [verify] — the sources on record are aggregate-level, and that gap is precisely the book's contribution.

The model for filling it is Cricsheet, which solved the same problem in cricket: a closed official pipe and a community that needed a shareable, versioned, ball-by-ball open format. Its lessons transfer directly: one delivery equals one row with innings/over/ball addressing — rugby league's equivalent addressing is set:tackle; a registry of people IDs separate from names; a structured source of truth (YAML/JSON) with CSV as a projection; and a per-file metadata header (match, teams, venue, date) so rows are interpretable standalone. The book's proposal — NRLSheet — is tackle-by-tackle: set count, tackle number, PTB timestamp, field position, possession, kick type, try/conversion/penalty fields.

The field semantics differ from football in the way the set grammar demands. A soccer SPADL row is a pass or a shot; a rugby league row is a state row: the tackle count is the conditioning variable (a run on tackle 1 is a field-position play; on tackle 6 with no kick it is a turnover). Contact, which soccer records only as fouls and cards, is the centre of the rugby game — a tackle event carries tackler_ids[], tackle_type, and, in the welfare lane, head-contact flags per the Bath 2025 CV framework. NRLSheet therefore carries: set_number, tackle_number (1-6, plus six-again resets), ptb_timestamp, field_position_m (metres from the attacking team's own try line on the 100 × 68 m field), possession, kick_type ∈ {grubber, bomb, chip, touch_finder, goal_line_dropout, last_tackle}, outcome ∈ {try, error, handover, penalty}, and the identity fields (player_id from the roster join, chapter 13). The schema is the book's contribution, and it is stated as such.

Why not just reuse kloppy? Because kloppy is soccer-first by construction: its event taxonomy (PassEvent, ShotEvent, CardEvent, …) and serializers cover association-football providers only — no rugby serializers, no rugby event types, no set/tackle counting semantics, no play-the-ball, no kick-on-last structure; even scoring breaks the model (try = 4, conversion, penalty goal, field goal). What transfers is the chassis — datasets, metadata, coordinate systems, .transform() — which is why NRLSheet is specified as kloppy-shaped plus the PBN evidence layer rather than as a kloppy fork. And the schema is validated by a published model's appetite: Kempton, Kennedy & Coutts (2016) built the canonical NRL expected-possession-value model on 768 matches (2010–13), showing possession value rises monotonically toward the try line. EPV needs exactly the fields NRLSheet proposes — set, tackle, field position, outcome — so the value model for the schema already exists in the literature.

The honest boundary: NRLSheet v1 codifies what event data can say. Tracking data (per-frame positions) stays a separate table under MOTChallenge/EPTS conventions, joined by (clip_id, pts_s). Events reference the track-level evidence that produced them, and the evidence-tier column carries the same four labels as PBN.

Rugby league field strip marked in 10m bands with six tackle-event markers stepping toward the try line, play-the-ball icons between them, each marker carrying a small data chip such as set 3, tackle 2, x_m 68, ptb 2.9s.
Figure 17.6: NRLSheet Row Anatomy — a Set of Six. Rugby addressing is set:tackle, not clock time. Each tackle marker carries its data chip: field position in metres from the attacking team's own line, and PTB (play-the-ball) speed — the ruck-speed metric CV must infer from the PTB frame to first touch.

17.5b The Common Representation Across Sports

The five questions of §17.1 map onto both row contracts:

Question SPADL (soccer) PBN (pickleball) NRLSheet (rugby league)
Athlete player_id, team_id player_slot + blank player_id actor_id (roster join) / tackler_ids[]
Action actiontype, bodypart 12-event enum + shot_phase tackle / PTB / run / pass / kick / try
Time period_id, time_seconds frame_id, timestamp (spine key) half, clock_s + set_no, tackle_no
Location start_x/y, end_x/y (105×68) court meters, NVZ-aware zones field_position_m from own try line
Outcome result (separate from type) outcome, unknown-first outcome ∈ {try, error, handover, penalty}
Evidence — (provider-trusted) evidence_level, evidence_refs, confidence same four-tier layer, CV or scrape sourced

The evidence row is where the schemas diverge from their ancestors on purpose. SPADL rows trust the provider; PBN and NRLSheet rows carry provenance because they are produced by a CV pipeline that can be wrong, corrected by a human layer, and versioned by a schema that evolves. The taxonomies stay sport-specific — nobody needs dink in rugby or six_again in pickleball — but the skeleton and the evidence layer are shared verbatim. "Common representation" means one shape for the fact, with the vocabulary as a pluggable enum. The payoff: C18, C19, and C20 each read one row shape; the sport-specific code is the enum and the geometry, nothing else.

Pickleball rally panel and rugby league tackle panel both converging via arrows into one shared row of five boxes: athlete, action, time, location, outcome, with an evidence bar beneath.
Figure 17.7: The Common Representation. A pickleball rally and a rugby league set produce the same five-field fact plus an evidence layer. The sport lives in the taxonomy and the geometry; the row shape is shared.

17.6 Versioning & Validation

The PBN v2 proposal (from the dossier) is experiment-gated — each delta ships only when a lab proves it: the spine key on every row, court-meter start/end coordinates, taxonomy promotion, structured evidence_refs, schema_version, review_state machine, rally-span fields. Non-goals stated: no per-player identity as a hard field (identity is chapter 13), no spin fields (the C12 frontier), no rugby fields in PBN (NRLSheet is a sibling schema, not a superset — the spine and evidence layer are shared, the taxonomy is not).

A schema without tests is a suggestion. The discipline:

  1. Schema as JSON Schema: the dataset/pb003 schema is v1 (12 court keypoints, 4 players, ball states, 12 event types, draft 2020-12 with a versioned $id, const pins on identity fields, patterns on frame IDs, enums on every categorical). It is machine-validatable and it is what the G0.5 dataset contribution ships.
  2. Data dictionary in markdown: the PBN dictionary's 31 fields with allowed values and the "current rule" column — the human-readable half of the contract. The two halves must never drift: every enum in the schema appears in the dictionary; every dictionary rule that can be an enum is an enum.
  3. Validation tests: every row against the dictionary in CI (the C17 validator is 30 lines of Python; it ran on our 11 rows: 0 violations, 100% mandatory-field coverage).
  4. Migration rules: v1 → v2 additive (new fields default to unknown; no renames without a migration script plus a fixture assert). Old files stay valid under their declared version; readers dispatch on schema_version.
  5. Manifest hash on every change: the schema version is part of the evidence chain; a row produced by v1 is not the same as one from v2, and a diff of the schema is a diff of the dataset's meaning.
  6. The unknown is first-class: the schema's allowed values include unknown, candidate, not_visible. The validation is about catching typos, not forcing spec.

Two further rules deserve emphasis. Annotation quality is schema-level: the pb003 schema carries an annotators block with role, agreement metric (cohen_kappa, iou, distance_px), and value — inter-rater reliability is part of the dataset, not a paper afterthought, and C18 and C20 read it. License travels with the data: the license object is required, not metadata-by-convention — pb-003 labels can ship while PPA frames stay non-redistributable, and NRLSheet rows scraped via nrlR inherit their sources' terms. The lesson generalizes from C16's licensing landmines.

JSON schema drawn as an indented tree: event root with event_id, clip_id, frame_idx, pts_s, actor, action, x_m, y_m, outcome children; the evidence_refs subtree highlighted in burnt orange expanding into clip, frame, det_id, conf; small type tags on leaves.
Figure 17.8: The Event Row as a Machine Contract. The v2 event object: spine key fields first, the five-question skeleton in the middle, and the evidence_refs subtree — structured so the spine rule "every reference resolves in the manifest" is checkable in CI rather than enforced by hope.

17.6b The Event-Model Use Cases: Applied Framework

The use cases below are the applied bridge from the contracts above to daily practice. They follow three categories: Cat-A The Row as a Contract (01-03), Cat-B Time, Space & Provenance (04-07), and Cat-C Storage, Granularity & Audit (08-10). Each case pairs a pickleball and a rugby league application so the spine transfers, and each carries its evidence label: measured (book experiment), source-backed (paper/standard), or [verify] (practitioner model, not yet established).

Category A: The Row as a Contract (01-03)

UC 01 — Reading a PBN Row: Anatomy of One Shot

A coach asks "what happened on the third shot?" and the honest answer is a row, not a paragraph. The mechanism is the five-block read: identity & spine fields resolve the row against frame_map.csv; actor fields give the slot, never a guessed name; action fields say what is confirmed versus candidate; context fields state visibility honestly; evidence fields point at disk artifacts. Formally the row is a tuple over the 31-field dictionary with enum domains — contact_state ∈ {confirmed, candidate, not_visible, not_contact, unknown} — and the compliance test is: every claim the row makes is licensed by a field whose value supports it. Measured: the canonical 11-row pb-003 set passes this read with 0 violations (W4.1).

One PBN event row exploded into labeled field chips, with the shot_type chip carrying an unknown-OK badge.
Figure 17.9: UC 01 — PBN Row Read. The five blocks of one shot row, with the honesty badge visible. Pickleball: a third-shot row with shot_type=unknown, contact_state=candidate — a review target, not a verdict. Rugby league: the same read discipline applied to a tackle row whose ptb_speed_s is still provisional.

Payoff: the reviewer and the coach — one row answers "what do we actually know?" in under a minute, and the unknowns route straight into the C18 review queue.

UC 02 — Reading an NRL-Set Row: Set:Tackle Addressing

A rugby league possession is not a timestamp range — it is a numbered set of up to six tackles, and the tackle count is the conditioning variable. The mechanism: address every event by (half, clock_s, set_no, tackle_no), store field_position_m as metres from the attacking team's own try line with an orientation flag, and compute ruck speed as ptb_speed_s = t(first_touch) − t(ptb_frame). The schema is validated by a published model's appetite: Kempton, Kennedy & Coutts (2016) need exactly set, tackle, field position, and outcome for expected possession value — 768 matches, source-backed.

Rugby league field strip with one event row exploded into chips: set 3, tackle 2, x_m 68, ptb 2.9s.
Figure 17.10: UC 02 — NRL-Set Row Read. Rugby addressing is set:tackle, not clock time. Rugby league: a tackle-2 row at 68 m with a 2.9 s PTB — a fast ruck in attacking territory. Pickleball: the equivalent addressing is rally:shot_index, with the third shot as the tactical fork.

Payoff: the analyst — EPV-style value models (C22) become computable on day one because the schema was designed around the covariates the literature already proved matter.

UC 03 — The Common Representation in One Query

The test of a common representation is not a diagram — it is whether one SQL query runs unchanged against both sports' rows. The mechanism: one row shape (the five questions plus the evidence layer), the sport isolated in the taxonomy enum and the geometry. A query like SELECT actor, event, x_m, outcome FROM events WHERE evidence_level = 'real-experiment-output' reads PBN rows and NRLSheet rows identically; swapping sports means swapping the enum, not the schema. Source-backed design (SPADL/kloppy); measured on the PBN side (11 rows); NRLSheet side is the proposed sibling [verify] until its v1 validator runs (17.8).

Pickleball shot row and rugby tackle row converging into one shared five-box row with an evidence bar and a query chip.
Figure 17.11: UC 03 — One Shape, Two Sports. The row skeleton is shared; the vocabulary is a pluggable enum. Pickleball: shot rows with the 12-event taxonomy. Rugby league: tackle rows with set:tackle addressing — same skeleton, different enum.

Payoff: the data engineer — C18, C19, and C20 each read one row shape; the sport-specific code is the enum and the geometry, nothing else.

Category B: Time, Space & Provenance (04-07)

UC 04 — Event Timestamp Alignment: One Clock, One Map

The analyst thinks in seconds; the pipeline thinks in frames; the review UI scrubs frames while the review windows are time intervals. The mechanism is the spine triplet (clip_id, frame_idx, pts_s) with the rule that pts_s = frame_idx / fps is computed once, in the ingest frame_map.csv, and never recomputed downstream — every recomputation is a drift opportunity. Measured context: E06's review windows (8.33 s / 12.83 s) are time intervals, but the dataset schema keys frames (^pb-003-frame-[0-9]{3}$) — the triplet is what makes both addressable from one row.

Frame timeline aligned to a seconds axis through burnt-orange ticks rooted in a frame_map.csv table chip.
Figure 17.12: UC 04 — One Clock, One Map. Frames and seconds join only through the authoritative frame map. Pickleball: a contact frame pinned to pts_s for the review scrubber. Rugby league: broadcast 25 fps aligned to the match clock for PTB timing.

Payoff: the reviewer — a click in the review UI lands on the exact frame the detector saw, not a frame 40 ms away because someone recomputed time downstream.

UC 05 — Event Metadata: Model, Version, Confidence

An event row without provenance is an assertion; with provenance it is evidence. The mechanism: evidence_refs is structured, not free text — {clip_id, frame_idx, det_id, model, model_version, conf} — and evidence_level carries the four provenance tiers (real-experiment-output → concept-transfer). Confidence is a pinned enum (high, medium, medium-low, low), not a float that invites false precision. The canonical rows already smuggle this in text (det_id=1; yolo_conf=0.8713); v2 makes it queryable and makes the spine rule "every reference resolves in the manifest" checkable in CI.

Event row chip with a provenance ribbon showing model, version, confidence, evidence tier, and a human review tick.
Figure 17.13: UC 05 — Provenance on the Row. Every claim carries its producing artifact and its honesty tier. Pickleball: a dink row traced to det_id=1 at yolo_conf=0.8713. Rugby league: a tackle row traced to the CV tackle candidate that proposed it, tier-labeled until human-confirmed.

Payoff: the auditor and the downstream model — C19's automatic eventing can filter "CV-sourced, unreviewed" rows out of any published stat with one WHERE clause.

UC 06 — Coordinate Conventions in Practice: The Tagged-Space Rule

The most expensive silent bug in sport analytics is merging two feeds with different corner origins and watching every player teleport 6 metres. The mechanism is kloppy's formalization — every coordinate carries (Origin, VerticalOrientation, PitchDimensions) plus an orientation flag, and transform is a pure function between tagged spaces — with the book's storage rule on top: meters canonical, pixels evidence, feet presentation. Sport geometry rides the same rule: NVZ membership is <= on the line because all bounding lines are part of the zone (C03-verified); rugby's field_position_m is meaningless without the orientation flag saying which end they attack.

Pickleball court with one ball dot and four axis tags — pixels, court meters, feet, normalized — meters highlighted as canonical.
Figure 17.14: UC 06 — One Point, Tagged Four Ways. Every coordinate carries its space tag; the canonical store is court/field meters. Pickleball: NVZ predicates in meters on the 13.41 × 6.10 m court. Rugby league: metres from own try line on the 100 × 68 m field, orientation flag attached.

Payoff: the data engineer — merging a CV feed with a vendor feed becomes a declared transform instead of a debugging week, and foot-fault predicates stop having off-by-one-pixel bugs.

UC 07 — The JSON Schema in Production: CI as the Contract's Teeth

A schema nobody runs is a suggestion. The mechanism: JSON Schema draft 2020-12 with a versioned $id, const pins on identity fields (dataset_id, clip path, fps: 25), regex patterns on frame IDs, and enums on every categorical — validated by the jsonschema library in CI on every commit, plus the 30-line dictionary validator that checks the rules the schema cannot express. Measured: the pb-003 schema validates the canonical 11 rows × 31 fields with 0 violations and 100% mandatory-field coverage (experiments/c17-event-data/outputs/metrics.json, 2026-09-01).

JSON tree flowing through a burnt-orange CI gate into a database cylinder, with a PASS shield and one FAIL cross.
Figure 17.15: UC 07 — The Schema with Teeth. Rows that violate the contract fail the build; rows that pass land in the spine. Pickleball: the pb003 v1 schema gating every label batch. Rugby league: the same CI pattern reserved for the NRLSheet v1 schema (17.8).

Payoff: the whole pipeline — overconfidence becomes a build failure, and "the dataset changed meaning" shows up as a schema diff, not a silent drift.

Category C: Storage, Granularity & Audit (08-10)

UC 08 — Event Storage & the C19 Feed

Event rows are write-once, read-many: the CV pipeline and the correction layer write them; annotation (C18), automatic eventing (C19), and statistics (C20) read them. The mechanism: event rows land in Parquet beside the track rows, both keyed by the spine triplet, with DuckDB as the SQL layer — and the standards projections are views, not copies: MOTChallenge export is one query (drop spine columns, fill x,y,z=-1), so TrackEval/HOTA evaluation of any tracking change is one SQL statement away. The correction layer is first-class storage: first-pass generated rows are never overwritten in place; human corrections live alongside until a regeneration pass supersedes them.

Pipeline from video clip through detector and event rows into Parquet and a database cylinder, branching to review, eventing, and stats consumers.
Figure 17.16: UC 08 — One Store, Three Readers. The spine feeds annotation, eventing, and statistics from one Parquet store. Pickleball: pb-003 rows feeding the E06 review UI. Rugby league: scraped nrlR rows and CV tackle rows sharing the same store, tier-labeled apart.

Payoff: the pipeline owner — no export scripts to maintain, no stale copies; the benchmark projection and the coaching query read the same bytes.

UC 09 — Fine Events vs Coarse Events: The Granularity Tradeoff

Every annotation program picks a granularity and pays for it twice — once in labeling time, once in error surface. Fine events (12 shot types per rally) maximize tactical resolution but multiply the rows a human must review; coarse events (rally spans with outcomes) are cheap and robust but cannot answer "was the third shot a drop or a drive?" The mechanism is the honest rule: record at the finest granularity you can validate, publish at the coarsest granularity that answers the question — the fine rows carry review_required=yes, the coarse aggregates carry only what survived review. E06's rally spans (8.33 s / 12.83 s) are the measured coarse layer; the 12-event taxonomy is the pinned fine layer.

Two timelines of the same rally: a dense fine timeline of twelve shot ticks and a coarse timeline of two rally-span brackets.
Figure 17.17: UC 09 — The Granularity Tradeoff. Fine rows for review, coarse rows for publishing. Pickleball: shot-level rows beneath rally-level spans. Rugby league: tackle-level rows beneath set-level summaries (completion, meters gained) — the NRL headline stats are the coarse projection.

Payoff: the analyst and the budget — review effort concentrates on the rows that carry tactical value, and published numbers never rest on unreviewed fine grain.

UC 10 — The Event-Quality Audit: Agreement as Data

An event dataset without a measured disagreement rate is an unaudited claim. The mechanism is schema-level: the annotators block carries role and an agreement metric (cohen_kappa, iou, or distance_px) with its value, so inter-rater reliability ships inside the dataset — C18 and C20 read it directly, with Landis-Koch interpretation on record. The audit loop closes against C16's lesson: any generated sentence about a match is falsifiable against the rows (E07 measured a 66.7% ungrounded-claim error baseline), and every correction lands in the correction layer, never as a silent overwrite. [verify] for the rugby side: no public tackle-by-tackle corpus exists to audit against — the audit discipline is what NRLSheet v1 must be born with.

Magnifier over an event table, two annotator icons joined by a kappa symbol, agreeing rows checked and one disputed row flagged.
Figure 17.18: UC 10 — Agreement as Data. Inter-rater reliability is a schema field, not a paper afterthought. Pickleball: kappa over the 12-event taxonomy on pb-003 review windows. Rugby league: the same block reserved for NRLSheet annotators — CV model versus human coder.

Payoff: the reviewer and the reader — every published stat carries its measured agreement, and every correction is auditable back to the row it superseded.

These ten use cases are one system read ten ways: the row as contract (01-03), the spine in operation (04-07), and the store under audit (08-10). They extend existing chapters — C05's spine, C18's review workflow, C19's automatic eventing, C20's statistics, C22's value models — and the honest labels mark exactly which claims are measured in the book's lab (the 11-row validation, the E06 windows, the E07 baseline) versus proposed for the rugby sibling (NRLSheet v1).

17.7 The Event-Row Recipe (Copy This)

  1. Schema first: define the row contract (event_id, rally_id, clip, frame, time, slot, type, confidence, evidence_refs) — the PBN dictionary is the pickleball reference; NRLSheet is the rugby one. Answer the five questions (athlete, action, time, location, outcome) plus evidence; plug in the sport's taxonomy as an enum.
  2. Validate in CI: every row against the dictionary's allowed values (the C17 validator is 30 lines of Python and runs on every commit).
  3. Coordinate declare: metric court/pitch coordinates; never mix spaces without declaring; respect sport geometry (NVZ lines inclusive with <=; rugby metres from the attacking team's own line with an orientation flag).
  4. Conservative defaults: shot_type=unknown, player_id blank, contact_state=candidate — the schema's honesty IS the value.
  5. Evidence links: every row's evidence_refs point at the artifact that produced it, and CI checks that the referenced frames exist in the manifest.
  6. Version the schema: additive migrations, manifest hash, fixture assert against format drift; corrections live in a correction layer, never as silent in-place overwrites.

17.8 What I Would Measure Next

  • Convert the PBN rows to kloppy-normalized events (the vendor-format bridge): wrap the pb-003 track rows and E06 rally spans (8.33 s / 12.83 s) in a kloppy-shaped object — one RallyPeriod per rally, A1–B2 player slots, 13.41 × 6.10 m PitchDimensions with NVZ markings at 2.13 m — and prove the .to_df() output joins the Parquet track rows on (clip_id, frame_idx, pts_s) with zero orphans.
  • Draft the NRLSheet schema v1 and validate 50 nrlR event rows against it — then the harder, more valuable half: produce one NRLSheet-conformant tackle sequence from the nrl-001 broadcast by hand-coding one set of six (tackle candidates from the C19 detector lane per the Bath 2025 framework, field position via homography on the 10 m stripes, PTB speed from PTB frame to first-touch frame, evidence layer exactly as PBN). Acceptance: one JSON file passing the v1 NRLSheet JSON Schema — the schema itself is the deliverable — with rows joining nrl-001's frame_map.csv.
  • The pklmart prior bridge: 300K+ shot records across ~1,000 matches give pickleball its first scale-grade statistical prior (license [verify] before print); a pklmart→PBN serializer would let priors and video-evidence rows share one shape.
  • The EPTS writer: no open-source Python EPTS writer was found ([verify] once more before building); shipping one would make every book output club-ingestable.

17.9 Sources

  • kloppy (PySport, BSD-3-Clause, 3.19.0): https://kloppy.pysport.org/, https://github.com/PySport/kloppy, coordinates guide https://kloppy.pysport.org/user-guide/concepts/coordinates/; SPADL/socceraction (Apache-2.0): https://socceraction.readthedocs.io/en/latest/documentation/spadl/spadl.html; Atomic VAEP arXiv:2011.00126 [verify arXiv id].
  • FIFA EPTS Standard Data Format (page updated 2026-04-01): https://inside.fifa.com/innovation/standards/epts/research-development-epts-standard-data-format; MOTChallenge: https://motchallenge.net/instructions/ + TrackEval format spec; SoccerNet GSR: https://github.com/SoccerNet/sn-gamestate.
  • Kempton, Kennedy & Coutts (2016), NRL expected possession value, 768 matches: https://pubmed.ncbi.nlm.nih.gov/26190116/; Bath 2025 head-contact CV framework: https://pubmed.ncbi.nlm.nih.gov/39832883/.
  • NRL open data: nrlR https://cran.r-project.org/package=nrlR; Rugby League Project https://www.rugbyleagueproject.org; UselessNRLStats https://github.com/uselessnrlstats/uselessnrlstats; Stats Perform/Opta NRL partnership https://www.statsperform.com/insights/ten-years-as-official-nrl-data-provider/; Cricsheet https://cricsheet.org (exact license [verify]); pklmart https://www.kaggle.com/datasets/cakesofspan/pklmarts-competitive-pickleball-extracts (license [verify]).
  • PBN Data Dictionary (book-research/PBN, 2026-06-29) — 31 fields, allowed values; canonical rows 2026-06-29-pb-003-pbn-event-rows.csv + correction layer 2026-06-29-pb-003-manual-contact-review.csv; dataset schema dataset/pb003/pb003.schema.json.
  • Lab: lab/w4_lab_event_data.pyexperiments/c17-event-data/outputs/metrics.json (11 rows, 0 violations, measured 2026-09-01).

Next Chapter

Chapter 18 — Annotation: The Ground-Truth Workflow

Tooling, label budgets, inter-annotator agreement, and the discipline that makes labels trustworthy

Continue Reading
AS '26

Agentic Sport Analytics

A practitioner's field guide to automated sport analytics: watching, tagging, modelling, interpreting, and acting with AI, LLMs, computer vision, and agent harnesses. Measured on pickleball and Australian rugby league. By Mehran Mozaffari. First Edition, August 2026.

Front Matter

Preface

Front Matter

Copyright & License

Watching

Chapter 01 — Build the Lab, Not the Manuscript

Watching

Chapter 01 — Why This Book Exists: The Five Verbs of Sport Analytics

Watching

Chapter 02 — The Evidence Contract & Data Provenance

Watching

Chapter 02 — The Evidence Contract & Data Provenance

Watching

Chapter 03 — Calibrating the World: Homography & Court Geometry

Watching

Chapter 03 — Sport Rules as Formal Systems

Watching

Chapter 04 — Finding & Tracking the Actors: From ByteTrack to Meta SAM 2/3

Watching

Chapter 04 — Capture: Cameras, Lenses, Shutter, Placement

Watching

Chapter 05 — The Body in Motion: 2D Keypoints to Meta SAM 3D Body

Watching

Chapter 05 — Data Engineering for Sport Video

Watching

Chapter 06 — Smashing the Ball Wall: Spatio-Temporal Trajectory Recovery & SAM 2/3 Equipment Segmentation

Watching

Chapter 06 — Calibration I: Homography, Intrinsics, Distortion

Watching

Chapter 07 — The Structured Representation: PBN & State Machines

Watching

Chapter 07 — Calibration II: Broadcast Dynamics, GMC, and Per-Frame H_t

Tagging

Chapter 08 — Reading Space & Pressure: Geometric Deep Learning

Tagging

Chapter 08 — Detection: YOLO, RF-DETR, and the AGPL Decision

Tagging

Chapter 09 — Generative Replay & Counterfactual Simulation

Tagging

Chapter 09 — Tracking & Identity: Metrics, ReID, and Role Priors

Tagging

Chapter 10 — Where Vision-Language Models Help, and Where They Lie

Tagging

Chapter 10 — Segmentation & Foundation Models: SAM 2/3, DINOv3

Tagging

Chapter 11 — Building the Live Coaching Cockpit on Apple Silicon

Tagging

Chapter 11 — The Body in Motion: 2D Keypoints to 3D Biomechanics

Tagging

Chapter 12 — Complex Motion & Field Sport Scaling

Tagging

Chapter 12 — Smashing the Ball Wall: Spatio-Temporal Trajectory Recovery

Tagging

Chapter 13 — Evaluation, Rights, and the Next 10 Runs

Tagging

Chapter 13 — Identity: Who Is Who

Tagging

Chapter 14 — Multi-Camera Geometry, Line Calls, 3D Reconstruction

Tagging

Chapter 15 — Audio & Multimodal Cues: The Free Sensor

Tagging

Chapter 16 — Video Understanding: Action Recognition, Spatio-Temporal

Tagging

Chapter 17 — Event Data & the Common Representation

Modelling

Chapter 18 — Annotation: The Ground-Truth Workflow

Modelling

Chapter 19 — Automatic Eventing: State Machines, Confidence, Review Queues

Modelling

Chapter 20 — Statistics for Sport Practitioners

Modelling

Chapter 21 — Rating Systems: DUPR, ELO, Glicko, and Skill

Modelling

Chapter 22 — Expected Value: xG, VAEP, EPV, and Their Sport Transplants

Modelling

Chapter 23 — Tactical ML: Graphs, Equivariance, and Honest Forecasting

Modelling

Chapter 24 — Simulation & Counterfactuals: The Honest Rebuild

Interpreting

Chapter 25 — Where Vision-Language Models Help, and Where They Lie

Interpreting

Chapter 26 — From Numbers to Narrative: Reports, Scouting, Coach UX

Interpreting

Chapter 27 — The Agent Harness for Sport Analytics

Interpreting

Chapter 28 — The Live Coaching Cockpit: Real-Time Systems, Honestly Measured

Acting

Chapter 29 — Practice Design & Interventions: The Acting Loop

Acting

Chapter 30 — Sensors & Hardware: Wearables, Smart Courts, the Fusion Spine

Acting

Chapter 31 — Deployment, Licensing, Rights & Ethics

Acting

Chapter 32 — The Laboratory: Reproducing the Book's Claims

Acting

Chapter 33 — The Frontier: What's Changing in 2025-2026

Acting

Chapter 34 — The Book as a System: How to Use It (Human + Agent)

©2026 Mehran Mozaffari. Free for personal/noncommercial use (CC BY-NC-ND 4.0); commercial license required for business use.