An open, versioned, per-action record of rugby union matches — the thing cricket has in Cricsheet and football has in StatsBomb open-data, and rugby has nowhere.
One row per action, the same columns whatever the source. A schema strict enough that a corpus built on it stays readable by someone who did not write the producer, and a licence that lets them use it: MIT for the code, CC BY 4.0 for the data, with no requirement to display anyone's logo.
Status: v0.2.2 draft. One producer, one converter, and no real match traced
yet. Everything in data/ is synthetic. That is the gap this exists to close,
and it is the honest reason to look at the schema now rather than later — the
conventions are still cheap to change.
| football | cricket | rugby union | |
|---|---|---|---|
| open per-action corpus | StatsBomb open-data | Cricsheet, 15,000+ matches | nothing |
| flat action language | SPADL | ball-by-ball, by construction | nothing |
| open packages | many | cricketdata |
rugbypy — match/player aggregates, not actions |
Rugby data exists, but it stops at aggregates or sits behind a commercial feed. There is no free file you can open that says this carry started here, ended there, and gained nine metres.
pip install -e .
radl validate data/events/synthetic-001.json # conformance, exit code only with -q
radl schema > radl.schema.json # JSON Schema, for non-Python readersimport radl
actions = radl.read_match("data/events/synthetic-001.json")
actions[actions.action_type == "carry"].metres_gained.sum()
radl.score(actions) # {'ENG': 12, 'WAL': 20}
radl.left_to_right(actions) # SPADL's per-team orientationread_match returns a pandas DataFrame with the 23 columns in
docs/spec-v0.2.md §1. Every vocabulary is closed and
enforced: a value outside one raises, naming the row it came from, rather than
entering a corpus where it would read as part of the standard.
The CLI and the JSON Schema exist so RADL is
checkable from R, JS, or a CI job — most rugby analytics is not written in
Python, and a standard only reachable through import pandas is not one.
| path | |
|---|---|
| docs/spec-v0.2.md | the schema and the pitch coordinate convention, in enough detail to write a converter from |
| docs/radl-v0.2.2.schema.json | the same thing machine-readable, generated from config.py |
| docs/governance.md | who decides a version, what counts as breaking |
| docs/rfc-001-tackle.md | the open question: rugby's central contest has no row |
| docs/opta-superscout-mapping.md | how the vocabulary lines up against a commercial rugby feed |
| radl/config.py | the closed vocabularies and pitch constants |
| radl/phase_trace.py | the one converter: phase-trace's raw export → an action frame |
| radl/frame.py | the two operations on a frame: left_to_right, score |
| data/ | the seed corpus, StatsBomb layout, currently one synthetic match |
| tools/ | the generator for that synthetic match; not part of the package |
RADL borrows its shape from three football projects and says, throughout, which one each decision came from. It takes SPADL's middle idea — one row per on-the-ball action, same columns whatever the source — because that is the part which is not sport-specific, and pairs it with StatsBomb's corpus layout. The spec cites each borrowing at the point it is used.
Two places it deliberately goes further than its sources. SPADL's specification
is its documentation and config.py; RADL's is a versioned document with a
changelog, because a corpus outlives the code that read it. And SPADL does not
validate its vocabularies on ingest; RADL refuses the row.
phase-trace is the only producer
of RADL-shaped rugby data today, so it is the only converter. There is no
deserializer layer for providers that do not exist. It reads the match.json
written by phase-trace's raw exporter as plain JSON and never imports
phase-trace, treating that file as a stable interface.
phase-trace's other export, the momentum-chart JSON, is not a valid input. Its rows bundle a whole possession and carry no coordinates, so it cannot produce one row per action.
No valuation framework: no VAEP, no xT equivalent, no action weights. There is no real corpus to fit or validate one against yet, and a published valuation nobody can check is worse than none. See spec §8.
No tackle. Rugby's central contest is not in the schema, which is the largest
honest gap and the reason result is null on nearly every carry.
RFC 001 is the open proposal, not a plan.
pip install -e .[dev]
pytest
Regenerating the synthetic seed needs a phase-trace checkout:
python tools/make_synthetic_match.py ../match-momentum
Code: MIT (LICENSE). Data: CC BY 4.0 (LICENSE-DATA).