一次落地一行 — BTS 航班准点记录 1987 至今

三十九年逐月一包,记的是实际飞了没有,不是计划要飞什么

来源 https://www.transtats.bts.gov/发布时间: 2026-10-30最后更新: 2026-10-30 阅读 4
一次落地一行 — BTS 航班准点记录 1987 至今

信息速览

数据集名称一次落地一行 — BTS 航班准点记录 1987 至今
数据类型1987 年起,235,618,745 行,109 字段,逐月一包,本地时间整数,联邦法定报送
规模不适用(航段级运营记录,一行是一次实际执行的起降,主体是航班而不是人;无乘客身份、无订单、无票价)
接入方式https://www.transtats.bts.gov/
AI 就绪度

美国交通统计局航空信息办公室维护的航空公司准点性能数据,是一份关于「有没有飞、晚没晚、为什么晚」的运营台账。它不是航班计划,而是一次次实际执行的残影:某天某架飞机从甲机场飞到乙机场,这一次算一行。官方表元数据给出的规模是 Reporting 表两亿三千五百万条记录量级、109 个字段,起始年 1987,2026 年 10 月的快照里最新可用月份是 2026 年 8 月。

它最容易被误读的地方,恰恰是它最常见的那几个列名。CRSDepTime 看着像时间,其实是四位整数;ArrDelay 看着像延误,提前到达时是负数;Cancelled 为 1 的那些行里,所有与飞行有关的列都是空的。三个误会叠在一起,正好足以把一张准点率算错。

这份条目写给三类人:要用它训练延误预测的人、要做机场与航司绩效比较的人、要把它和别的运输数据叠加的人。全篇只关心规模有多大、结构怎么组织、边界在哪里。

项目 数值 来源
官方全名 Airline On-Time Performance Data TranStats 数据库 EFD
主管机构 BTS,航空信息办公室(OAI) 数据档案页首段
报送主体 承担国内定期客运收入 0.5% 以上的持证航司 数据档案页 Coverage(2018 年由 1% 调为 0.5%)
报送依据 49 U.S.C. 41722,承运人法定上报 数据档案页 Overview
Reporting 表记录数 235,618,745 TableInfo FGJ
Marketing 表记录数 62,078,378 TableInfo FGK
字段数 Reporting 109 / Marketing 119 TableInfo FGJ、FGK
年份区间(元数据) 1987–2026 DatabaseInfo EFD
实际月份覆盖 1987-10 至 1987-12、1988-01 至 1989-12、2000-01 至 2026-08 PREZIP 目录清单
单月包体积区间 7.7 MB 至 34.4 MB PREZIP 目录清单
逐月包体积合计 8,296,327,412 字节(约 7.73 GiB) 347 个文件大小加总
更新频率 每月追加,2026-10 快照已到 8 月 TableInfo、DL_SelectFields 页头
访问门槛 无,PUBLIC 目录匿名可读,支持 Range HTTP 响应头
许可 官方页面未给 SPDX 已读页面全文

§1 速览:它是什么,也不是什么

§1.1 一句话定义

这份数据是以航段为单位、以计划日为行的国内航班执行台账。每一行说的是:某承运人在某个计划日,用某个航班号,指派某架飞机,从某个机场飞到另一个机场,计划几点走、实际几点走、实际几点到、有没有被取消、如果晚了这几十分钟该算在谁头上。

把这句话拆开,每一段都对应一个列,也对应一个坑。理解这份数据的全部难度,几乎都集中在「一行到底代表什么」这一个问题上。

§1.2 使用者最常带着的三种处境

第一种:做延误预测。 目标是给定起飞前的已知信息,预测这一班会不会晚。这类工作最需要的不是平均延误,而是延误的传导结构——前一班飞机晚了五十分钟,这一班就很可能跟着晚,LateAircraftDelay 这一列正是为此存在。它也在 §3 展开。

第二种:做机场或航司的准点排行榜。 这类需求最容易出错,因为「准点」在官方定义里是一个门槛判断(延误满十五分钟才算不准点),不是一个连续量。用平均值排序与用门槛排序会得到两份不同的榜单。

第三种:把航空数据与别的时空数据叠起来。 例如把机场延误和当地天气、和节假日客流、和地面交通叠在一起。此时最大的障碍不是延误的定义,而是时间字段不是标准时间戳——它是一个四位整数,不带时区,不能直接与任何外部时间序列对齐。

三种处境与它能给出的东西对照起来,是一张决策卡。

你的处境 它能给你 它给不了你,需要另找
训练延误预测模型 逐航段的计划与实际时间、延误五分解、飞机尾号串联的机队周转 天气实况、机场跑道构型、座位数、票价
机场与航司准点排行 官方口径的十五分钟门槛指示、取消与改航标记 加权后的旅客视角准点率(它没有旅客人数)
与外部数据时空对齐 机场代码与机场 ID、计划日、当地时间 标准 UTC 时间戳、经纬度坐标
还原某个旅客的行程 航段与时段的匹配线索 任何能定位到人的信息,它完全不含乘客维度
计算某航线一年航班量 实际执行与被取消的航段 从未执行也未取消的计划班次

最后一行值得单独说一句。这份数据里没有「计划班次」这个概念。一趟计划好但被取消的航班会留下记录,一趟因为班表调整而根本就没出现在计划里的航班不会留下任何东西。所以只要拿去数航班量,数出来的永远是「实际发生的次数」,而不是「计划安排的次数」。

§1.3 它和航班计划不是一回事

这是理解整份数据的第一道分水岭。

航班计划类产品(例如各航司的时刻表、以及第三方汇总的班期表)描述的是将要发生的事:某年某月某日起,某航班每天从甲飞乙,几点走几点到。它是有意为之的安排。

这份数据描述的是已经发生的事:那个安排后来到底怎么执行了。两者在字段上有一个直接的可观察差别:它没有「正点率」以外的契约性承诺,只有事后的事实。 计划表不会告诉你某航班那天晚了两小时,因为它不知道;这份数据里也不会告诉你某航班本该加开一班,因为加开的班次如果没有报送,它看不到。

由此推出一条实用推论:做「供给」分析(有多少运力、覆盖哪些航线)应当找计划类或运量类产品;做「执行」分析(准点、取消、改航、延误归因)才是这份数据的本行。 两个方向混用,结论会在不知不觉间偏离。

§1.4 不适合谁

不适合要旅客视角数字的人。 一份记录是一个航段,不管这个航段上坐了一百二十人还是十七人。计算「有多少旅客被延误」的加权准点率,这份数据做不到,它没有任何座位或登机人数字段。

不适合做国际航线分析的人。 覆盖面被限定在美国五十州、哥伦比亚特区、波多黎各与美属维尔京群岛之间的国内航段。飞往伦敦、东京、墨西哥城的航段不在其中,哪怕承运人是美国航司。

不适合做货运或包机分析的人。 报送义务针对定期客运,货运承运人与包机不在报送范围内。

不适合要连续三十九年序列的人。 目录里 1990 年至 1999 年整段为空,详见 §2.2。

不适合要机场经纬度的人。 表里有的是机场代码、机场 ID、城市市场 ID,没有坐标。想做地图叠加,得另外接一份机场位置表。

§1.5 贯穿本文的案例:2026 年 2 月 1 日的 DL725

为了让「一行是什么」与「当地时间整数意味着什么」落到具体数字上,全文用同一个航段贯穿。它在数据的 2026 年 2 月包里,识别键是:

FlightDate                    2026-02-01
Reporting_Airline             DL
Tail_Number                   N695DL
Flight_Number_Reporting_Airline  725
Origin / Dest                 SAN / ATL

这一行里,计划离场 CRSDepTime 是 1111,计划到场 CRSArrTime 是 1820,计划耗时 CRSElapsedTime 是 249 分钟。实际离场 DepTime 是 1107,比计划早四分钟;实际到场 ArrTime 是 1821,比计划晚一分钟。DepDelay 写作 -4.00,ArrDelay 写作 1.00。滑出 TaxiOut 二十一分钟,滑入 TaxiIn 十一分钟,AirTime 二百二十二分钟,两个机场之间 Distance 一千八百九十二英里。

选它有三个理由。第一,它的两个时间字段都是真实值,没有留空,可以完整走一遍「换算」这件事。第二,它是西海岸到东海岸的跨时区航段,正好暴露 hhmm 整数相减的陷阱。第三,它的同一架飞机当天还飞了另一段 ATL 到 SAN 的 DL725,Tail_Number 相同、Flight_Number 也相同,把「航班号会复用」这件事故意摆在明面上。

后面 §3 会用它讲准点口径,§4 会用它算一遍跨时区的时间差,§5 会用它的同日姊妹航段说明机尾号串联的作用。下文凡提「§1.5 的案例」,指的都是这一个航段。

§2 规模与结构:两千三百万级月度增量怎么累成一张表

§2.1 两个官方数字与它们的口径

官方表元数据里有三个数字值得记住:Reporting 表的记录数(见信息速览,此处不重复)、109 个字段;Marketing 表 62,078,378 条记录、119 个字段;年份区间 1987 至 2026。

第一个数字是三十九年逐月累积的结果。它是行数,不是航班数、不是航线数、不是机场数。想换算成别的东西,必须先明确单位。

第二个数字容易被忽略,但它决定了分析时的主体。Marketing 表从 2018 年 1 月开始,按营销承运人组织:一张在 A 航司网站买到的票,可能实际由 B 航司的飞机执行,代码共享把这两家绑在一起。Marketing 表把这种「用谁的名义卖」的网络也记下来,所以同一段实际飞行,在两张表里会以不同主体出现。

第三个数最容易误读。1987 到 2026 是年份区间,不是覆盖区间。区别在 §2.2。

§2.2 逐月一包,以及中间空着的十年

交付形态是极简的:每个月一个压缩包,放同一个目录下,目录匿名可读。压缩包里固定两个文件,一个是当月全部航段的 CSV,一个是说明字段顺序与含义的 readme.html。

按 2026 年 10 月 29 日抓取的目录清单逐一点数,Reporting 系列的月包共 347 个。数出来的分布是这样的:

年份 月包数 当年压缩体积约
1987 3(10、11、12 月) 24 MiB
1988 12 94 MiB
1989 12 95 MiB
1990–1999 0 无
2000 12 168 MiB
2005 12 315 MiB
2010 12 295 MiB
2015 12 276 MiB
2019 12 364 MiB
2024 12 338 MiB
2026 8(1 至 8 月) 232 MiB

把 347 个文件的大小相加,合计 8,296,327,412 字节,约 7.73 GiB 压缩态。而官方年份筛选器从 1987 一路列到 2026,四十年一格不缺。

这两件事同时为真,正是这份数据最容易骗人的地方。筛选器列的是你能选的年份,目录里才有你能拿到的文件。 1999 年那一格可以点,点下去拿到的是一张空表。任何以「1987 年至今」为前提写下的连续时间序列假设,都必须先对着目录核一遍月份。

空着的这十年怎么处理,取决于问题。若研究的是长期趋势,这段空白本身就是结论的一部分——它说明这份数据不是为长趋势设计的,任何跨越这段空白的拟合都要在方法里显式声明。若只需要近期,从 2000 年起取即可,二十六年、三百二十个月,字段也稳定。

§2.3 体积从哪来

单月包最小的是 1989 年 4 月的约 7.7 MB,最大的是 2024 年 7 月的约 34.4 MB,差四倍多。体积增长来源有三个,其中只有第一个是航线扩张。

一是承运人数量。 报送门槛从客运收入 1% 调到 0.5% 之后,符合报送条件的承运人数目变多,同样的航线上多出若干家区域承运人各自报一份。

二是逐站明细。 每个航段带有最多五个改航站的逐站记录,每站七个字段。这一组在 2003 年之后逐步加入,早期月份整列为空,晚期月份对绝大多数航段也是空的,但它占住了列位。

三是字段本身的分阶段扩张。 官方字段页把字段按组划分,并在组名前直接标明起始时间:「延误成因」组标着数据自 2003 年 6 月起,「起飞机场登机口返回信息」组标着数据自 2008 年 10 月起。这是官方给出的唯一明确的分期线索,比任何二手文档都可靠。

§2.4 一百零九个字段的五个组

字段按语义分成五组,理解分组比记住一百零九个名字更有用。

第一组,标识。 日期、承运人代号、承运人 DOT 编号、IATA 代号、尾号、航班号、起降机场的三套标识(三字码、机场 ID、机场序号 ID)、城市市场 ID、州名与州代码、世界区域代码。这一组决定了一行能不能被唯一定位,也决定了跨年比对时该用哪一列。官方在两处专门提醒:机场三字码会变更、也会被重新分配,跨年分析请用机场 ID;承运人代号也会被重复使用,跨年分析请用唯一的承运人代号,而不是 IATA 代号。

第二组,离场表现。 计划离场、实际离场、离场延误(带符号与截零两个版本)、十五分钟门槛指示、延误分组、计划离场时段、滑出、离轮。

第三组,到场表现。 计划到场、实际到场、到场延误(同样两个版本)、十五分钟门槛指示、延误分组、计划到场时段、触轮、滑入。

第四组,取消、改航与汇总。 取消指示与原因码、改航指示、计划耗时、实际耗时、空中时间、航班数、距离、距离分组,以及最多五站逐站改航明细。

第五组,延误成因。 承运人、气象、国家空域系统、安检、前序航班晚到,五个分钟数。

五组之中,只有第一组与第四组是「描述这次飞行是什么」,其余三组都是「描述这次飞行怎么样」。做任何建模,先想清楚要用哪一组做标签、哪一组做特征,比通读字段清单更有效。

§2.5 两表之间不是父子关系

Reporting 与 Marketing 两张表的关系常被误解为「同一批航段的两个视角」。更准确的说法是:它们按不同的记账主体切同一堆实际飞行。

一趟由区域承运人执飞、以干线航司名义售卖的航班,在 Reporting 表里挂在区域承运人名下,因为报送义务落到实际运行的承运人身上;在 Marketing 表里同时会以干线航司名义出现。所以两表的行数不能相加,也不能直接相减求差——它们的差值是代码共享结构的度量,不是数据缺失量。

Marketing 表从 2018 年 1 月起才有,之前的年份根本不存在这个视角。想分析 2017 年及以前的代码共享,这份数据给不了。

§3 准点口径:同一个事实在四个列里

§3.1 官方怎么定义「准点」

官方的定义很短:航班在计划到场时间之后不到十五分钟到达,即视为准点。 换算成数据里的列,就是 ArrDel15 等于 0。

这一定义有两个直接后果。第一,准点是一个门槛判断,不是连续量。晚十四分钟与晚一分钟在 ArrDel15 里同样是 0,晚十五分钟与晚三小时同样是 1。第二,门槛设在十五分钟,是因为这个尺度在航空业运营实践中被广泛用作「可感知延误」的分界。

把门槛判断当成连续量来平均,就是最常见的错法。一百个航段里有九十个晚十四分钟、十个准点,ArrDel15 口径的准点率是 10%,平均延误却是 12.6 分钟——两个数字都对,但描述的是完全不同的东西。排行榜、绩效合同、监管报送用的都是前者,所以排序必须用门槛口径。

§3.2 四个延误列,两套截断规则

同一个「实际减计划」,在数据里以四个列存在:

列名 单位 负值处理 适用
DepDelay 分钟(带小数) 保留,提前为负 需要保留提前量的场景
DepDelayMinutes 分钟 负值一律置零 累计延误时长
ArrDelay 分钟(带小数) 保留,提前为负 情况同离场
ArrDelayMinutes 分钟 负值一律置零 累计延误时长

官方字段说明写得很直白:DepDelayMinutes 是「计划与实际离场时间之差,提前离场置零」。也就是说,凡是名字里带 Minutes 的列,都已经替你做了截断。

选哪一列取决于你要回答的问题。算「总延误人时」用截断版,因为提前不能抵消晚点;算「平均偏差」用带符号版,因为它反映的是系统性偏差。用错一列,符号会反,结论会翻。

§1.5 的案例正好把这层差别摆在眼前:DepDelay 是 -4.00,DepDelayMinutes 是 0.00;ArrDelay 是 1.00,ArrDelayMinutes 是 1.00。同一个航段,四个列里的数不都相同,但它们都对。

§3.3 十五分钟门槛的两种算法

ArrDel15 是官方给的门槛指示,直接用即可。但在某些分析里需要自己按 ArrDelay 重算门槛,这时会出现一种系统性偏差。

官方的门槛定义在边界上有个微妙之处:延误「达十五分钟及以上」记为 1。也就是说 ArrDelay 恰为 15.0 时属于不准点。自己重算时如果写成「大于十五」,边界上的航段就会被判成准点。这类边界样本量不大,但在机场层面排名里,一次一两个航段的差别足以翻转名次。

稳妥做法有两条:要么直接用 ArrDel15,要么在重算时严格按「大于等于十五」实现并把 ArrDelay 为空的航段排除在外。 后者要额外注意:ArrDelay 为空不等于零延误,它意味着这个航段没有到场记录,通常是取消或改航。

§3.4 取消与改航怎么进分母

这是准点率口径里分歧最大的一处。三家机构可以基于同一份数据报出三个不同的准点率,原因往往就在这里。

口径甲:只算实际飞了的航段。 分母是有到场记录的航段,取消与改航全部排除。这是最简单的一种,也是容易把准点率算高的那一种——取消的航班往往是天气最坏或运力最紧的时候,把它们从分母里拿掉,等于把最难的那部分直接从样本里删除了。

口径乙:取消计入分母且算作不准点。 这是监管报送的方向,因为对旅客而言,取消比晚到更难接受。做法是把分母还原成「计划执行的全部航段」,分子只数准点到达的那些。

口径丙:取消单列,不进入准点率。 很多机场绩效报告采用这种,把准点率与取消率并列陈述,让两个数字各自说明一件事。

口径本身没有对错,但必须声明。同一批数据,口径甲与口径乙的差可以到几个百分点——在一个取消率 2% 的月份,两者必然不同,差异的量级取决于那个月的天气。

§3.5 改航是一个特殊状态

改航比取消更绕。官方在字段说明里专门写了三条:改航航段的 ActualElapsedTime 一律为空,ArrDelay 也一律为空;改航后最终降落到原计划目的地的,其实际耗时与实际延误记在 DivActualElapsedTime 与 DivArrDelay 两列里;改航到别处落地的,DivDistance 给出计划目的地与最终落地机场之间的距离,落到原计划目的地的则为零。

翻译成使用层面的规则:用 Diverted 为 1 的行去算准点率时,ArrDelay 一定是空的,把这个空当零算,等于把一次改航当成一次完美准点。 正确做法是按 DivReachedDest 分流——改航后仍到达原目的地且有到达延误的,用 DivArrDelay;改航后没有到达原目的地的,单独计数。

§3.6 延误成因五分解能回答什么,不能回答什么

CarrierDelay、WeatherDelay、NASDelay、SecurityDelay、LateAircraftDelay 五个分钟数,是这份数据里最独特的字段组。

先说约束。官方字段说明把这一组的起始时间标为 2003 年 6 月,之前没有。这一组只在到达延误达十五分钟以上的航段里有值,其余航段整组留空。五个数相加,等于该航段的到达延误分钟数。

再说用法。这五个数最常被用错的方式,是把它当成一份责任认定书。实际上它是承运人的自我归因:由报送方自己填写。承运人说这四十分钟是「国家空域系统」(NAS,指流量管制、跑道容量等由空管与机场侧造成的原因),那就是 NAS,数据里没有第二方来复核。

更微妙的是「前序航班晚到」这一项。它看起来是中性的传导结果,但在归因序列上,它几乎是所有晚点的起点之后的第一个中转站。同一个晚到,究竟是记在这一班头上(承运人原因)还是记在前一班头上(前序晚到),会直接改变五分解的结构,而不改变总延误。

因此这一组字段的正确用法是:回答「延误在系统里是怎么传导的」,而不是回答「谁该负责」。

§3.7 §1.5 的案例:一个准点到场、提前离场的航段

回到 §1.5 的案例。这个航段 1107 从 SAN 起飞,比计划的 1111 早四分钟;1821 到达 ATL,比计划的 1820 晚一分钟。DepDel15 为 0,ArrDel15 为 0,两边都算准点。它也不带任何延误成因——五分解整组为空,因为没达到十五分钟门槛。

看上去这是一个平淡的航段。它的价值在于指出一件事:准点在数据里是常态,而且「准点」这个词掩盖了大量的微观波动。 提前四分钟起飞、晚一分钟到达,中间空中时间比计划短了七分钟(计划耗时 249 分钟里含滑出滑入,实际 254 分钟;空中时间 222 分钟)。如果只看二元的准点标签,这次飞行与「晚十四分钟」的那一百次是不可区分的。

这也是所有以 ArrDel15 为标签的延误预测模型面临的第一道天花板:标签本身丢掉了绝大部分信息。

§4 时间与主键:hhmm 整数与六列组合

§4.1 时间字段是一个四位整数

CRSDepTime、DepTime、CRSArrTime、ArrTime、WheelsOff、WheelsOn,以及逐站改航明细里的轮触轮离时间,全部采用同一种表示:四位整数,hhmm 形式,表示当地时间。

几个具体形态:0830 表示上午八点三十;2359 表示当日二十三点五十九;0005 表示零点零五。9 点整写作 0900 而不是 900——前导零在这里有意义,因为解析时要用整除与取模,不能当成十进制数。

四个后果:

第一,它不是日期时间。 没有日期、没有秒、没有时区。要还原成一个可比较的时刻,必须外接两张表:一张给日期,FlightDate 可以补;一张给时区,而时区这份数据里没有。

第二,跨零点需要自己处理。 一段在 2335 起飞、次日 0045 落地的航段,DepTime 是 2335,ArrTime 是 0045。直接相减得到负二十三小时五十分钟,取模二十四小时才得到正解。

第三,当地时间意味着每天各机场的偏移不同。 美国本土横跨四个时区,加上阿拉斯加、夏威夷、波多黎各,一共六个常用偏移,还有夏令时切换。表里没有任何关于这些的信息。

第四,早年的字段是空的。 目录里 1987 年 11 月的样例行显示,同一行里 DepTime 有值而 WheelsOff、WheelsOn、TaxiOut、TaxiIn 全空。这说明航段级时间在早期月份并不完整。

§4.2 用 §1.5 的案例算一遍

§1.5 的案例最适合演示这个陷阱。SAN 在太平洋时区,ATL 在东部时区,两者相差三小时。

按 hhmm 整数朴素相减:1821 减 1107,得到七小时十四分钟,也就是四百三十四分钟。而官方给的 CRSElapsedTime 是 249 分钟,ActualElapsedTime 是 254 分钟。

四百三十四与二百五十四,差了一百八十分钟,正好是三小时。这多出来的三个小时不是飞行时间,是飞机在追着太阳跑——从西向东飞,表上的钟被拨快了三小时,落地时刻看起来远晚于起飞时刻,而实际只飞了四个多小时。

反方向会更荒诞。同一架飞机当天反程 ATL 到 SAN 的 DL725,计划 0807 起飞、0923 落地,CRSElapsedTime 是 291 分钟。朴素相减 0923 减 0807 得到七十六分钟——从亚特兰大飞圣地亚哥只要一小时十六分钟,这显然不可能。差了两百一十五分钟,也就是倒欠三小时三十五分钟。

这两种荒谬同源:当地时间整数相减得到的是「钟面差」,不是「飞行时长」。 钟面差 = 飞行时长 − 时区偏移差,符号取决于飞行方向。

正确做法有三条,按可靠性排序:

  1. 直接用 CRSElapsedTime 与 ActualElapsedTime。 官方已经算好了门到门的分钟数,跨时区、跨零点都不必操心。这是首选。
  2. 用 AirTime 加 TaxiOut 与 TaxiIn 复原。 空中时间本身不含时区因素,三段相加约等于实际耗时,可以交叉校验。
  3. 外接时区表转成 UTC 再相减。 这需要一份机场到 IANA 时区名的映射,以及夏令时规则;工作量大,只有在不得不精确到「某个航段在 UTC 几点起飞」时才值得。

§4.3 官方口径下的时间含义

延误的定义建立在「实际减计划」之上,而这两个时刻都是同一机场的当地时间,所以延误本身不受时区影响。这是一个重要的便利:DepDelay 与 ArrDelay 可以放心使用,不需要任何时区处理。时区问题只出现在「你想自己从 DepTime 和 ArrTime 求耗时」的时候。

换句话说,这份数据的延误字段是时区安全的,耗时字段也是时区安全的,只有你自己相减出来的结果不安全。 记住这一条,就能避开这一类错误的大半。

§4.4 主键不是一个字段

没有任何单一字段能唯一定位一行。可用的兼容键至少需要六列:

FlightDate + Reporting_Airline + Tail_Number + Flight_Number_Reporting_Airline + Origin + Dest

逐列说明为什么每一列都必要:

FlightDate 区分不同的日子。同一个航班号每天都飞。

Reporting_Airline 区分承运人。不同承运人可以有相同的航班号,因为航班号在承运人内部编号。

Tail_Number 在最麻烦的那种情形下起决定作用:同一个承运人在同一天,同一个航班号,同一个起降机场对,出现了两行。这种情形在数据里确实存在,因为在部分航线(尤其枢纽机场的短途摆渡)上,一架飞机可能当天往返多次而沿用同一个航班号。加上尾号,两行就能分开。

Flight_Number_Reporting_Airline 区分同一天同一承运人的不同班次。

Origin 与 Dest 区分方向。同一架飞机飞去程和回程时,尾号相同、日期相同、航班号也可能相同,只有航向不同。

CRSDepTime 可以再加,但不是必需。 在上面的六列组合已经唯一的情况下,加不加它都行。官方字段页没有声明主键,六列组合是从数据行为反推的兼容键,不是官方保证的唯一性。

§4.5 航班号会被复用

航班号复用不是异常,是常态。同一号码在一天里可以既表示去程又表示回程;在不同日期可以是完全不同的航线;在季节班表调整时可以直接换一条完全不相干的航线。

以 2026 年 2 月的包为观察对象,同一承运人同一航班号出现两条以上不同起降机场对的组合相当常见,并不是个别现象。§1.5 的案例就是最简单的形态:DL725 在同一天既有 ATL 到 SAN 也有 SAN 到 ATL,尾号都是 N695DL,构成一个摆渡往返。

实践上的推论:看到航班号就想到「这是一班飞机」是错的,它只是承运人内部对某一对往返的编号。 想追踪「某一班」的连续性,最可靠的抓手是尾号加日期——同一架飞机同一天的航段链,才是真正连续的。

§4.6 用尾号串飞机的一天

尾号是这份数据里最有用也最容易被忽略的列。它把同一天同一架飞机的多个航段串成一条链,这条链正是延误传导的物理路径。

以 §1.5 的案例为例,尾号 N695DL 在 2026 年 2 月 1 日当天,先飞 ATL 到 SAN 的 DL725(计划 0807 起飞、0923 落地),再飞 SAN 到 ATL 的 DL725(计划 1111 起飞、1820 落地)。第二段的 LateAircraftDelay 为空,因为这一段没有延误;但如果第一段晚了一小时,第二段的延误归因里很可能就会出现「前序航班晚到」这一项。

需要留意的两个边界。第一,尾号在取消航段上常为空,因为没飞就没有指派记录,这条链会断在取消处。第二,尾号会在不同承运人之间流动——同一架飞机今天属于某区域承运人,下个月可能被调到另一家,Reporting_Airline 会变而 Tail_Number 不变。所以尾号串链时,必须同时限定承运人与日期。

§4.7 机场标识的三层结构

起飞机场与目的机场各有三套标识,三者用途不同:

三字码(Origin、Dest)最好读,但官方明确说它不唯一——机场码会变更,也会被重新分配。跨年比对时用它,会把两家不同机场当成一家。

机场 ID(OriginAirportID、DestAirportID)是 DOT 分配的稳定编号,官方建议跨年分析用它。

机场序号 ID(OriginAirportSeqID、DestAirportSeqID)把「机场在某一时点的属性版本」也编进去。同一个机场改名、改航站楼、改代码时,序号会变。要精确到「当时的这个机场」,用它。

另有一个城市市场 ID(OriginCityMarketID、DestCityMarketID),把服务同一城市的多个机场合并成一个市场。做城市层面的分析,例如「纽约」这个市场包含肯尼迪、拉瓜迪亚与纽瓦克三个机场,用这一列最省事。

三层标识对应三个分析颗粒度:航班层、机场层、城市层。选错层,结果会差出很多。

§5 取消与空值:把空白当零的代价

§5.1 一个取消航段长什么样

取消的航段在数据里仍然占一行。它有日期、承运人、航班号、起降机场、计划时间,但没有实际执行痕迹。

下面是一个真实形态的例子,取自 2026 年 2 月包,承运人 DL,航班号 1415,计划在 2026 年 2 月 19 日 0800 从 RIC 飞往 ATL:

CRSDepTime        0800
DepTime                       (空)
DepDelay                      (空)
TaxiOut                       (空)
ArrTime                       (空)
Cancelled         1.00
CancellationCode  A
CRSElapsedTime    122.00      (计划耗时仍有值)

CancellationCode 为 A,按官方查找表,A 是承运人原因,B 是气象,C 是国家空域系统,D 是安检。这一行的计划耗时仍然有值,因为那是计划,不需要实际执行就能知道。

§5.2 空值的三层来源

把空值全当成同一件事,是第二类错法的根源。空值至少来自三层:

第一层,取消。 Cancelled 为 1 的行,飞行过程字段整片为空。这是结构性空值,本身含有信息——它告诉你「这次没有发生」。

第二层,改航。 Diverted 为 1 的行,官方明确说 ActualElapsedTime 与 ArrDelay 一律为空,改航实际耗时与实际延误改记在 DivActualElapsedTime 与 DivArrDelay 两列。这也是结构性空值,且容易被漏读。

第三层,字段尚未纳入报送期。 2003 年 6 月之前的延误五分解、2008 年 10 月之前的登机口返回信息、早期月份的滑出滑入与轮触轮离,都是整列为空。这是历史性空值,不代表那天没延误,只代表那时候不报这个。

三层之间没有标记可区分——数据里只有一个空串。必须先按 Cancelled、Diverted 与月份过滤,才能知道剩下的空值意味着什么。

§5.3 把空当零会把准点率算高

这是最常见的错法,也最容易被忽视,因为它在代码里看起来完全正常。

情节是这样的:读入一个月的数据,对 ArrDelay 求均值,再按门槛算准点率。ArrDelay 为空的行在多数语言里会被转成 NaN 或零。转成零的那些行,既贡献了零延误,又进入了分母——结果是准点率被抬高、平均延误被压低。

在一个取消率 2% 的月份里,这种偏差大约是两个百分点,够让一个机场从榜首掉出前十。在天气极端的月份,取消率可以到更高,偏差会成倍放大。

正确的过滤链是这样的:

import pandas as pd

df = pd.read_csv("On_Time_Reporting_Carrier_On_Time_Performance_(1987_present)_2026_2.csv",
                 dtype={"CRSDepTime": str, "DepTime": str, "CRSArrTime": str, "ArrTime": str,
                        "Origin": str, "Dest": str, "Tail_Number": str, "Reporting_Airline": str},
                 low_memory=False)

## Step 1: 拆成三堆:正常执行、取消、改航
done     = df[(df.Cancelled == 0) & (df.Diverted == 0)]
cancel   = df[df.Cancelled == 1]
diverted = df[df.Diverted == 1]

## Step 2: 门槛口径:分母是「计划执行的全部航段 = 全部行」,取消按不准点计
n_planned = len(df)
n_on_time = (done.ArrDel15 == 0).sum()
n_late    = (done.ArrDel15 == 1).sum()
on_time_rate = n_on_time / n_planned

## Step 3: 只算实际飞了的那个口径(与上面并列陈述,不要二选一)
on_time_rate_flown = n_on_time / len(done)

三个数字排在一起读:n_planned 是应飞航段数,n_on_time 是准点到达数,on_time_rate 是把取消算作不准点的口径。这套写法不依赖任何外部表,唯一需要人判断的是把哪一个当作对外报告的准点率。

§5.4 五分解里的空也不是零

延误成因五分解的空值有它自己的规则:未达十五分钟延误门槛的航段,整组留空。 不是零,是「不适用」。

这会带来一个隐蔽的偏差。若把五分解当零算平均,均值会被大量未延误航段稀释,得到的数字看上去极小,且没有多少解释力。正确做法是先筛出到达延误达十五分钟以上的航段,再在子集里看五分解的构成——那时五个数相加才接近到达延误分钟数,比例才有意义。

§1.5 的案例就是典型:它的五个延误成因列全空。不是因为它没有延误,而是因为它晚了一分钟,没到门槛。若把这行当零延误参与归因平均,就会把一次「准点」误记为一次「零成因的延误」。

§5.5 交叉校验的三个自洽关系

空值处理是否正确,可以用三个内部关系来自检:

关系一:五分解之和等于到达延误分钟数。 在到达延误达十五分钟的航段上,CarrierDelay + WeatherDelay + NASDelay + SecurityDelay + LateAircraftDelay 应当等于 ArrDelayMinutes(忽略舍入)。这一条对不上,说明五分解被污染了。

关系二:实际耗时约等于滑出加空中时间加滑入。 ActualElapsedTime ≈ TaxiOut + AirTime + TaxiIn。这一条对不上,说明有行处在取消或改航状态却没按状态过滤。

关系三:到达延误等于离场延误加途中变化。 一般情况下 ArrDelay ≈ DepDelay + (CRSElapsedTime − AirTime − TaxiOut − TaxiIn) + 途中损失。这条关系不精确,因为途中损失因天气与航线而异,但它的量级应当合理;若某个航段的到达延误与离场延误相差几小时,那通常是改航或备降。

用 §1.5 的案例验一遍关系二:二十一分钟加二百二十二分钟加十一分钟,等于二百五十四分钟,与实际耗时 254 分钟完全吻合。这一行是干净的。

§5.6 送达滞后与修订

官方在页面抬头直接写「最新可用数据:2026 年 8 月」。抓取当日是 2026 年 10 月 29 日,也就是两个月左右的滞后。

这个滞后意味着两件事。第一,最近两个月的数据拿不到,做实时监控这类需求,这份数据不适用。第二,已发布的月份仍可能被修订——官方在数据档案页里提到了 2018 年门槛从 1% 改到 0.5% 这类规则变更,也提到了历次承运人合并后的联合报送起点,这些都说明历史口径不是静止的。

可以对照的是发布日程页:该页在 2026 年 10 月 29 日更新的记录里,能看到后续月份的追加与既往月份的「updated」标注并存。这提示部分月份的确存在重发。做可复现研究,应当记录取数日,并在方法里声明「以某日快照为准」。

§5.7 DAIMS 视角下的定位

DAIMS 是常用的数据集成熟度框架,关注发现、访问、互操作、溯源与治理几个维度。按这些维度看这份数据:

维度 表现 依据
发现与识别 有表代码 FGJ/FGK 与数据库代码 EFD,无 DOI、无正式引用文献条目 TableInfo 与 DatabaseInfo 页
访问 PREZIP 目录匿名可读,支持 Range;也可用选字段页定制 目录页与 DL_SelectFields 页
互操作 CSV 加一份随包的 readme.html;列名稳定,新增组追加在末尾 包内结构;字段页的分期标注
溯源 承运人自报,制度可查;但无版本号、无修订日志、无校验和 数据档案页;响应头无 Content-MD5
治理 法定报送,门槛与联合报送规则公开;许可语未见 SPDX 数据档案页的合并与门槛说明
版本控制 无。靠月份与取数日定位,旧文件不保留历史版本 PREZIP 目录

在这几个维度上,它的成熟度是「制度可靠、工程朴素」。溯源与治理靠的是法规与机构公信力,不是元数据规范;版本控制几乎为零,这是它最大的结构性短板。

§5.8 承运人合并留下的断点

数据档案页列出的一串日期,是这份数据的另一个隐藏断点:

时间 变化
2006-01 US 与 HP 开始以 US 联合报送
2010-01 DL 与 NW 开始以 DL 联合报送
2012-01 UA 与 CO 开始以 UA 联合报送
2012-01 EV 与 XE 开始以 E* 联合报送
2013-08 9E 更名为 Endeavor
2014-04 MQ 更名为 Envoy
2015-01 WN 与 FL 开始以 WN 联合报送
2015-07 AA 与 US 开始以 AA 联合报送
2018-04 AS 与 VX 开始以 AS 联合报送

对做长期趋势的人,这些日期意味着承运人层级的序列在合并点会跳。合并前是两个代号两份数据,合并后是一个代号一份数据。若按承运人代号做时间序列,合并点会出现一次不真实的跃变,既可能向上也可能向下,取决于哪一家的历史份额更大。

官方给出的处理建议是使用唯一的承运人代号做跨年分析,并明确指出旧代号在合并后不再适用。对极端严谨的序列分析,还有一种做法:把合并双方的历史数据按其市场份额合并成一个虚拟主体,再与合并后的实体接续。这需要额外的份额数据,通常不值得。

§6 怎么拿到:从目录到一行数据

§6.1 三条路径

路径一,PREZIP 目录直接取。 这是最省事的方式:目录匿名可读,文件名规整,按月份一个压缩包,支持 Range 请求,可以只取一段而不必整包下载。适合批量取全量或按年取。

路径二,选字段下载页。 官方提供一个可视化页面,可以按年按月勾选需要的字段、按地理范围收紧,然后生成一份定制 CSV。适合只想要少数几列的场景——全字段全月份合计七点七 GiB,裁到十列可以省下大半。

路径三,概览查询页。 官方另有一个按机场、按航司、按时段查询统计的界面。适合快速看一眼某个机场某月的准点概况,不适合做批量分析。

§6.2 一个不加占位符的取数脚本

下面这段可以直接运行,取 2026 年 2 月的 Reporting 包并解出 CSV 名与字段数。它用 Range 只读压缩包的尾部与头部,不落整包,适合先探一眼结构。

import struct
import urllib.request
import zlib

URL = ("https://transtats.bts.gov/PREZIP/"
       "On_Time_Reporting_Carrier_On_Time_Performance_1987_present_2026_2.zip")


def fetch(a, b):
    req = urllib.request.Request(URL, headers={"Range": f"bytes={a}-{b}",
                                               "User-Agent": "Mozilla/5.0"})
    return urllib.request.urlopen(req, timeout=120).read()


## Step 1: 取文件总长
head = urllib.request.urlopen(
    urllib.request.Request(URL, method="HEAD", headers={"User-Agent": "Mozilla/5.0"}),
    timeout=60)
size = int(head.headers["Content-Length"])

## Step 2: 从尾部找到中央目录,列出包内文件
tail = fetch(size - 70000, size - 1)
eocd = tail[tail.rfind(b"PK\x05\x06"):]
n_entry, cd_size, cd_off = struct.unpack("<HII", eocd[10:20])
cd = fetch(cd_off, cd_off + cd_size - 1)
p = 0
while cd[p:p + 4] == b"PK\x01\x02":
    n_len, e_len, c_len = struct.unpack("<HHH", cd[p + 28:p + 34])
    name = cd[p + 46:p + 46 + n_len].decode()
    print("包内文件:", name)
    p += 46 + n_len + e_len + c_len

## Step 3: 解出 CSV 的第一段,看表头
p = 0
while cd[p:p + 4] == b"PK\x01\x02":
    n_len, e_len, c_len = struct.unpack("<HHH", cd[p + 28:p + 34])
    comp_size = struct.unpack("<I", cd[p + 20:p + 24])[0]
    local_off = struct.unpack("<I", cd[p + 42:p + 46])[0]
    name = cd[p + 46:p + 46 + n_len].decode()
    if name.endswith(".csv"):
        lh = fetch(local_off, local_off + 29)
        ln, le = struct.unpack("<HH", lh[26:30])
        start = local_off + 30 + ln + le
        raw = fetch(start, start + min(comp_size, 60000) - 1)
        text = zlib.decompressobj(-15).decompress(raw).decode("utf-8", "replace")
        header = text.split("\n")[0].split(",")
        print("字段数:", len(header))
        print("前六列:", header[:6])
        break
    p += 46 + n_len + e_len + c_len

这段的输出应当告诉你包内有两个文件(一个 CSV 与一个 readme.html)、字段数一百一十、前六列依次是年份、季度、月份、日、星期、航班日期(列名带双引号,读入时会连同引号一起保留,需要自行剥掉)。注意一百一十比官方表元数据的 109 多一个,因为 CSV 尾列会多一个空列——这是文件层面的形态差异,不影响列名。

§6.3 为什么值得先读 readme

每个压缩包里都有一份 readme.html,内容是字段顺序与逐条释义。它比任何二手文档都可靠,因为它是随包发布的。

尤其值得注意的是它对几个边界条件的表述。例如改航航段的实际耗时与实际延误一律为空这一条,只有在 readme 里才写得最明确;而官方字段页与表元数据页都没有用同样的字眼重复。

一个实用习惯:第一次打开某个年份的数据,先把当年的 readme.html 读一遍。 字段在不同年代有增减,readme 是最直接的分期说明书。

§6.4 服务端行为

几个服务端行为上的细节,写在这里省去重复试探:

主站与 PREZIP 目录返回 200,目录列表以纯文本 <pre> 形式给出,可以直接用文本解析拿到文件名、大小、修改时间。文件请求支持 Range,单字节区间返回 206 并带正确长度。响应头里有 Accept-Ranges: bytes 与 ETag,但没有 Content-MD5,无法直接校验完整性,只能靠解压是否成功来判断。

名称相同的另一个域名 www.bts.gov 与 data.bts.gov 在本环境下返回 403,无法作为替代入口。只有 transtats.bts.gov 这一条链路可用,这提示做长期抓取时应当把单点依赖纳入考虑。

§6.5 下载策略

全量七点七 GiB,单线程取完需要相当长的时间。几个可以省力的做法:

先列清单再决定取哪几年。 目录一次就能拿到全部 347 个文件名与大小,先按需要筛,不要一上来就拉全量。

按年取,不要按月取。 单年的包在 100 至 400 MiB 之间,按月取会产生三百多次请求。

用选字段页裁列。 全字段的 CSV 解压后单月可达两百多兆,裁到十列后体积可以降一个数量级。

验证一份代表作。 取到第一份后用 §6.2 的脚本核字段数与表头,确认解析链路正确,再批量。

§7 在生态中的位置:它旁边站着谁

§7.0 阅读本节前的一个框架校正

讨论「旁边站着谁」时,最常见的错位是把这份数据与「航班时刻表」「航班追踪」放在同一个格子里比较。它们看起来都在讲飞机,但回答的是三个方向不同的问题:

航班时刻表回答「以后要飞什么」。它是计划,是承诺,是有意为之的安排。

航班追踪回答「此刻飞到哪里」。它是位置流,是秒级更新,是对正在空中的飞机的观测。

这份数据回答「后来到底怎么样」。它是台账,是事后汇总,是按月交付的复盘。

三者之中只有这份是回溯性、批量、带归因的。它不预测、不实时、不提供轨迹。把它当其中任何一个用,都会失望。校正完这一点,再来看它的邻居。

§7.1 与气象类数据的配合

§1.5 的案例提示了一条最自然的叠加路径:把航段与机场当地的天气对起来。

这份数据里没有天气。它有的是延误成因五分解里的一项「气象」。那一项是承运人填的,且只在延误达十五分钟时出现,颗粒度是分钟数而非天气要素。想真正分析天气与延误的关系,必须外接一份逐时气象观测,按机场与日期对齐。

对齐的难点不在数据,在时间基准:航段时间是带 hhmm 的当地时间且不带时区,气象观测通常是 UTC 或站点当地时间。做这种叠加,必须先建一张机场到时区的映射表,把航段的时间统一到与气象源相同的基准上。§4.2 讲的三条路径在这一步少一条都不行。

§7.2 与机场与航线基础表的配合

这份数据没有经纬度、没有跑道信息、没有机场吞吐量。做地理分析必须外接一张机场表。

好在它内置了足够多的连接键:机场三字码、机场 ID、机场序号 ID、城市市场 ID、州代码、州 FIPS、世界区域代码。六把钥匙,几乎对得上任何一份外部机场表。选哪一把取决于对方的颗粒度:对方用三字码就用三字码,对方用 IATA 编号就用 IATA 编号,对方用城市就用城市市场 ID。

一个提醒:三字码是六把钥匙里最弱的一把。 官方专门写了它会变更、会被重新分配。用它连接外部表,在跨年分析时会出现「同一个码接上了两家机场」的情形。

§7.3 与运量与财务类产品的分工

这份数据是「执行」视角。同一机构还发布运量与运力统计(航段数、座位数、客英里)、以及财务类的季度报表。三者互补:

  • 这份回答准点、取消、改航、延误归因。
  • 运量类回答运力投放与载运率。
  • 财务类回答收入与成本。

把准点率与票价放在一张表里做相关性,需要把这份数据与运量财务按承运人加季度对齐。对齐点是承运人的唯一代号,不是 IATA 代号,也不是中文公司名。

§7.4 与地面交通和出行数据的间接关系

把它与地面交通叠加,是一个常见但容易过头的需求。两者的颗粒度差得很远:航段以机场对为单位,地面交通多以路段或站点为单位。能对得上的层级通常只有「城市」——用城市市场 ID 把机场映射到城市,再用城市作为地面数据的连接键。

这种对齐会丢掉机场层面的差异(同一个城市的三家机场差异可以很大),也会引入时间上的错配(航段是离散事件,地面流量是连续量)。做这类分析的可靠做法是把时间聚合到小时或日,再比较趋势,而不是逐航段匹配。

§7.5 它不可替代的那一部分

上面几条讲的都是「配合」。这份数据真正不可替代的地方只有一处:它是唯一把「延误归因」以航段为粒度公开发布的美国数据源。

别的数据源会告诉你延误了多久,却不会告诉你这四十分钟里有多少是前序航班晚到造成的。这个信息之所以只能从这里拿到,是因为它来自报送制度本身——49 U.S.C. 41722 把报送义务课给承运人,承运人必须自报成因。这是制度设计出来的数据,不是观测或推断出来的。

§8 长期可用性:它为什么能一直更新

§8.1 供给方不是版权方

这份数据能持续三十九年、每月一包,根本原因不在技术,在制度。

它不是一个机构的商业产品,而是联邦法规课以承运人的报送义务的产物。承运人必须报,BTS 必须汇总公开。数据量、字段扩张、交付节奏,都由这套制度决定,而不是由市场或产品团队决定。

这解释了几个看起来反常的现象:为什么它没有版本号、为什么没有 DOI、为什么没有 SPDX 许可标识、为什么目录是朴素的纯文本列表。这些在商业产品里都是缺陷,在法定统计产品里都是常态。

§8.2 门槛变化会改变样本

样本不是固定的。报送门槛在 2018 年从「客运收入 1%」调整为「0.5%」。门槛下调,符合条件的小承运人进入样本,同一时点的航段数会因此增加。

这意味着跨 2018 年的承运人数量对比不是纯粹的市场变化,其中有一部分是制度变化。做这类对比,要么把分析窗口限在门槛稳定的一段内,要么显式声明门槛变化的影响。

同一页还记录了另一条:2018 年之前,符合条件的是「至少 1%」;官方在页面里用了过去时描述这一条。这类制度变更没有单独的修订日志,只能从页面的措辞里读出来。

§8.3 承运人数量与结构在变

承运人不是常量。三十九年里,有合并(US 并 HP、DL 并 NW、UA 并 CO、AA 并 US、WN 并 FL、AS 并 VX),有更名(9E 改 Endeavor、MQ 改 Envoy),有代号停用与重启。

对使用者,这意味着**「承运人」这个维度不能跨年直接比较**。一个看起来稳定的代号,在合并点前后的含义完全不同。§5.8 那张表就是为此准备的最小核对清单。

§8.4 字段在长,但没有断代

从字段页的分期标注看,字段是增量式扩张的:新增的组放在末尾,旧组的顺序不变。这种做法的好处是向后兼容——按列位置写死的旧脚本,读新文件时只会多出空列,不会错位。

代价是每一行携带大量空值。晚期月份的改航逐站明细绝大多数为空,延误五分解在未延误航段上为空。这不是数据质量问题,是「宽表兼容」的必然结果。

做长期分析时的稳妥做法是按照列名而不是列序号读取,并在读入后显式检查目标列在目标年份是否整列为空——若整列为空,说明那年还没纳入报送,而不是那年没有延误。

§8.5 对使用项目的几条建议

锁快照。 每月追加意味着同一份脚本在不同月份跑会得到不同结果。做可复现研究,应当记录取数日与当年的文件大小,作为快照指纹。

别指望补历史空白。 1990 至 1999 年没有文件,这不是暂时缺档。项目论证若依赖那十年,需要另找来源。

预留字段画像。 每次拿到新年份,先做一次空值率画像:哪些列整列为空、哪些列的空值率异常高。历史性空值与结构性空值的区别,只能从画像看出来。

对承运人与机场用稳定 ID。 承运人用唯一的承运人代号,机场用机场 ID。这两条是官方明确给出的建议,也是跨年分析里最容易省掉却代价最大的一步。

§8.6 它为何能持续更新

回到本节开头的问题。它之所以能持续,是因为它不是一个「产品」,而是一条法定的数据管道。承运人有报送义务,机构有汇总与公开职责,使用者的需求由监管、研究、媒体、公众的持续关注支撑。

这种结构下的数据,生命周期通常比商业数据长得多,也更稳定;代价是它的形态不会为了使用者而优化。没有 DOI,没有版本,没有规范的 API,没有数据字典的可机器读形式。它只保证一件事:每个月一包,放在那儿,谁都可以拿。

§9 常见误读:八条要提前避开的说法

常见说法 实际 证据
这是航班计划表,能查到某航线一年该飞多少班 它只记实际执行与被取消的航段,没有任何未执行也未取消的计划 数据档案页 Overview;§1.3
一行是一个航班 一行是一个航段,联程转机拆成多行,同一架飞机一天飞几段就有几行 readme.html 的字段顺序与官方 Description
CRSDepTime 是时间戳,可以直接与别的时序对齐 它是四位整数 hhmm,不带日期、不带秒、不带时区 官方字段说明「local time: hhmm」
用 ArrTime 减 DepTime 就是飞行时长 两列都是当地时间,跨时区航段会得到负数或超过二十四小时的值 §1.5 的案例:朴素相减 434 分钟,实际 254 分钟
取消的航段没有数据,把它们过滤掉就行 过滤会抬高准点率;取消必须按选定口径处理,通常计入分母 取消航段的 DepTime、ArrDelay 全空,见 §5.1
延误成因五分解是谁的责任认定 它是承运人自报的归因,只回答延误如何在系统里传导 数据档案页 Overview 与字段说明「Data starts 6/2003」
1987 至 2026 是连续三十九年 1990 至 1999 年目录里没有任何文件,1987 年只有三个月 PREZIP 目录清单,§2.2
准点率是连续量,可以用平均延误代替 官方准点是门槛判断,满十五分钟即不准点;平均值与门槛率会给出不同排序 数据档案页术语表「Arrival Delay」
Reporting 与 Marketing 两张表可以拼起来用 按不同主体记账,行数不可相加;Marketing 从 2018 年起才有 TableInfo FGJ 与 FGK;§2.5
航班号加日期就能唯一定位一行 同一天同一航班号可以有去程与回程两行,必须再加尾号与起降机场 §4.4、§4.5;§1.5 的案例

§10 一份最小的使用清单

要从头用起,按下面的顺序走一遍,基本不会出错。

第一步,定粒度和主体。 先回答「一行是什么」和「你要 Reporting 还是 Marketing」。前者决定所有聚合的分母,后者决定 2018 年前的代码共享分析做不了。

第二步,核月份。 打开 PREZIP 目录,把目标年份的月包数点一遍。1990 至 1999 年要预判为空,1987 年只有三个月。把这份清单存下来,作为后续所有分析的覆盖声明。

第三步,读当年的 readme。 每个包都带一份,字段顺序以它为准。特别确认改航、登机口返回、延误成因三组在目标年份是否已有值。

第四步,定时间口径。 延误字段直接用;耗时字段直接用 CRSElapsedTime 与 ActualElapsedTime;只有确需 UTC 时刻时才外接时区表。不要对 hhmm 字段做朴素相减。

第五步,拆三种状态。 按 Cancelled 与 Diverted 把行分成正常执行、取消、改航三堆。三堆各自处理,不要让空值流进分母。

第六步,明说准点口径。 是门槛口径还是均值口径,取消计不计入分母,改航按 DivReachedDest 如何分流。这三句话要写在结论旁边,不能只写在附录。

第七步,用内部关系自检。 五分解之和是否等于到达延误分钟数;实际耗时是否等于滑出加空中时间加滑入。两条都对得上,说明过滤链没出错。

第八步,锁快照。 记录取数日、文件大小、字段数。三个月后再跑,先比对这三项,再比对结果。

这八步不需要任何专门工具,也不需要下载全量。做完之后,这份数据里那些看起来矛盾的数字——为什么同一个航段有四个延误列、为什么一行有两套机场标识、为什么目录里少了一块——就不再是障碍。

编辑说明

核验时间 2026-10-29 至 2026-10-30。方法:逐个打开 transtats.bts.gov 的数据档案页、数据表元数据页(FGJ、FGK)、字段选择页与 PREZIP 目录,取原文;目录清单逐行解析,按前缀统计得 Reporting 系列 347 个文件、Marketing 系列 208 个文件,逐文件大小加总得 8,296,327,412 字节;对 1987_1、1999_1、2026_9 等月份直接请求确认 404,对 1987_11、2000_2、2026_2 取中央目录与首个数据段,解出表头并核对字段数为一百一十(含尾空列);从 1987_11 包内解出 readme.html 全文,据此确认改航航段 ActualElapsedTime 与 ArrDelay 一律为空;从字段页的 Lookup 链接取到取消原因码与延误分组的官方码表;流式读取 2026 年 2 月包的前 200,000 行作为结构样本,用于验证主键组合的唯一性、同日同航班号往返的存在,以及跨时区时朴素相减的偏差方向,样本仅用于结构验证,正文未做任何分布统计。

未覆盖:www.bts.gov 与 data.bts.gov 在本环境返回 403,许可表述只能依据 transtats.bts.gov 上已读到的页面,未读到独立的法务许可条款页,因此正文不写具体开放许可名称;readme.html 中的字段释义较简,部分列的精确边界(例如 Flights 列恒为 1 的确认)未逐行核实;Marketing 表的逐月包体积只统计未解包内容,未抽样其字段结构;两表之间代码共享的具体映射关系未实证,正文只写机制不写数字。

外部站点在本环境可正常访问,未引用任何未读到的原文。


相关数据集导航

以下为站内 AI-Ready 数据集百科中与本词条共享多个主题标签的相关数据集,按相关度降序排列:

  • ndbc-buoy — 共享标签:公共卫生与流行病学 / 时序数据
  • noaa-cors-gnss — 共享标签:公共卫生与流行病学 / 时序数据
  • nhanes — 共享标签:公共卫生与流行病学 / 调查数据
  • brfss — 共享标签:公共卫生与流行病学 / 调查数据
  • our-world-in-data — 共享标签:公共卫生与流行病学 / 时序数据
  • nhis — 共享标签:公共卫生与流行病学 / 调查数据
  • mics — 共享标签:公共卫生与流行病学 / 调查数据
  • dryad — 共享标签:公共卫生与流行病学 / 调查数据
  • chns — 共享标签:公共卫生与流行病学 / 调查数据
  • elsa — 共享标签:公共卫生与流行病学 / 调查数据

导航说明:本章节由全站统一标签体系自动计算生成(标签重合度算法),双向可达;点击链接可跳转至对应数据集词条。

返回 AI-Ready 数据集