← All projects

Personal project · 2026

Claim-to-Evidence Scouting

A scouting report makes claims — "creates a lot of chances", "doesn't track runners". This project tests each one against league data and my own video coding, using rules written down before the data is collected.

Status: the engine and a demo on synthetic data are built and tested. The real case study has no data yet.

Problem

In a scouting report, a line like "protests a lot to the referee" may come from one memorable match or from a real pattern, and the reader can't tell which. It is also easy, once the numbers are in, to adjust the criterion until the claim looks confirmed.

Context and motivation

I write player reports from video and wanted to know which of my own claims would survive a test. The idea comes from research practice: fix the method and the claims before looking at the data (pre-registration), and publish every result, including the ones that don't back the report.

What I built

Individual project: the method, the video-coding protocol, the Python engine, the tests, the CI pipeline and the case study.

From claim to verdict

  1. 01 Before the data

    • player.yaml, claims.yaml, metrics.yaml
    • Validated with pydantic
    • Rules and minimums fixed (pre-registration)
  2. 02 Evidence

    • League snapshot (private CSV)
    • Official match facts
    • My own video coding
    • pandera contracts on every table
  3. 03 Calculation

    • Per 90 minutes and per 100 touches
    • Peer group: same positions, minimum minutes
  4. 04 Rules

    • L — position among peers
    • V — video share (Wilson 95%)
    • M — majority of matches
    • Sensitivity: the weakest result stands
  5. 05 Output

    • claims.csv and evidence-report.md
    • Four figures
    • The conclusion is written by the scout
How Claim-to-Evidence handles each claim in a report.

Technical decisions

5

Three kinds of rule, chosen by the evidence

L places the player's season value among peers in the same positions (top quarter, above the median, or not). V uses the share of opportunities in my own video coding, with a one-sided 95% Wilson bound and leave-one-match-out checks. M counts the matches in which a behaviour was seen. Each claim uses the rule its evidence allows.

Seven verdicts, including "don't know"

Supported, partly supported, not supported, contradicted, inconclusive, insufficient data and unavailable. "Contradicted" only exists where the evidence can genuinely point the other way. No league verdict is presented as a significance test.

The final verdict is the weakest

Each claim also runs the pre-registered sensitivity checks (stricter peer minutes, possession adjustment, ambiguous video events). If they disagree, the weakest result stands.

Validated configuration, contracted data

Player, claims and metrics are YAML validated with pydantic; data tables have pandera contracts. Adding a player means copying a folder and editing two files — no code changes.

Third-party data stays out of Git

Only synthetic data is committed, and a test checks that. Real snapshots live in a private folder, with source, URL and retrieval date on every row.

What it does

  • c2e validate and c2e run CLI over a player folder
  • Per-90 and per-100-touches rates from season totals
  • Peer group by position and minimum minutes
  • L, V and M rules with sensitivity checks
  • Markdown report, verdict table and four figures
  • Video-coding protocol and a one-page coding reference

Evidence

what was checked, and how
WhatResultSourceChecked
Automated tests97 passedpytest run locally (hand-worked cases, Hypothesis invariants, golden end-to-end test)
Continuous integrationruff, mypy, pytest and the demo on every push.github/workflows/ci.yml
Method status1.0-rc1, draftdocs/METHOD.md — frozen at the prereg-v1.0 tag

Screenshots

2
Table listing report claims with the type of evidence, the verdict and the numeric basis for each.
Claim board produced by the demo. Synthetic data: the player, teams and matches are fictional.
Horizontal strips showing where a player sits among peers on several metrics.
Position among peers per metric. Synthetic data.

Limitations and next steps

Known limitations

  • No real data yet: the engine and demo run on an invented player, teams and matches.
  • The method and the claims for the case study (an attacking midfielder/winger in the Greek Super League) are drafts awaiting review before pre-registration.
  • League positions are point estimates on small samples; they don't say whether a difference comes from the player, the role or the team.

Next steps

  • Review and freeze the method and claims at the prereg-v1.0 tag
  • Collect the league snapshot and code the case-study matches
  • Publish the evidence report with every verdict