Single-match team report generator for football: 18-page PDF (overview, per-team passing/carries/defense/network, individual maps, shots, momentum, top-performer tables) from SPADL event data.
Project description
tmviz
Single-match team report generator for football. Feed it one SPADL-style event CSV; get back an 18-page report (per-team passing, carries, defensive activity, individual maps, shot map, match-flow momentum, top performers) plus a stitched PDF.
python -m tmviz make <match.csv> --home X --away Y [...flags] # one-shot, scriptable
python -m tmviz wizard <match.csv> # interactive prompts
python -m tmviz render <config.yml> # re-render a saved config
python -m tmviz pages # list available pages
Install
git clone https://github.com/yureed/tmviz.git
cd tmviz
pip install -e .
playwright install chromium # one-time
Quickstart
python -m tmviz make match.csv \
--home "Arsenal" --away "Atletico" \
--matchday 35 --date 2026-04-26 \
--venue "Emirates Stadium" \
--competition "Champions League 25/26" \
--out-dir out/arsenal_atletico
Renders 18 PNGs and one combined PDF to out/arsenal_atletico/. Run with --help to see every flag.
Python API
from tmviz import generate_report
generate_report(
csv="match.csv",
home="Arsenal", away="Atletico",
matchday=35, date="2026-04-26",
venue="Emirates Stadium",
competition="Champions League 25/26",
out_dir="out/arsenal_atletico",
)
Returns {"html_paths": [...], "png_paths": [...], "pdf_path": Path}.
Wizard
For interactive use — auto-detects team_ids, lists each detected goal so you can flag own-goals, collects metadata, and saves a YAML config you can re-render later:
python -m tmviz wizard match.csv
Data format
One row per event. Required columns:
| column | type | notes |
|---|---|---|
type_name |
str | event verb — see vocabulary below |
team_id |
int | one of two team ids in the file |
period_id |
int | 1, 2 (3, 4 if extra time) |
time_seconds |
float | seconds within the period |
start_x, start_y |
float | Opta 0–100 coordinates |
end_x, end_y |
float | same scale |
type_name vocabulary — tmviz uses the SPADL standard. The names that map to common concepts:
| concept | type_name value(s) |
|---|---|
| pass | pass |
| cross | cross |
| carry (a player moving with the ball) | dribble |
| take-on (1v1 dribble past a defender) | take_on |
| shot | shot |
| direct free-kick shot | shot_freekick |
| penalty shot | shot_penalty |
| corner delivery (in-swinger / out-swinger) | corner_crossed |
| corner played short | corner_short |
| free-kick whipped into the box | freekick_crossed |
| free-kick played short | freekick_short |
| throw-in | throw_in |
| tackle | tackle |
| interception | interception |
| clearance | clearance |
| header (any of above when contested in the air) | bodypart_name = head |
If you have data from a different schema, you'll need a thin transform to remap your event names to these strings before handing the CSV to tmviz. The vocabulary above is the SPADL convention used by socceraction and most public xT/xG models.
Player names — exactly one of:
- a
player_namecolumn inline in the main CSV (preferred), or - a
player_idcolumn + a separate file passed via--players-csv(cols:player_id, player_name). tmviz joins it in at load.
Team names come from the --home / --away flags (or wizard prompts). Optional: pass --teams-csv (cols: team_id, team_name) to give the wizard nicer name suggestions.
Recommended (better output if present)
| column | what improves |
|---|---|
result_name |
success/fail — feeds pass-completion %, take-on success rate, etc. |
is_goal |
flags goals on the shot map and in the goals timeline |
Optional (auto-used if present, ignored if not)
| column | what it does |
|---|---|
xG (or xg) |
sizes shot circles by chance quality, adds team xG totals + an xG column on the tables page, and switches the match-flow chart to xG-based momentum |
xT_added |
per-event expected-threat. If absent, tmviz auto-computes it on load using the bundled Karun Singh xT grid — you only need raw coordinates. |
Coordinate orientation
tmviz expects each team's events in their own attacking direction (low x = own goal, high x = opp goal — SPADL canon). If your input uses absolute coordinates (one fixed system where home attacks right and away attacks left), tmviz detects it from per-team mean shot start_x and flips the away team automatically. You'll see Note: detected absolute coords — flipped team_id N in the load output when this happens.
Pages
| name | what |
|---|---|
overview |
Score, goals timeline, both-team shot dots on a mini pitch, head-to-head stat comparison (possession, shots, xG/xT, pass %, prog passes, prog carries, take-ons, box entries, defensive actions) |
passing_home, passing_away |
Full-pitch open-play pass map per team. Forward = ink, backward = grey, failed = red dotted, progressive = gold. Side panel: top 5 progressive passers + key team rates |
network_home, network_away |
Pass network: nodes at average position sized by passes attempted, edges sized by combination count, top 8 combinations listed |
players_pass_home, players_pass_away |
One mini-pitch per player who featured, showing their passes for the match |
carries_home, carries_away |
All carries (progressive in gold) + take-on attempts (won/lost), top 5 carriers + take-on winners |
players_carry_home, players_carry_away |
One mini-pitch per player, showing their carries + take-ons |
defense_home, defense_away |
Defensive actions heatmap + shape markers (tackle / interception / clearance / aerial duel won-or-lost) |
players_defense_home, players_defense_away |
One mini-pitch per player, showing their defensive actions |
shots |
Both teams' shots on a single full pitch — colored by team, sized by xG (if present), goals haloed in gold |
flow |
Match-flow momentum graph: rolling 5-minute danger signal per team plotted around a zero line (above = home on top, below = away). Uses xG if available, otherwise xT. Goals planted on the zero line. |
tables |
6 (or 7 with xG) side-by-side per-team tables: top 5 in xT, prog passes, prog carries, take-ons, defensive actions, shots |
Use --pages overview,shots,flow,tables (or any subset) to generate fewer pages.
Output
For each match:
out/<dir>/01_overview.html…18_tables.html- One PNG per page (rendered at 2× device scale)
- One combined PDF:
<home>_<score>_<away>_md<n>.pdf
Themes
cream (default) — newsprint paper, ink black, red/gold/blue accents.
To add a theme: drop a dataclass into tmviz/themes/, register it in tmviz/themes/__init__.py:THEMES. Pages read all colors via CSS variables so a theme swap restyles every page.
Adding a new page
- Subclass
tmviz.pages.base.BasePageand implementbuild(cfg, df, derived) -> strreturning the inner HTML (the shell adds top strip + footer + styles). - Register in
tmviz/pages/__init__.py:REGISTRY. - Add the page name to
cfg.pagesin any config that wants it.
License
MIT.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file tmviz-0.1.1.tar.gz.
File metadata
- Download URL: tmviz-0.1.1.tar.gz
- Upload date:
- Size: 46.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
856fa3666e0ff31b019e679d1eba9f07ee610f1b5bcf6495f09a0b8bb53e600b
|
|
| MD5 |
0129bbc6a10663eefe511289ad2f98e2
|
|
| BLAKE2b-256 |
6d1459500400349e6717dfbc27e6571720df1ea59af271e660cc793c80d17c69
|
File details
Details for the file tmviz-0.1.1-py3-none-any.whl.
File metadata
- Download URL: tmviz-0.1.1-py3-none-any.whl
- Upload date:
- Size: 56.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
83fdcc55ba7b8e54220a03ea5b0ac44e9b411bf42eca95a404bdb4526cbc3a88
|
|
| MD5 |
7604f5f0bc7181af50d789cb963ab28f
|
|
| BLAKE2b-256 |
f82cb62f474d0d154a2df6ec89cbcfa9f00ca39481c6ac069f77a4c8d095fe1a
|