FastF1 变更日志全解读:从 v3.8.0 到 v3.9.0 的 API 演进、弃用清理与数据可靠性修复
【免费下载链接】Fast-F1FastF1 is a python package for accessing and analyzing Formula 1 results, schedules, timing data and telemetry项目地址: https://gitcode.com/GitHub_Trending/fa/Fast-F1
FastF1 是用于访问和分析 F1 比赛结果、赛程、计时数据与遥测数据的 Python 包。本文基于仓库内 docs/changelog/current.rst(该文件被 docs/changelog/index.rst 通过.. include::引入发布说明页面)对 v3.8.0 至 v3.9.0 五个版本的全部变更逐条展开,并结合源码实现讲解其底层原理。读完本文,你将掌握这些版本对Session.results、异常体系、fastf1.utils与EventSchedule等核心 API 的具体影响,以及升级迁移时需要注意的每一项改动。
v3.9.0(开发中):API 清理与排位赛结果行为变更
v3.9.0 目前标记为(in development),尚未正式发布,但变更方向已经明确:一是彻底移除长期弃用的 API,二是修正Session.results在排位赛场景下的数据处理逻辑,三是开启新一轮弃用。
移除:fastf1.utils.delta_time
fastf1.utils.delta_time函数被正式移除。该函数自 v3.0.0 起即被弃用,原因是它会产生不准确的结果(对应 issue #884)。从当前源码搜索看,fastf1/utils.py与整个fastf1包中已不存在delta_time的定义,使用它会直接抛出AttributeError。如果代码中仍引用该函数,必须改用基于Laps与Telemetry对象计算的替代方案。
行为变更:Qualifying / Sprint Qualifying 的Session.results
这是 v3.9.0 最重要的功能性改动,涉及两类调整:
- 未通过 107% 规则的车手,其 Q1 最佳成绩现在会保留在结果中。旧行为下,这些车手的时间会被从结果中排除;新行为则完整保留 Q1 时间,为数据分析提供更完整的信息。
ClassifiedPosition列会被填充。Q1 即被淘汰的车手,该列取值被设为字符串"N"(表示 Not Classified);旧行为下该列完全不被使用。
该逻辑在源码中有两处体现。一处是 fastf1/core.py 中基于圈速计算排位赛结果的_calculate_qualifying_results:
# augment not classified based on 107% rule fastest_q1 = quali_results["Q1"].min() eliminated = quali_results["Q2"].isna() nc = (quali_results["Q1"] > (fastest_q1 * 1.07)) & eliminated c_pos = quali_results["Position"].astype(str) c_pos.loc[nc] = "N" quali_results["ClassifiedPosition"] = c_pos另一处在 fastf1/core.py 的结果加载路径中,当self.name == "Qualifying"时执行同样的 107% 规则判定(注释明确说明"old Ergast API does not provide this info")。变更日志指出,新行为部分源于 Jolpica-F1 API 返回数据的改变,同时使返回结果与文档中早已描述的行为保持一致,数据信息量也更丰富。
弃用:fastf1.utils三个解析辅助函数
fastf1.utils.recursive_dict_get、fastf1.utils.to_datetime、fastf1.utils.to_timedelta被标记为弃用(#884)。这三个函数"从未打算成为公共 API 的一部分",其实现在 v3.9.0 中被迁移至内部模块 fastf1/internals/parsing_helpers.py。
当前 fastf1/utils.py 的模块头注释明确写道:
The functions in this module were never intended to be part of the public API; see issue #884 for more details. They remain importable for backwards compatibility but emit a FutureWarning and forward to the implementations in fastf1._utils.
公共名称recursive_dict_get、to_timedelta、to_datetime继续可用,但它们现在只是转发到内部实现的薄包装(thin wrapper),每次调用都会通过warnings.warn(..., FutureWarning)发出弃用警告(源码中实际使用FutureWarning类别),并将在未来版本中移除。新的内部实现保留了原行为:to_timedelta支持24.3564、36:54、8:45:46等灵活的时间字符串格式(1 至 6 位小数精度),to_datetime支持2020-12-13T13:27:15.320000Z形式的日期字符串(可选尾随Z,可选毫秒/微秒精度),解析失败时返回None并记录 debug 日志。迁移建议:时间解析可改用pandas.to_timedelta/pandas.to_datetime,或直接使用内部模块fastf1.internals.parsing_helpers(注意内部模块不保证 API 稳定性)。
弃用:get_event_by_round的round关键字参数
EventSchedule.get_event_by_round的round关键字参数被弃用(#890),应在代码中改用round_number。源码 fastf1/events.py 的实现展示了完整的迁移约束:
- 传入
round=...时发出FutureWarning; - 若同时传入
round与round_number,直接抛出ValueError("Cannot pass both..."); - 不传任何参数时抛出
ValueError; round_number == 0时抛出ValueError("Cannot get testing event by round number!");- 找不到对应轮次时抛出
ValueError("Invalid round: ...")。
仓库中的测试 fastf1/tests/test_events.py 对上述行为做了完整覆盖,包括test_event_schedule_get_event_by_round_deprecated_kwarg验证旧参数仍可用但触发警告、同时传两个参数会报错等场景。
import fastf1 schedule = fastf1.get_event_schedule(2025) event = schedule.get_event_by_round(round_number=1) # 新用法 # 旧用法 schedule.get_event_by_round(round=1) 已弃用新特性:练习赛结果现在包含Time与Position
SessionResults现在为练习赛('Practice 1'、'Practice 2'、'Practice 3')提供Time(最佳圈速)和Position两列。此前这两列在练习赛中恒为NaT/NaN。
底层实现是 fastf1/core.py 中的_calculate_practice_like_session_results:当结果数据缺失且满足self.name in self._PRACTICE_LIKE_SESSIONS(即 Practice 1/2/3,定义见 fastf1/core.py)时,该方法从已加载的圈速数据中按车手分组取LapTime最小值,重命名为Time并排序生成Position:
best_laps = ( self._laps.loc[ ~self._laps["LapTime"].isna() & ~self._laps["Deleted"] ] .groupby("DriverNumber") .agg({"LapTime": "min"}) .rename(columns={"LapTime": "Time"}) .sort_values(by="Time") .reset_index() ) best_laps["Position"] = (best_laps.index + 1).astype("float64")注意该方法依赖self.laps["Deleted"]的布尔信息,若未加载 race control messages,会输出警告"missing information about deleted laps"。获取练习赛结果的示例:
session = fastf1.get_session(2026, 1, 'FP1') session.load() print(session.results[['DriverNumber', 'Time', 'Position']])v3.8.3:三个数据可靠性修复
v3.8.3 于 2026 年 4 月 29 日发布,是当前最新的稳定版本,集中修复了三个数据质量问题:
- 修复未发车车手被错误生成不存在的首圈(#899):此前部分未参加发车的车手会被错误添加一个实际不存在的"第一圈",该问题在 2026 年中国大奖赛中被观察到。
- 修复开赛阶段轮胎数据延迟导致部分圈速缺少轮胎数据(#893):当源头轮胎数据在 session 开始时延迟到达时,部分圈次的轮胎数据会缺失,该问题在 2018 年阿塞拜疆大奖赛中被观察到。
- 修复支援赛(support race)车手数据意外污染 F1 车手数据(#908,由 @Casper-Guo 贡献):在少数边界场景下,来自支援赛的异常车手数据会混入 F1 车手列表与结果数据。
v3.8.2:撞车圈去重与空时间戳处理
v3.8.2 于 2026 年 3 月 29 日发布,包含两项修复:
- 修复撞车圈(crash lap)被重复添加(#852,由 @sheehanr 贡献):当圈速数据已加载、随后又单独加载遥测数据时,撞车圈会被重复计入。这一交互问题提醒用户注意
load()各数据块之间的加载顺序与缓存联动。 - 干净处理 Jolpica-F1 API 响应中的空时间戳(#868):面对 API 返回的空时间戳字段,FastF1 现在能优雅处理,不再产生解析异常。
v3.8.1:2026 季前测试支持
v3.8.1 于 2026 年 2 月 11 日发布,内容是为 2026 年季前测试(Pre-Season Testing)提供适配补丁,确保get_testing_event/get_testing_session相关能力在新赛季测试阶段可用。
v3.8.0:依赖升级、异常体系重构与绘图常量自动生成
v3.8.0 于 2026 年 2 月 10 日发布,是这五个版本中改动面最大的一个,涵盖依赖、异常体系、绘图与多项修复。
依赖变更:Python 3.9 支持终止,Pydantic 加入
- Python 最低版本从 3.9 提升至 3.10,3.9 不再受支持。
- 新增依赖 Pydantic,用于数据模型校验。
- 部分核心依赖的最低版本要求提升,与 requirements/minver.txt 记录一致:
| 依赖 | 最低版本(v3.8.0 起) |
|---|---|
| matplotlib | >= 3.8.0 |
| numpy | >= 1.26.0 |
| pandas | >= 2.1.1 |
| requests | >= 2.30.0 |
| scipy | >= 1.11.0 |
升级时请确保环境满足上述版本约束。
新子模块fastf1.exceptions:统一的公共异常入口
新的子模块 fastf1/exceptions.py 成为所有公共自定义异常的唯一入口,未来新增异常也会集中于此。该模块内部还通过模块头注释说明了 FastF1 的异常设计哲学:接口型代码直接抛出异常,数据处理型代码采用"尽可能优雅降级、把错误转成警告"的策略;而继承自FastF1CriticalError的异常(如RateLimitExceededError)属于不可恢复错误,必须穿透 catch-all 错误处理直接抛给用户。
该模块当前的公共异常类包括:
| 异常 | 继承关系 | 触发场景 |
|---|---|---|
DataNotLoadedError | Exception | 访问尚未加载的数据 |
ErgastError | Exception | Ergast API 错误基类 |
ErgastJsonError | ErgastError | 服务器响应无法解析 |
ErgastInvalidRequestError | ErgastError | 请求被服务器拒绝 |
NoLapDataError | Exception | API 请求成功但无可用数据 |
FuzzyMatchError | ValueError | 模糊匹配置信度不足 |
FastF1CriticalError | RuntimeError | 不可恢复内部错误基类 |
RateLimitExceededError | FastF1CriticalError | 任一 API 触发硬性速率限制 |
弃用:异常必须从fastf1.exceptions导入
v3.8.0 同步开启了三个异常导入路径的弃用:
- 从
fastf1.ergast.interface导入ErgastError、ErgastJsonError、ErgastInvalidRequestError已弃用; - 从
fastf1.core导入NoLapDataError、DataNotLoadedError、InvalidSessionError已弃用; - 从
fastf1顶层导入RateLimitExceededError已弃用。
其中InvalidSessionError情况特殊:它实际上已不再被使用(fastf1/exceptions.py 中标注"TODO: remove in v3.11"),目前通过模块级__getattr__返回内部_InvalidSessionError并发出弃用警告,计划在 v3.11 移除。
迁移示例:
# 旧写法(已弃用) from fastf1.core import DataNotLoadedError, NoLapDataError from fastf1.ergast.interface import ErgastError from fastf1 import RateLimitExceededError # 新写法:统一从 fastf1.exceptions 导入 from fastf1.exceptions import ( DataNotLoadedError, ErgastError, NoLapDataError, RateLimitExceededError, )Bug 修复
- 防止支援赛车手混入车手列表与结果数据(#836):此前在少数边界场景下,由于源头数据异常,支援赛车手会被错误包含进 F1 车手列表与结果数据。
RateLimitExceededError现在会被正确抛出(#748、#842):此前该异常在内部 catch-all 错误处理中被吞掉,导致用户无法感知速率限制;修复后它会以FastF1CriticalError的语义直接抛给调用方,便于用户实现重试或限流逻辑:
from fastf1.exceptions import RateLimitExceededError try: session.load() except RateLimitExceededError: print("触发了 API 硬性速率限制,请稍后重试。")新特性:速度陷阱值自动填充
当速度陷阱(speed trap)数据缺失时,FastF1 现在会尽可能自动填充(#834)。实现位于 fastf1/core.py:针对每个车手,对SpeedI1、SpeedI2、SpeedST使用前向填充(ffill),前提是该圈不是 FastF1 自动生成的圈、且未处于红旗(TrackStatus 不含 "5")状态下;终点线速度陷阱SpeedFL的填充额外要求该圈没有进站(PitInTime为空)。此修复针对的是 API 数据会跳过连续等价值的问题(#775)。
新特性:车队名称/颜色常量的自动生成与 2026 赛季常量
- 自动生成常量作为兜底方案(#848):
fastf1.plotting子模块完整功能依赖车队名称与颜色常量。现在当遇到车队(名称)变更、或需要绘制 FastF1 尚未内置常量的未来赛季数据时,FastF1 会基于 F1 API 返回的数据自动生成常量。注意:自动生成的车队名称常量可能不完美,且此时所有配色方案都会跟随 'official' 色系。 - 2026 赛季车队名称与颜色常量已初步加入:可在 fastf1/plotting/constants.json 中查看;后续赛季开始阶段可能会继续微调,以更好还原车队品牌形象并使调色板更易区分。
自动生成的触发与警告逻辑在 fastf1/plotting/_backend.py:
warnings.warn( f"No built-in team name/color constants for {year}. " f"Update FastF1 for official values. " f"Using auto-generated names/colors (may be inaccurate). " f"All color schemes will follow the 'official' scheme." )也就是说,绘图时如果收到 "No built-in team name/color constants" 警告,说明当前赛季常量尚未内置,正在使用 API 数据自动生成的近似值,升级 FastF1 或等待常量更新可消除该警告。
升级迁移要点速查
FastF1 的弃用策略在变更日志末尾有明确注释:弃用的 API 会在弃用两个次要版本后移除("Deprecated API is removed two minor releases after its deprecation.")。基于 v3.8.0 至 v3.9.0 的全部变更,升级到 v3.9.x 时建议按以下清单检查代码:
- 删除或替换
fastf1.utils.delta_time调用(v3.9.0 已移除,直接报错); - 将
schedule.get_event_by_round(round=N)改为get_event_by_round(round_number=N); - 不再依赖
fastf1.utils.recursive_dict_get / to_datetime / to_timedelta,改用pandas等价函数或内部模块(后者需自行承担 API 变动风险); - 将所有异常导入迁移到
fastf1.exceptions(ErgastError、ErgastJsonError、ErgastInvalidRequestError、NoLapDataError、DataNotLoadedError、RateLimitExceededError等); - 确认运行环境 Python >= 3.10,且 matplotlib / numpy / pandas / requests / scipy 满足 v3.8.0 的最低版本要求;
- 关注排位赛结果语义变化:107% 规则淘汰车手的 Q1 时间现在保留在
Session.results中,ClassifiedPosition会以"N"标记未分类车手; - 练习赛场景可直接使用
session.results[['Time', 'Position']]获取最佳圈速与排序。
以上变更的原始来源为 docs/changelog/current.rst,涉及的具体实现可进一步阅读 docs/api_reference/exceptions.rst、docs/api_reference/events.rst 与 docs/api_reference/utils.rst 等 API 参考文档,以及本文引用的各源码文件与 fastf1/tests 下的对应测试用例,以在升级前完成完整的兼容性验证。
【免费下载链接】Fast-F1FastF1 is a python package for accessing and analyzing Formula 1 results, schedules, timing data and telemetry项目地址: https://gitcode.com/GitHub_Trending/fa/Fast-F1
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考