☰
地铁AFC客流量预测实战:从交易流水到时间序列的完整代码与调优指南
2026/9/28 22:36:45 网站建设 项目流程

简介:这份资源面向城市交通、智能交通方向的研究人员与数据分析学习者,围绕地铁站AFC客流量预测这一典型时间序列问题,提供从数据处理到多模型对比的完整代码与实验数据。压缩包共61个文件,约7.59MB,包含10个Python脚本、41张png图表、5个txt指标文件,以及xlsx、csv、html和pth模型权重等,覆盖数据预处理、特征工程、模型训练与结果可视化全流程。代码中实现了LSTM、RNN、GRU、DNN、SVM、KNN等多种预测模型,并配有统一的模型比较脚本,可输出误差分析、散点矩阵、雷达图与对比仪表盘等图表,便于直接用于论文或报告撰写。已有48人学习,适合希望快速复现客流量预测实验、对比不同模型性能或搭建自身预测方案的中高级读者参考。

1. 从闸机流水到客流曲线:这套 AFC 预测代码到底能干什么

早高峰的站厅里,闸机每刷一次卡就吐出一条交易记录,十几分钟里能堆出上万条。这些记录本身只是流水,真正值钱的是把它们变成未来 15 分钟、30 分钟、1 小时的进出站客流曲线——排班、限流、备品备件、能耗调度全指望这条曲线。地铁站 AFC 客流量预测(完整代码数据分享)这套资源,干的就是把原始 AFC 交易流水清洗成时间序列、再喂给预测模型输出未来客流这件事,附带完整代码和实测数据,拿到手就能跑通全流程。

它适合三类人:做轨道交通信息化、智慧车站的工程师,需要一套能落地的预测基线;做时序预测研究的学生,想要真实场景数据而不是玩具数据集;还有被临时派去做客流分析、手上只有一堆 CSV 的开发者。下面按「数据长什么样 → 怎么清洗建模 → 坑在哪 → 怎么调优」的顺序拆开讲,每一步都落到能抄的代码和参数上。

2. 拆开数据包:AFC 交易字段与时间粒度怎么定

拿到一份 AFC 数据,第一件事不是建模,是搞清楚每条记录代表什么。地铁 AFC 系统的原始交易一般包含卡号、交易类型(进站/出站)、交易时间、车站编号、设备编号、票卡类型等字段。预测任务真正用到的是「某车站在某个时间窗口内的进站人数和出站人数」,所以核心动作是把逐条交易聚合成等间隔的时间序列。

2.1 原始字段到建模字段的映射

常见 AFC 交易表结构大致如下,不同城市字段名会有差异,但语义基本一致:

原始字段含义建模用途
CARD_ID票卡编号去重、识别同一乘客
TXN_TYPE交易类型区分进站/出站
TXN_TIME交易时间戳时间切片依据
STATION_ID车站编号分组维度
DEVICE_ID闸机编号异常排查、设备粒度分析
CARD_TYPE票卡类型可选特征(通勤卡/单程票)

建模时通常只保留 STATION_ID、TXN_TIME、TXN_TYPE 三列做聚合,其余字段要么做特征,要么在清洗阶段丢弃。这里有个容易忽略的点:同一张卡短时间内可能有多条记录(比如进站刷卡失败重刷),直接计数会把客流虚高,需要按卡号和时间窗口去重。

2.2 时间粒度选择:15 分钟还是 1 小时

粒度选多大,取决于你要预测什么。做实时限流预警,15 分钟粒度才有意义;做全日客流预测或排班,1 小时粒度足够且噪声更小。这套代码默认按 15 分钟切片,因为地铁客流在早晚高峰的爬坡和回落往往发生在半小时内,1 小时粒度会把峰值抹平。

聚合逻辑用 pandas 的 resample 就能完成,关键是先把时间戳设为索引并排序:

import pandas as pd # 读取原始 AFC 交易流水 df = pd.read_csv("afc_raw.csv", parse_dates=["TXN_TIME"]) # 只保留进站记录做进站客流预测,出站同理改 TXN_TYPE df_in = df[df["TXN_TYPE"] == "进站"].copy() # 按卡号+时间窗口去重,避免重刷导致客流虚高 df_in = df_in.sort_values("TXN_TIME") df_in = df_in.drop_duplicates(subset=["CARD_ID", "STATION_ID"], keep="first") # 按车站分组,15 分钟粒度聚合计数 df_in = df_in.set_index("TXN_TIME") flow = (df_in.groupby("STATION_ID") .resample("15T")["CARD_ID"] .count() .reset_index() .rename(columns={"CARD_ID": "inflow"}))

这段代码的逻辑是:先筛进站、再去重、最后按车站和时间窗口计数。resample("15T")里的15T表示 15 分钟,改成30T或1H就能切换粒度。drop_duplicates的 subset 用卡号加车站,是因为同一张卡在不同车站的进站是两次独立出行,不能跨站去重。聚合后如果某些时间窗口没有记录,count 会得到 0,这本身是有效信息(深夜无客流),不要急着删。

注意:不同城市的 AFC 时间戳可能是字符串格式且带时区,parse_dates 解析失败时先检查原始格式,必要时用pd.to_datetime(df["TXN_TIME"], format="%Y%m%d%H%M%S")显式指定。

3. 特征工程与模型选型:把时间序列喂给谁

聚合出客流序列只是起点,直接拿原始数值丢进模型,效果通常一般。时间序列预测的胜负手在特征:历史滞后、滑动统计、时间周期编码,这三类特征决定了模型能不能抓住早晚高峰的规律。

3.1 滞后特征与滑动窗口特征

客流序列有强自相关性——当前 15 分钟的客流和上一个、上两个 15 分钟高度相关,也和昨天同一时刻、上周同一天同一时刻相关。把这些滞后项显式构造成特征列,模型才有抓手:

import numpy as np # 按车站分组后构造滞后与滑动特征 flow = flow.sort_values(["STATION_ID", "TXN_TIME"]) def build_features(g): g = g.copy() # 滞后特征:前1、2、4个时间窗口(15min/30min/1h前) for lag in [1, 2, 4]: g[f"lag_{lag}"] = g["inflow"].shift(lag) # 昨日同时刻、上周同时刻(96=24h/15min,672=7天/15min) g["lag_yesterday"] = g["inflow"].shift(96) g["lag_lastweek"] = g["inflow"].shift(672) # 滑动统计:过去1小时均值与标准差 g["roll_mean_4"] = g["inflow"].shift(1).rolling(4).mean() g["roll_std_4"] = g["inflow"].shift(1).rolling(4).std() return g flow = flow.groupby("STATION_ID", group_keys=False).apply(build_features) flow = flow.dropna().reset_index(drop=True)

逻辑说明:shift(1)表示取前一个时间点的值,shift(96)是 24 小时前(96 个 15 分钟),shift(672)是一周前。滑动窗口用shift(1).rolling(4)是为了避免数据泄漏——计算当前时刻特征时只能用当前时刻之前的数据,否则模型在训练集上表现虚高,上线就翻车。dropna会丢掉序列开头没有足够历史的行,这是正常代价。

3.2 时间周期编码与模型选择

时间本身要编码成模型能用的形式。小时、星期几这类周期变量,用 sin/cos 编码比直接给整数好,因为 23 点和 0 点在数值上应该接近而不是相差 23:

# 时间周期特征 flow["hour"] = flow["TXN_TIME"].dt.hour flow["minute"] = flow["TXN_TIME"].dt.minute flow["dow"] = flow["TXN_TIME"].dt.dayofweek # 一天内的位置(0~95),做周期编码 flow["slot"] = flow["hour"] * 4 + flow["minute"] // 15 flow["slot_sin"] = np.sin(2 * np.pi * flow["slot"] / 96) flow["slot_cos"] = np.cos(2 * np.pi * flow["slot"] / 96) flow["dow_sin"] = np.sin(2 * np.pi * flow["dow"] / 7) flow["dow_cos"] = np.cos(2 * np.pi * flow["dow"] / 7)

模型选型上,这套代码走的是「树模型基线 + 时序模型进阶」的路线。树模型(LightGBM、XGBoost)对特征工程友好、训练快、可解释,适合先跑出基线;时序模型(LSTM、GRU、Transformer)能自动学序列依赖,但需要更多数据和调参。常见做法是先用 LightGBM 把特征重要性跑出来,看看哪些滞后项真正有用,再决定要不要上深度模型。如果数据量只有几个月、车站数不多,树模型往往就够了,别一上来就堆 LSTM。

提示:划分训练集和测试集时,时间序列必须按时间顺序切,不能随机打乱。用前 80% 时间做训练、后 20% 做测试,否则未来信息泄漏进训练集,评估结果没有参考价值。

4. 避坑与排查:AFC 客流预测里最容易翻车的五件事

这套流程跑通不难,难的是跑出来的结果能信。下面五条是我在实际项目里踩过的坑,每条按现象、原因、解决写清楚。

现象一:模型在测试集上 MAPE 只有 5%,上线后误差翻三倍。原因多半是数据泄漏——滑动窗口特征用了当前时刻的值,或者随机打乱了时间顺序做交叉验证。解决:所有统计类特征统一加shift(1),划分数据集严格按时间切,交叉验证用 TimeSeriesSplit 而不是 KFold。

现象二:早晚高峰预测总是偏低,平峰偏高。原因是损失函数用 MSE,模型倾向于预测均值,峰值被平滑掉。解决:改用对峰值更敏感的损失,比如 LightGBM 的objective="regression_l1"(MAE)或 Huber 损失,也可以在训练时对高峰样本加权。

现象三:某些车站预测完全不准,其他车站正常。原因通常是该车站数据缺失严重,或者有大型活动、临时封站等异常事件没被剔除。解决:先按车站统计缺失率和异常值,缺失超过 30% 的车站单独处理或直接排除;异常日(客流突增突降超过 3 倍标准差)标记后从训练集剔除。

现象四:换一批新数据重新训练,特征列对不上报错。原因是 AFC 字段名或时间格式在不同批次间不一致。解决:在数据加载层做字段标准化映射,把各批次字段统一重命名后再进特征工程,别让原始字段名渗透到建模代码里。

现象五:预测值出现负数。树模型一般不会,但线性回归或某些神经网络会。原因是模型没有输出约束。解决:预测后做clip(lower=0),或者在模型层面用对数变换log1p训练、预测后再expm1还原。

5. 调优与验证:让预测曲线真正贴合高峰

基线跑通之后,真正拉开差距的是调优和验证方式。很多人模型训完看一眼 MAPE 就结束,但客流预测的评估要看峰值时段误差、要看不同车站的稳定性,还要看预测曲线和真实曲线的形状是否吻合。

5.1 分时段评估与峰值误差

整体 MAPE 会被大量平峰样本拉低,掩盖高峰时段的糟糕表现。评估时按早高峰(7:00-9:00)、晚高峰(17:00-19:00)、平峰分别算误差:

from sklearn.metrics import mean_absolute_percentage_error def eval_by_period(y_true, y_pred, slots): # slots 为对应的时间槽,用于区分高峰/平峰 peak_am = (slots >= 28) & (slots < 36) # 7:00-9:00 peak_pm = (slots >= 68) & (slots < 76) # 17:00-19:00 for name, mask in [("早高峰", peak_am), ("晚高峰", peak_pm), ("平峰", ~(peak_am | peak_pm))]: mape = mean_absolute_percentage_error(y_true[mask], y_pred[mask]) print(f"{name} MAPE: {mape:.4f}")

逻辑说明:slot 是 0~95 的一天内时间槽,28 对应 7:00,36 对应 9:00。分时段算 MAPE 能暴露「整体好看、高峰拉胯」的问题。如果高峰 MAPE 明显高于平峰,说明模型对峰值的学习不足,回到上一章加高峰样本权重或换损失函数。

5.2 关键参数与调优方向

以 LightGBM 为例,几个对客流预测影响最大的参数:

参数建议范围作用
num_leaves31~127控制树复杂度,太大易过拟合
learning_rate0.01~0.1学习率,配合 n_estimators 调
min_data_in_leaf20~100叶子最小样本,防止噪声拟合
feature_fraction0.7~0.9特征采样比例
objectiveregression_l1 / huber对峰值更敏感

调参顺序建议:先固定 learning_rate=0.05、num_leaves=63 跑基线,再用早停确定 n_estimators,最后微调 min_data_in_leaf 和 feature_fraction。别一上来就网格搜索所有参数,客流数据量不大,过拟合风险比欠拟合高。

验证方法上,除了时间序列切分,建议留出一个完整周做「滚动预测」验证:用前 N 天预测第 N+1 天,逐日滚动,看误差是否稳定。如果某几天误差突然变大,回去查那几天是不是节假日或异常事件。从那以后我每次做完客流预测,都强制走一遍分时段评估加滚动验证,再好看的总体指标也不直接信。希望这套代码和数据能帮你少走几个弯路,把预测曲线真正用到排班和限流决策里。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询