Skip to main content

Fast, pythonic access to MotoGP data — inspired by FastF1

Project description

PyMotoGP

CI Python License: MIT

Fast, pythonic access to MotoGP data — inspired by FastF1.

PyMotoGP gives you session-centric access to qualifying, sprint, and race data from the official PulseLive API and DORNA timing PDFs. Load a session, get a pandas DataFrame of laps, plot pace, run analytics, predict race outcomes.

import motogp

session = motogp.load(2024, 'qatar', 'Q2')
print(session.best_lap.rider_name)         # 'Jorge MARTIN'
df = session.laps.to_dataframe()           # full lap data, sectors, top speeds
session.plot.lap_times()                   # matplotlib figure
session.analysis.gap_to_pole()             # gap analysis

Install

pip install pymotogp

Requires Python 3.10+. Core dependencies: requests, pandas, pdfplumber, matplotlib.


Quickstart

Load a session

import motogp

# Year + event + session label. Event matches by name, country, or short code.
session = motogp.load(2024, 'cataluña', 'Q2')

Supported session labels: Q1, Q2, qualifying, FP, practice, sprint, warm-up, race.

Inspect laps

df = session.laps.to_dataframe()
# Columns: rider_name, lap_number, lap_time_ms, sector1_ms ... sector4_ms,
#          top_speed, is_valid, is_cancelled, is_pit, is_best

session.best_lap                  # fastest valid lap
session.riders                    # list of rider names
session.classification            # official position list

Plot

fig = session.plot.lap_times(riders=['Bagnaia', 'Martin'])
fig.savefig('out.png')

session.plot.pace_distribution()      # boxplot per rider
session.plot.sector_comparison()      # grouped bars
session.plot.compare('Bagnaia', 'Martin')  # 2-panel comparison

All plotting methods return a matplotlib.figure.Figure. Cancelled laps, pit laps, and likely outlaps (>1.15× rider median) are filtered by default.

Analyze

session.analysis.theoretical_best()       # sum of best sectors per rider
session.analysis.gain_potential('Bagnaia')   # ms left on the table
session.analysis.gap_to_pole()
session.analysis.sector_strength()
session.analysis.consistency_ranking()

Cross-season history

from motogp import HistoricalAnalyzer

hist = HistoricalAnalyzer()
hist.track_evolution('catalunya')         # pole-time progression by year
hist.rider_form('Bagnaia', year=2024)     # per-round qualifying form
hist.team_pace(year=2024)                 # aggregate by team

Predict race pace (transparent baseline)

from motogp import RacePaceEstimator

qual = motogp.load(2024, 'malaysia', 'Q2')
est = RacePaceEstimator()
est.predict(qual, n_laps=20)

The estimator uses a transparent linear model: race_lap(n) = q_best + race_offset + degradation × (n − 1). All assumptions are exposed as parameters. See "Honest limits" below for accuracy data.


Data sources

PyMotoGP uses a two-tier resolution strategy:

  1. Local cache (instant) — if you have a directory of pre-scraped JSON files, set MOTOGP_SCRAPER_OUTPUT to point at it. Hits return instantly.
  2. PulseLive API (live) — falls back to the official MotoGP API: api.motogp.pulselive.com/motogp/v1/. Discovers the session, downloads the official Analysis PDF, parses it with pdfplumber. PDFs are cached at ~/.motogp_pdfs/.

No API key required. Be considerate — the library caches everything.

Environment variables

Variable Default Purpose
MOTOGP_SCRAPER_OUTPUT ~/.motogp/scraper_output Pre-scraped JSON cache
MOTOGP_PDF_CACHE ~/.motogp_pdfs DORNA Analysis PDF cache

Honest limits

PyMotoGP includes a backtest pipeline so you can measure model accuracy instead of trusting it blindly.

from motogp.analysis import RacePaceValidator

v = RacePaceValidator()
df = v.validate_season(2024)
v.summarize(df)

2024 season backtest results (20 GP races validated):

Metric Value
Winner hit rate 30%
Podium overlap (mean of 3) 1.2
Position MAE 3.5 places
Kendall's tau 0.24

Treat RacePaceEstimator.predict() as a directional baseline, not a black-box predictor. The model assumes qualifying pace transfers linearly to race pace with uniform degradation — which is wrong for ~70% of MotoGP races because tire management, race craft, weather, and DNFs aren't captured. Calibrate per-track with est.calibrate_from_race(qual, race) once you have real race data.


Architecture

motogp/
├── core/           # Session, Lap, Sector, Rider data models
├── api/            # PulseLive client + DORNA PDF parser
├── plots/          # Matplotlib-based session plots
├── analysis/       # SessionAnalyzer, HistoricalAnalyzer,
│                   # RacePaceEstimator, RacePaceValidator

Roadmap

  • Phase 1 — PulseLive API wrapper, core data models, caching
  • Phase 2 — Real data integration (scraper cache + PulseLive + PDFs)
  • Phase 3 — Session/historical/race-pace analytics + validation
  • 🚧 Phase 4 — Sphinx docs, VCR-cassette integration tests, PyPI release

Contributing

Issues and PRs welcome. Run tests with:

pip install -e ".[dev]"
pytest

License

MIT

Not affiliated with Dorna Sports or MotoGP™. All data is sourced from publicly available APIs and PDFs for personal and research use.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pymotogp-0.1.0.tar.gz (50.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pymotogp-0.1.0-py3-none-any.whl (42.1 kB view details)

Uploaded Python 3

File details

Details for the file pymotogp-0.1.0.tar.gz.

File metadata

  • Download URL: pymotogp-0.1.0.tar.gz
  • Upload date:
  • Size: 50.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for pymotogp-0.1.0.tar.gz
Algorithm Hash digest
SHA256 643b243a43b33027520a74262a77e5ade70c7a6c77abe23be42bff911da7af8f
MD5 26efbb16dd68593ddac09ab79aa4be99
BLAKE2b-256 367be9099209fd71b562930c4887f0ecaf7caeddc56e942df75ea771f6557c1f

See more details on using hashes here.

Provenance

The following attestation bundles were made for pymotogp-0.1.0.tar.gz:

Publisher: release.yml on tejred213/PyMotoGP

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pymotogp-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: pymotogp-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 42.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for pymotogp-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 cda334029a5febc2540115be6a3be8c78af28a22989d566963041244efd7d931
MD5 4db1347f2760e4fa1fa7ffd220a47bf7
BLAKE2b-256 8adc57eda625522eb1ea3e046bd85a7dc8f2ef31a8565bbf80ccefef309efa8d

See more details on using hashes here.

Provenance

The following attestation bundles were made for pymotogp-0.1.0-py3-none-any.whl:

Publisher: release.yml on tejred213/PyMotoGP

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page