MatchTimeline
MatchTimeline is the central time-indexed container for 60 FPS match dynamics, coupling frame-by-frame FrameState snapshots with static domain metadata (Court, EnvironmentConditions, BallProperties, SurfaceMaterials, and Match) for lossless serialization to Apache Parquet (.parquet), CSV, and Pandas DataFrames.
Properties
- Name
frames- Type
- list[FrameState]
- Description
Chronological sequence of
FrameStatesnapshots.
- Name
fps- Type
- float
- Description
Nominal sampling frequency in frames per second.
- Name
court- Type
- Court | None
- Description
Court geometry, enclosure, turf color, and
CourtLocationmetadata associated with the recording.
- Name
environment- Type
- EnvironmentConditions | None
- Description
Atmospheric and environmental parameters (
temperature_c,relative_humidity,altitude_m,wind_velocity,air_density,gravity).
- Name
ball_properties- Type
- BallProperties | None
- Description
Physical ball properties (
mass,radius,cor_normal,fuzz_factor,spin_decay_tau).
- Name
surface_materials- Type
- SurfaceMaterials | None
- Description
Friction and restitution parameters for the court floor, glass walls, fence, and net.
- Name
match- Type
- Match | None
- Description
Associated
Matchdomain entity (teams, players,MatchConfiguration, andscore_history).
- Name
metadata- Type
- dict[str, Any]
- Description
Arbitrary user-defined JSON-serializable key-value metadata preserved in
.parquetheaders.
- Name
duration- Type
- float
- Description
Total elapsed duration in seconds (
frames[-1].timestamp - frames[0].timestamp).
- Name
has_skeletal_poses- Type
- bool
- Description
Returns
Trueif any player in any frame contains 17-joint 3D skeletal keypoints (keypoints_3d).
Export to Universal Parquet
Writes the timeline to a single self-contained .parquet file. Static domain entities (court, environment, ball_properties, surface_materials, match, metadata) are serialized as JSON inside the Parquet schema header under b"padelkit_metadata", while frame dynamics are stored as columnar float32 / int16 / string arrays.
Arguments
- Name
path- Type
- str | Path
- Description
Destination
.parquetfile path.
- Name
compression- Type
- str
- Description
Parquet compression codec (
"zstd","snappy","gzip", or"none").
- Name
bake_skeleton- Type
- bool
- Description
If
Trueandnot timeline.has_skeletal_poses, automatically callsbake_skeletal_poses()before writing so all2043D joint columns (p1_j0_x..p4_j16_z) are included.
- Name
animation_library_dir- Type
- str | Path | None
- Description
Optional path to a
padelkit-animatorlibrary folder used whenbake_skeleton=True.
Save to Parquet
from padelkit import Court, EnvironmentConditions, MatchTimeline
timeline = MatchTimeline.demo_rally(
court=Court.fip_standard(),
environment=EnvironmentConditions.from_atmosphere(
temperature_c=22.0,
altitude_m=650.0,
relative_humidity=0.45,
),
)
path = timeline.to_parquet(
"rally_recording.parquet",
compression="zstd",
bake_skeleton=True,
)
print(f"Saved {len(timeline.frames)} frames to {path}")
Load from Universal Parquet
Reads a .parquet file written by to_parquet(), reconstructs all attached domain objects (court, environment, ball_properties, surface_materials, match) from the padelkit_metadata schema header, and restores all FrameState objects (including 17-joint keypoints_3d when present).
Arguments
- Name
path- Type
- str | Path
- Description
Source
.parquetfile path.
Load from Parquet
from padelkit import MatchTimeline
timeline = MatchTimeline.from_parquet("rally_recording.parquet")
print("Frames:", len(timeline.frames), "@", timeline.fps, "FPS")
print("Has 3D skeletons:", timeline.has_skeletal_poses)
print("Court turf:", timeline.court.turf_color)
print("Air density:", timeline.environment.air_density)
Bake 3D Skeletal Poses
Populates PlayerPose.keypoints_3d (shape (17, 3) in the H36M_17 convention) for every player in every frame of the timeline.
If animation_library_dir is provided and contains .npz or .glb clips matching PlayerPose.animation, their motion-captured 3D joint trajectories are phase-aligned via contact_swing_phase and rotated by PlayerPose.yaw_deg. Otherwise, PadelKit synthesizes an anatomically accurate 17-joint athletic padel rig driven by the player's locomotion cycle and stroke swing_phase.
Arguments
- Name
animation_library_dir- Type
- str | Path | None
- Description
Optional directory containing
manifest.jsonand 3D clips frompadelkit-animator.
- Name
overwrite- Type
- bool
- Description
When
False, preserves existingPlayerPose.keypoints_3darrays (e.g., from camera pose estimation) and only bakes missing players/frames.
Bake 17-Joint Skeletons
from padelkit import MatchTimeline
timeline = MatchTimeline.demo_rally(fps=60.0)
assert not timeline.has_skeletal_poses
timeline.bake_skeletal_poses(
animation_library_dir="../padelkit-animator/data/animations"
)
assert timeline.has_skeletal_poses
p1_pose = timeline.frames[30].players["P1"]
print("Root-relative (17 joints):", len(p1_pose.keypoints_3d))
print("World Pelvis (X, Y, Z):", p1_pose.world_keypoints_3d[0])
DataFrame & CSV Conversion
Converts between MatchTimeline and tabular Pandas DataFrame or .csv files. All structured score columns, 9D ball state columns, and optional 204 skeletal columns (p1_j0_x..p4_j16_z) are supported in both DataFrames and CSV files.
Methods
- Name
to_dataframe(include_skeleton=None)- Type
- pd.DataFrame
- Description
Converts all frames into a flat Pandas DataFrame. By default (
include_skeleton=None), includes the204skeletal columns automatically ifhas_skeletal_posesisTrue.
- Name
from_dataframe(df, fps=60.0, ...)- Type
- MatchTimeline
- Description
Classmethod that reconstructs a
MatchTimelinefrom a Pandas DataFrame, automatically detectingp{1..4}_j{0..16}_{x,y,z}columns when present.
- Name
to_csv(path, include_skeleton=False)- Type
- Path
- Description
Writes the timeline to a
.csvfile (defaults to compact root-pose columns unlessinclude_skeleton=True).
- Name
from_csv(path, fps=60.0)- Type
- MatchTimeline
- Description
Classmethod that loads a
MatchTimelinefrom a.csvfile.
- Name
build_metadata_envelope()- Type
- dict[str, Any]
- Description
Returns the serializable metadata dictionary stored in the
padelkit_metadataParquet header.
- Name
demo_rally(court=None, fps=60.0, ..., bake_skeleton=False)- Type
- MatchTimeline
- Description
Classmethod that generates a physics-driven 6-shot padel rally (Serve, Return, Volley,Chiquita, Lob, Bandeja) with 9D ball aerodynamics and 4 players.
DataFrame Roundtrip
from padelkit import MatchTimeline
timeline = MatchTimeline.demo_rally(bake_skeleton=True)
df = timeline.to_dataframe()
print("Rows:", len(df), "| Columns:", len(df.columns))
restored = MatchTimeline.from_dataframe(df, fps=timeline.fps)
assert restored.has_skeletal_poses