# TFAG practice dataset: data dictionary (v2)

*Fictional practice data created by The Football Analysis Guy.*

## Files

- `data/competitions.json`: the competition and season.
- `data/matches/1001/2026.json`: the matches in that competition and season.
- `data/lineups/7001.json`: both squads, starters and substitutes.
- `data/events/7001.json`: every event, in order, as nested JSON.
- `tfag-practice-match-7001-events.csv`: the same events, one row each, with ids for joining.
- `tfag-practice-match-7001-spadl.csv`: the on-ball actions in the open SPADL format (socceraction), 105 x 68 m pitch.
- `tfag-practice-match-7001.xlsx`: events, SPADL actions, line-ups and this dictionary in one spreadsheet.

## Events columns

| Column | Meaning |
|---|---|
| match_id | The match the event belongs to (7001). |
| index | Order of the event in the match, starting at 1. |
| id | Unique id for the event. linked_event_id points at these. |
| period | 1 = first half, 2 = second half. |
| minute, second | Match clock. Second-half minutes carry on from 45. |
| timestamp | Time since the start of the period, hh:mm:ss.sss. |
| possession | Possession number. A new number starts whenever the other team gains control, and at every restart. |
| possession_team(_id) | The team that started the possession. Opponents' events inside it (pressures, fouls, blocks) keep this value. |
| play_pattern | How the possession started: Regular Play, From Kick Off, From Goal Kick, From Throw In, From Free Kick, From Corner or From Keeper. Constant within a possession. |
| team(_id), player(_id), position | Who performed the event, and the position they were playing. |
| type(_id) | The action: Pass, Ball Receipt, Carry, Pressure, Shot, Goalkeeper, Ball Recovery, Interception, Clearance, Duel, Dribble, Dribbled Past, Block, Miscontrol, Dispossessed, Offside, Foul Committed, Foul Won, Substitution, plus Starting XI, Half Start and Half End. |
| sub_type | Extra detail: the pass type (Kick Off, Goal Kick, Throw-in, Free Kick, Corner), the shot type, the duel type (Tackle), the goalkeeper action (Shot Saved, Goal Conceded) or a card. |
| outcome | Pass: Complete, Incomplete or Offside. Ball Receipt: Complete or Incomplete (an intended receipt that did not arrive). Shot: Goal, Saved, Off Target, Blocked or Wayward. Dribble: Complete or Incomplete. Duel: Won. |
| x, y | Where the event happened, on a 120 x 80 pitch. Every team is shown attacking from x = 0 towards x = 120, and y = 0 is that team's left touchline. The goal it attacks is at x = 120, with posts at y = 36 and y = 44. |
| end_x, end_y, end_z | Where a pass, carry or shot ended. end_z is the height of a shot as it reached the goal line, in pitch units (the crossbar is at 2.44). |
| under_pressure | True when an opponent was pressing the player. |
| recipient(_id) | The intended receiver of a pass. |
| pass_length, pass_height | Length in pitch units, and Ground, Low or High Pass. |
| body_part | Right Foot, Left Foot, Head or Hands. |
| xg | Our own simple expected-goals value for each shot, from its distance and the angle to the goal. Fictional, for practice. |
| linked_event_id | Events this one is linked to, separated by ;. A pass links to its receipt, a shot to its block or goalkeeper event. |
| duration | Seconds the event lasted. |

## JSON-only fields

| Field | Meaning |
|---|---|
| pass.angle | Direction of the pass in radians: 0 is straight towards the goal being attacked. |
| pass.outcome | Only present when the pass failed. No outcome means the pass was complete. |
| shot.assist_event_id | The pass that set up the shot, when there was one. |
| related_events | Ids of linked events, in both directions. |
| tactics | On Starting XI events: the formation and starting line-up. |

## The pitch

Coordinates are always from the acting team's point of view, attacking left to right. To compare both teams on one pitch, flip the away team: x' = 120 - x, y' = 80 - y. To convert to a 0-100 scale: x * 100/120 and y * 100/80. To convert to a 105 x 68 m pitch with y measured from the bottom: x * 105/120 and 68 - y * 68/80 (this is how the SPADL file is made).

## Where this data comes from

The TFAG practice dataset is invented from start to finish. The clubs, players, matches and events don't exist, and none of it was derived from any data provider's data. We built it to follow the conventions working analysts meet in professional and open football event data: a competitions -> matches -> lineups -> events file layout, nested JSON events with possession and play-pattern fields, and a 120 x 80 pitch on which each team attacks left to right. The skills you practise here carry straight over. Every id, every metric (including our simple xG model) and every word of the documentation is our own. It is aligned with common industry practice, not copied from any provider's product, and no data provider endorses or is affiliated with it.
