TrackingDataset¶
The TrackingDataset is the central object returned by every provider's load_tracking() function. It contains all tracking data and metadata. The underlying type of each table (Polars, PySpark, or pyarrow) is determined by the engine kwarg passed to load_tracking.
Properties¶
| Property | Type | Description |
|---|---|---|
tracking |
pl.DataFrame / SparkDataFrame / pyarrow.Table |
Positional data for all players and the ball |
metadata |
pl.DataFrame / SparkDataFrame / pyarrow.Table |
Match-level information (1 row) |
teams |
pl.DataFrame / SparkDataFrame / pyarrow.Table |
Team information (2 rows: home and away) |
players |
pl.DataFrame / SparkDataFrame / pyarrow.Table |
Player roster with positions and starter status |
periods |
pl.DataFrame / SparkDataFrame / pyarrow.Table |
Period boundaries with start/end frame IDs |
engine |
str |
One of "polars", "pyspark", "arrow", "arrow[spark]" |
schemas |
Schemas |
Namespace exposing Arrow + PySpark schemas for all 5 tables (kwargs auto-bound from the load). See Obtaining schemas. |
coordinate_system |
str |
Current coordinate system name |
orientation |
str |
Current orientation name |
pitch_dimensions |
tuple[float, float] |
(pitch_length, pitch_width) in meters |
DataFrame Schemas¶
The dtypes below are for engine="polars". Arrow engines emit different physical types for some columns. See Per-engine schema for the full comparison.
tracking¶
The main DataFrame containing positional data. Schema depends on the layout.
Long layout (default):
| Column | Type | Description |
|---|---|---|
game_id |
String | Match identifier (if include_game_id=True) |
frame_id |
UInt32 | Frame index |
period_id |
Int32 | Period number (1, 2, 3, ...) |
timestamp |
Duration(ms) | Time since period start |
ball_state |
String | "alive" or "dead" |
ball_owning_team_id |
String | Team ID with possession |
team_id |
String | Team ID ("ball" for ball rows) |
player_id |
String | Player ID ("ball" for ball rows) |
x |
Float32 | X coordinate |
y |
Float32 | Y coordinate |
z |
Float32 | Z coordinate (height) |
metadata¶
Single-row DataFrame with match-level information.
| Column | Type | Description |
|---|---|---|
game_id |
String | Match identifier |
provider |
String | Provider name (e.g., "secondspectrum") |
game_date |
Date | Match date |
home_team |
String | Home team name |
home_team_id |
String | Home team ID |
away_team |
String | Away team name |
away_team_id |
String | Away team ID |
pitch_length |
Float32 | Pitch length in meters |
pitch_width |
Float32 | Pitch width in meters |
fps |
Float32 | Frames per second |
coordinate_system |
String | Current coordinate system |
orientation |
String | Current orientation |
teams¶
Two rows: one for home, one for away.
| Column | Type | Description |
|---|---|---|
game_id |
String | Match identifier |
team_id |
String | Team identifier |
name |
String | Team name |
ground |
String | "home" or "away" |
players¶
One row per player.
| Column | Type | Description |
|---|---|---|
game_id |
String | Match identifier |
team_id |
String | Team identifier |
player_id |
String | Player identifier |
name |
String | Full name (nullable) |
first_name |
String | First name (nullable) |
last_name |
String | Last name (nullable) |
jersey_number |
Int32 | Shirt number |
position |
String | Standardized position code |
is_starter |
Boolean | Whether player started the match |
Position codes:
| Code | Position | Code | Position |
|---|---|---|---|
| GK | Goalkeeper | CM, LCM, RCM | Central Midfield |
| LB, RB | Left/Right Back | CAM, LAM, RAM | Attacking Midfield |
| CB, LCB, RCB | Center Back | LM, RM | Left/Right Midfield |
| LWB, RWB | Wing Back | LW, RW | Left/Right Wing |
| CDM, LDM, RDM | Defensive Midfield | ST, LF, RF, CF | Strikers/Forwards |
| SUB | Substitute | UNK | Unknown |
| REF | Main Referee | AREF | Assistant Referee |
| VAR | VAR Official | AVAR | Assistant VAR |
| FOURTH | Fourth Official |
For a full reference on position codes, see the kloppy positions documentation.
periods¶
One row per period found in the data.
| Column | Type | Description |
|---|---|---|
game_id |
String | Match identifier |
period_id |
Int32 | Period number |
start_frame_id |
UInt32 | First frame of the period |
end_frame_id |
UInt32 | Last frame of the period |
Per-engine schema¶
The tracking schema dtypes for SkillCorner (long layout, all include flags on), shown across the four engine values:
| Column | engine="polars" (default) |
engine="arrow" (Polars-style Arrow) |
engine="arrow[spark]" (Spark-compat) |
engine="pyspark" |
|---|---|---|---|---|
game_id |
String |
string_view |
string |
StringType |
frame_id |
UInt32 |
int64 |
int64 |
LongType |
period_id |
Int32 |
int32 |
int32 |
IntegerType |
timestamp |
Duration(ms) |
duration[ms] |
int64 (ms) |
LongType |
ball_state |
String |
string_view |
string |
StringType |
ball_owning_team_id |
String |
string_view |
string |
StringType |
team_id |
String |
string_view |
string |
StringType |
player_id |
String |
string_view |
string |
StringType |
x, y, z |
Float32 |
float (float32) |
float (float32) |
FloatType |
ball_owning_player_id |
String |
string_view |
string |
StringType |
is_detected |
Boolean |
bool |
bool |
BooleanType |
Two columns differ between the two arrow dialects: string_view vs string, and duration[ms] vs int64. The other arrow columns are identical between engine="arrow" and engine="arrow[spark]". The choice between them is dictated by what your downstream framework accepts, not by which one is more correct. See the Distributed Compute page for guidance.
The non-tracking tables (metadata, teams, players, periods) follow the same dialect convention. The dtype list above applies to their string and integer columns equivalently.
Obtaining schemas¶
Two ways to get the Arrow + PySpark schemas. Both produce the same Schemas namespace with 10 lazy properties: tracking, metadata, teams, players, periods (Arrow), and tracking_spark, metadata_spark, teams_spark, players_spark, periods_spark (PySpark StructType).
dataset.schemas is available on any loaded dataset, with the load's kwargs already bound.
dataset = skillcorner.load_tracking(raw, meta, engine="arrow[spark]")
dataset.schemas.tracking_spark # StructType you'd pass to mapInArrow
skillcorner.schemas(...) is for when you don't have data loaded yet (for example, declaring a Spark mapInArrow output schema before any worker runs). It accepts the same kwargs as load_tracking, so unpack a single config dict into both:
LOAD_KWARGS = dict(
engine="arrow[spark]",
layout="long",
include_game_id=True,
include_ball_owning_player=True,
include_is_detected=True,
)
out_schema = skillcorner.schemas(**LOAD_KWARGS).tracking_spark
See the Distributed Compute page for the deeper explanation of when tracking vs tracking_spark matters and how the engine value controls the Arrow dialect.
Engine converters¶
Every TrackingDataset can be converted between engines. Polars ↔ Arrow is zero-copy via the Arrow C Data Interface capsule.
ds_pl = skillcorner.load_tracking(raw, meta, engine="polars")
ds_arr = ds_pl.to_arrow() # engine="arrow"
ds_arr_spark = ds_pl.to_arrow(engine="arrow[spark]")
ds_back = ds_arr.to_polars() # back to engine="polars"
ds_spark = ds_pl.to_pyspark(spark_session) # engine="pyspark"
Round-trip preserves values exactly. arrow to polars to arrow produces a tracking table with the same column names, row count, and column values as the original.
Methods¶
transform()¶
Transform coordinates, orientation, or pitch dimensions:
transformed = dataset.transform(
to_coordinates="opta", # Target coordinate system
to_orientation="home_away", # Target orientation
to_dimensions=(100, 100), # Target (length, width) in meters
)
All three parameters are optional. Transforms are applied in a fixed order: orientation, then dimensions, then coordinates. See Transformations for details.
to_polars() / to_pyspark() / to_arrow()¶
Convert between DataFrame engines. See Engine converters above for the full table; brief examples: