如何按航班链自定义分组计算Mean Abs Error:先求和再取绝对值
自定义分组下的特殊MAE计算实现
需求说明
需要计算特殊Mean Absolute Error(MAE):先对分组内的fuel_prediction_dev求和,取绝对值后再除以组内有效行数,而非常规的先取绝对值再平均。例如Group1的正确计算方式为:abs(2600-2500-3100+2300)/4 = 175,而非常规的(2600+2500+3100+2300)/4 = 2625。
数据示例
现有预测结果DataFrame(注:原表中Group列不存在,为示例标注用):
| Group | flight_date_exact_utc | scheduled_arrival_date/time_utc | tail_number(机尾号) | ond_flown(执飞航线) | fuel_prediction_dev(燃油预测偏差) | fuel_prediction_abs_dev(燃油预测绝对偏差) |
|---|---|---|---|---|---|---|
| 1 | 08.06.2021 | 09.06.2021 08:30 | Aircraft 1 | JFK-YYZ | 2600 | 2600 |
| 1 | 09.06.2021 | 09.06.2021 13:35 | Aircraft 1 | YYZ-ORD | -2500 | 2500 |
| 1 | 09.06.2021 | 09.06.2021 19:10 | Aircraft 1 | ORD-YYZ | -3100 | 3100 |
| 1 | 09.06.2021 | 10.06.2021 08:30 | Aircraft 1 | YYZ-JFK | 2300 | 2300 |
| 2 | 12.06.2021 | 13.06.2021 08:30 | Aircraft 1 | JFK-YYZ | -1200 | 1200 |
| 2 | 13.06.2021 | 14.06.2021 08:30 | Aircraft 1 | YYZ-JFK | 500 | 500 |
| 3 | 20.07.2021 | 21.07.2021 08:30 | Aircraft 2 | JFK-YYZ | 500 | 500 |
| 3 | 21.07.2021 | 21.07.2021 13:30 | Aircraft 2 | YYZ-ORD | -2300 | 2300 |
| 3 | 21.07.2021 | 21.07.2021 18:55 | Aircraft 2 | ORD-YYZ | -3700 | 3700 |
| 3 | 21.07.2021 | 22.07.2021 08:30 | Aircraft 2 | YYZ-JFK | 500 | 500 |
| 4 | 23.07.2021 | 23.07.2021 18:55 | Aircraft 2 | JFK-MIA | 500 | 500 |
| 4 | 23.07.2021 | 24.07.2021 08:30 | Aircraft 2 | MIA-JFK | -700 | 700 |
| 5 | 20.07.2021 | 21.07.2021 08:30 | Aircraft 2 | JFK-YYZ | 500 | 500 |
| 5 | 24.07.2021 | 21.07.2021 13:30 | Aircraft 2 | YYZ-ORD | -2300 | 2300 |
| 5 | 24.07.2021 | 21.07.2021 18:55 | Aircraft 2 | ORD-YYZ | -3700 | 3700 |
| 5 | 该行缺失 | 但应为 | Aircraft 2 | YYZ-JFK | 无数据 | 无数据 |
问题难点
- 无现成分组列,无法直接调用
groupby() - 同一分组内航班日期可能不同,无法按日期分组
- 部分行存在数据缺失,需过滤无效数据
分组规则
- 分组以
ond_flown的出发地为JFK作为起始,以目的地为JFK作为结束(首尾行可能缺失) - 每组最多4行,通常行数更少
- 每组属于同一
tail_number(机尾号)的连续航班链
实现步骤(Python Pandas)
1. 数据预处理
转换日期时间格式、拆分航线为出发/到达地、按机尾号和到达时间排序以保证航班连续性:
import pandas as pd # 转换日期时间列为datetime类型(处理格式错误时设为NaN) df['scheduled_arrival_date/time_utc'] = pd.to_datetime( df['scheduled_arrival_date/time_utc'], format='%d.%m.%Y %H:%M', errors='coerce' ) df['flight_date_exact_utc'] = pd.to_datetime( df['flight_date_exact_utc'], format='%d.%m.%Y', errors='coerce' ) # 拆分航线为出发地(dep)和目的地(arr) df[['dep', 'arr']] = df['ond_flown'].str.split('-', expand=True) # 按机尾号和到达时间排序,确保航班链的连续性 df = df.sort_values( ['tail_number(机尾号)', 'scheduled_arrival_date/time_utc'] ).reset_index(drop=True)
2. 生成分组ID
同一机尾号下,当航班出发地为JFK时标记为新组起点,累加生成唯一分组ID:
# 标记新组起点:当前行出发地为JFK,且与上一行机尾号相同(或为该行机尾号的第一行) df['is_new_group'] = (df['dep'] == 'JFK') & \ (df['tail_number(机尾号)'] == df['tail_number(机尾号)'].shift(1)).ne(True) # 按机尾号累加生成分组ID df['group_id'] = df.groupby('tail_number(机尾号)')['is_new_group'].cumsum()
3. 计算自定义MAE
过滤缺失数据后,按机尾号+分组ID分组,求和取绝对值再除以组内有效行数:
# 过滤掉fuel_prediction_dev为空的无效行 valid_df = df.dropna(subset=['fuel_prediction_dev']) # 分组计算自定义MAE group_mae = valid_df.groupby(['tail_number(机尾号)', 'group_id']).agg( total_dev=('fuel_prediction_dev', 'sum'), # 组内偏差求和 valid_row_count=('fuel_prediction_dev', 'count') # 组内有效行数 ).assign( custom_mae=lambda x: abs(x['total_dev']) / x['valid_row_count'] ).reset_index() # 查看结果 print(group_mae[['tail_number(机尾号)', 'group_id', 'custom_mae']])
结果验证
运行后Group1的custom_mae将输出175,与需求示例一致;其他分组也会按规则计算出正确的特殊MAE。
内容的提问来源于stack exchange,提问作者Paul1911
相关产品推荐
相关产品推荐

