当前位置: 首页 > news >正文

【竞赛系列】机器学习实操项目07——全球城市计算AI挑战赛(baseline、时间序列分析、地铁流量预测)

上一章:【竞赛系列】机器学习实操项目06——客户信用评估模型进阶流程(特征重要性分析与稳定性监控)
下一章:
机器学习核心知识点目录:机器学习核心知识点目录
机器学习实战项目目录:【从 0 到 1 落地】机器学习实操项目目录:覆盖入门到进阶,大学生就业 / 竞赛必备

文章目录

    • 一、时间序列基础与数据探索
      • 1.1 导入时间序列分析工具库
      • 1.2 读取股票时间序列数据
      • 1.3 可视化股票价格时间序列
      • 1.4 读取手机游戏时间序列数据
      • 1.5 可视化每小时广告观看量
      • 1.6 可视化每日游戏货币支出
    • 二、时间序列评价指标与模式分析
      • 2.1 导入时间序列预测评价指标
      • 2.2 时间序列季节性分解(游戏货币支出)
      • 2.3 时间序列季节性分解(每小时广告观看量)
      • 2.4 时间序列的常见特性
        • 季节性
        • 趋势性
        • 周期性
        • 随机性
    • 三、全球城市计算AI挑战赛:地铁流量预测
      • 3.1 导入挑战赛所需工具库
      • 3.2 读取挑战赛数据
    • 四、地铁流量数据EDA(探索性数据分析)
      • 4.1 基础数据概览(数据结构与类型)
      • 4.2 缺失值检查
      • 4.3 时间特征工程(提取时间维度信息)
      • 4.4 单个站点的日内流量趋势(0号站点1日进站量)
      • 4.5 地铁线路连接关系分析(Metro_roadMap)
      • 4.6 站点连接度分析(识别换乘站与终点站)
      • 4.7 单站点多日流量趋势对比(0号站点)
      • 4.8 同类站点流量趋势验证(1号站点)
      • 4.9 高流量站点识别(误差敏感站点)
      • 4.10 高流量站点趋势分析(15号站点)
      • 4.11 同期流量对比(15号站点21日vs28日)
      • 4.12 高流量站点趋势验证(9号站点)
    • 五、规则建模(基于时间序列相似性的流量预测)
      • 5.1 计算上周10分钟级均值(21-25日)
      • 5.2 计算上周小时级均值(21-25日)
      • 5.3 计算10分钟段占小时的比例
      • 5.4 比例异常值压缩(凌晨时段)
      • 5.5 计算28日小时级均值(基准日流量)
      • 5.6 构建预测数据集(匹配测试集与特征)
      • 5.7 计算29日预测值(比例映射+衰减调整)
    • 六、总结与补充说明
      • 6.1 规则建模核心逻辑回顾
      • 6.2 模型优缺点与改进方向
      • 6.3 扩展工具与技术选型建议

本项目围绕“时间序列分析”核心技术,先通过股票价格、游戏行为等数据拆解时间序列的趋势性、季节性特征,再聚焦“全球城市计算AI挑战赛”的地铁流量预测任务,完成从数据探索到规则建模的全流程落地。整体工作以“时间序列相似性”为核心逻辑:先通过EDA验证地铁流量的工作日稳定模式(如早晚高峰时段固定、同期数据分布相似),再基于此设计规则模型——以上周(21-25日)10分钟级流量的小时占比提取稳定分布规律,以预测日前一天(28日)的小时级流量为基准,结合春运返程的流量衰减系数(0.95),最终实现29日10分钟级进站/出站人数的精准预测,兼顾模型的可解释性与工程落地效率。

通过网盘分享的文件:天池地铁流量预测
链接: https://pan.baidu.com/s/1k-sI1sDGufBSLJveNTm6nA?pwd=pgkk 提取码: pgkk

一、时间序列基础与数据探索

1.1 导入时间序列分析工具库

该步骤加载数据处理、可视化及时间序列分析所需的基础库,同时通过warnings.filterwarnings('ignore')屏蔽无关警告,确保代码运行输出简洁。

import warnings                                  
warnings.filterwarnings('ignore')  # 屏蔽运行过程中的警告信息,避免干扰输出import numpy as np                                # 用于数值计算(如数组操作、统计量计算)
import pandas as pd                               # 用于数据处理(如DataFrame构建、CSV读取)
import matplotlib.pyplot as plt                  # 用于数据可视化(如时间序列趋势图)
import seaborn as sns                             # 用于更美观的统计可视化# plt.rcParams['font.sans-serif']=['SimHei'] #用来正常显示中文标签(若需中文可视化可启用)
# plt.rcParams['axes.unicode_minus']=False #用来正常显示负号(若需中文可视化可启用)
# plt.rcParams['font.family'] = ['sans-serif'] #设置默认字体# from dateutil.relativedelta import relativedelta # 处理日期差(本案例暂未使用)
# from scipy.optimize import minimize              # 函数最小化(本案例暂未使用)# import statsmodels.formula.api as smf            # 统计学公式接口(本案例暂未使用)
# import statsmodels.tsa.api as smt                # 时间序列分析专用接口(本案例暂未使用)
# import statsmodels.api as sm                     # statsmodels核心库(后续季节性分解将使用)
# import scipy.stats as scs                        # 科学计算统计工具(本案例暂未使用)from itertools import product                    # 生成笛卡尔积(本案例暂未使用)
from tqdm import tqdm_notebook                   # 显示循环进度条(本案例暂未使用)%matplotlib inline  # 设置Jupyter Notebook中图像内嵌显示

1.2 读取股票时间序列数据

该步骤读取谷歌(Google)和微软(Microsoft)2006-2018年的股票数据,数据以“日期(Date)”为索引,后续将用于展示时间序列的趋势特征。

# 读取谷歌股票数据(2006-2018年),指定Date列为索引并解析为日期格式
google = pd.read_csv('./google/GOOGL_2006-01-01_to_2018-01-01.csv', index_col='Date', parse_dates=['Date'])# 读取微软股票数据(2006-2018年),指定Date列为索引并解析为日期格式
microsoft = pd.read_csv('./google/MSFT_2006-01-01_to_2018-01-01.csv', index_col='Date', parse_dates=['Date'])

1.3 可视化股票价格时间序列

该步骤绘制谷歌和微软股票的“最高价(High)”时间序列图,直观对比两家公司股票价格的长期趋势,体现时间序列的“趋势性”特征。

# plt.figure(figsize=(20, 10)) # 若需调整图像大小可启用(宽20,高10)
# 绘制谷歌股票最高价时间序列
google.High.plot()
# 绘制微软股票最高价时间序列
microsoft.High.plot()
# 设置y轴标签(股票价格)
plt.ylabel('share price',fontsize=14)
# 设置x轴标签(日期)
plt.xlabel('date',fontsize=14)
# 添加图例,区分两条曲线
plt.legend(['Google','Microsoft'],fontsize=12)
# 保存图像为SVG格式(矢量图,放大不失真)
plt.savefig("9_1.svg", format="svg")

在这里插入图片描述

1.4 读取手机游戏时间序列数据

该步骤读取手机游戏的“每小时广告观看量(ads)”和“每天游戏内货币支出(currency)”数据,用于后续时间序列模式(趋势、季节性)分析。

# 读取每小时广告观看数据,指定Time列为索引并解析为日期时间格式
ads = pd.read_csv('./data/ads.csv', index_col=['Time'], parse_dates=['Time'])
# 读取每天游戏内货币支出数据,指定Time列为索引并解析为日期时间格式
currency = pd.read_csv('./data/currency.csv', index_col=['Time'], parse_dates=['Time'])

1.5 可视化每小时广告观看量

该步骤绘制“每小时广告观看量(Ads)”时间序列图,展示高频时间序列(小时级)的波动特征,为后续季节性分解提供原始数据。

# 设置图像大小(宽12,高6)
plt.figure(figsize=(12, 6))
# 绘制每小时广告观看量时间序列
plt.plot(ads.Ads)
# plt.title('每小时广告观看') # 若需添加标题可启用
# plt.grid(True) # 若需显示网格线可启用
# 设置y轴标签(流量,即广告观看次数)
plt.ylabel('traffic',fontsize=14)
# 设置x轴标签(日期时间)
plt.xlabel('date',fontsize=14)
# 保存图像为SVG格式
plt.savefig("9_2.svg", format="svg")

在这里插入图片描述

1.6 可视化每日游戏货币支出

该步骤绘制“每日游戏内货币支出(GEMS_GEMS_SPENT)”时间序列图,展示低频时间序列(日级)的趋势特征,为后续季节性分解提供对比数据。

# 设置图像大小(宽15,高7)
plt.figure(figsize=(15, 7))
# 绘制每日游戏内货币支出时间序列
plt.plot(currency.GEMS_GEMS_SPENT)
# 添加标题(每日的游戏花费)
plt.title('每天的游戏花费')
# 显示网格线,便于读取数据
plt.grid(True)
# 显示图像
plt.show()

在这里插入图片描述

二、时间序列评价指标与模式分析

2.1 导入时间序列预测评价指标

该步骤导入回归任务常用的评价指标(如R²、MAE、MSE),并自定义“平均绝对百分比误差(MAPE)”,用于后续时间序列预测模型的性能评估。

# 从sklearn.metrics导入回归任务评价指标
from sklearn.metrics import r2_score, median_absolute_error, mean_absolute_error
from sklearn.metrics import median_absolute_error, mean_squared_error, mean_squared_log_error# 自定义平均绝对百分比误差(Mean Absolute Percentage Error, MAPE)
# 公式:MAPE = (1/n) * Σ|(实际值 - 预测值)/实际值| * 100(百分比形式)
def mean_absolute_percentage_error(y_true, y_pred): return np.mean(np.abs((y_true - y_pred) / y_true)) * 100

2.2 时间序列季节性分解(游戏货币支出)

该步骤使用statsmodelsseasonal_decompose函数,将“每日游戏内货币支出”序列分解为原始数据(observed)、趋势(trend)、季节性(seasonal)、残差(residual) 四部分,量化分析时间序列的核心模式。

import statsmodels.api as sm  # 导入statsmodels核心库(用于季节性分解)# 对每日游戏内货币支出序列进行季节性分解
# model='additive'表示使用加法模型(适用于季节性波动幅度稳定的序列)
res = sm.tsa.seasonal_decompose(currency.GEMS_GEMS_SPENT)
# 绘制分解结果(包含4个子图:原始数据、趋势、季节性、残差)
res.plot()
# 显示图像
plt.show()

在这里插入图片描述

  • 分解结果说明:
    • observed:原始时间序列数据
    • trend:序列的长期趋势(去除季节性和随机波动后的整体变化方向)
    • seasonal:序列的季节性波动(固定周期重复的模式)
    • residual:残差(去除趋势和季节性后的随机噪声)

2.3 时间序列季节性分解(每小时广告观看量)

该步骤对“每小时广告观看量”序列进行季节性分解,对比日级序列与小时级序列的分解差异,突出高频序列的季节性特征(如日内周期)。

# 对每小时广告观看量序列进行季节性分解(加法模型)
res = sm.tsa.seasonal_decompose(ads.Ads)
# 绘制分解结果
res.plot()
# 显示图像
plt.show()

在这里插入图片描述

2.4 时间序列的常见特性

季节性

以固定时间段重复的模式

例如,一个网站在周末可能会收到更多的访问; 这会产生季节性(周期)为7天的数据;

也可能是某个电商平台的每天销售数据,那么这样数据的季节性(周期)是 365.25天。

趋势性

指标的基本趋势

一个日益流行的网站应该显示出一个普遍的趋势;某个电商品类也有有自己的趋势。

周期性

当时间序列数据存在不固定频率的上升和下降时,表示该序列有 周期性 。这些波动经常由经济活动引起,并且与“商业周期”有关。周期波动通常至少持续两年。

季节性和周期性

这两个概念是完全不同的。当数据的波动是无规律时,表示序列存在周期性;如果波动的频率不变并且与固定长度的时间段有关,表示序列存在季节性。一般而言,周期的长度较长,并且周期的波动幅度也更大。

随机性

也称为“噪音”,“不规则”或“余数(remainder)”,这是季节和趋势序列删除后原始时间序列的残差(residuals);比如一场演唱会或者马拉松对地铁流量的影响,这是很难取预测的。

三、全球城市计算AI挑战赛:地铁流量预测

3.1 导入挑战赛所需工具库

该步骤加载地铁流量预测任务所需的库,包括数据处理(numpy/pandas)、文本处理(re/gensim)、特征工程(TF-IDF/SVD)、模型训练(XGBoost/LightGBM)及交叉验证工具,为后续数据处理和建模做准备。

## 数据工具包
import numpy as np
np.random.seed(42)  # 设置numpy随机种子,确保结果可复现
import pandas as pd
from tqdm import tqdm  # 显示循环进度条(后续数据处理将使用)## 字符串处理工具包(本案例暂未用于文本,可备用)
import string
import re  # 正则表达式
import gensim  # 文本语义建模(本案例暂未使用)
from collections import Counter  # 计数工具(本案例暂未使用)
import pickle  # 数据序列化存储(本案例暂未使用)
from nltk.corpus import stopwords  # 停用词库(本案例暂未使用)# 特征工程工具:TF-IDF文本特征(本案例暂未使用)、SVD降维、标准化
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.decomposition import TruncatedSVD 
from sklearn.preprocessing import StandardScaler
# 模型评估与验证工具:训练集划分、AUC指标、K折交叉验证
from sklearn.model_selection import train_test_split
from sklearn.metrics import roc_auc_score
from sklearn.model_selection import KFoldimport warnings
warnings.filterwarnings('ignore')  # 屏蔽警告# 梯度提升树模型:XGBoost和LightGBM(本案例规则建模暂未使用,可备用)
import xgboost as xgb
import lightgbm as lgb
from functools import partial  # 函数偏应用(本案例暂未使用)# 系统操作工具:文件路径、内存回收、稀疏矩阵合并、时间处理
import os 
import gc  # 垃圾回收(释放内存)
from scipy.sparse import vstack  # 垂直堆叠稀疏矩阵(本案例暂未使用)
import time
import datetime  # 日期时间处理(后续特征工程将使用)import joblib  # 模型保存与加载(本案例暂未使用)# 多进程处理工具(本案例暂未使用)
import multiprocessing as mp
import seaborn as sns  # 统计可视化
%matplotlib inline  # 图像内嵌显示

3.2 读取挑战赛数据

该步骤读取地铁流量训练数据(含进站/出站人数)和测试提交模板,其中训练数据包含“站点ID、时间段、进站人数、出站人数”,测试模板用于后续填充预测结果。

# 定义数据路径(根据实际文件位置调整)
path ='./input/' # 读取训练数据(df_data.csv):包含地铁各站点的10分钟级进站/出站人数
train = pd.read_csv(path + 'df_data.csv') # 读取测试提交模板(testA_submit_2019-01-29.csv):需填充2019-01-29日的预测结果
test_A_submit = pd.read_csv(path + 'testA_submit_2019-01-29.csv') 

四、地铁流量数据EDA(探索性数据分析)

4.1 基础数据概览(数据结构与类型)

该步骤通过head()shapeinfo()函数,查看训练数据的前5行、数据维度(行数×列数)及数据类型,快速了解数据基本特征。

# 查看训练数据的前5行(了解数据列名和格式)
train.head()

输出结果:

stationIDstartTimeendTimeinNumsoutNums
002019-01-01 00:00:002019-01-01 00:10:000.00.0
102019-01-01 00:10:002019-01-01 00:20:000.00.0
202019-01-01 00:20:002019-01-01 00:30:000.00.0
302019-01-01 00:30:002019-01-01 00:40:000.00.0
402019-01-01 00:40:002019-01-01 00:50:000.00.0
# 查看训练数据的维度(行数:314928条记录,列数:5列特征)
train.shape

输出结果:

(314928, 5)
# 查看训练数据的列名、非空值数量及数据类型
# 关键信息:stationID(站点ID,int64)、startTime/endTime(时间段,object)、inNums/outNums(进出站人数,float64)
train.info()

输出结果:

<class 'pandas.core.frame.DataFrame'>
RangeIndex: 314928 entries, 0 to 314927
Data columns (total 5 columns):#   Column     Non-Null Count   Dtype  
---  ------     --------------   -----  0   stationID  314928 non-null  int64  1   startTime  314928 non-null  object 2   endTime    314928 non-null  object 3   inNums     314928 non-null  float644   outNums    314928 non-null  float64
dtypes: float64(2), int64(1), object(2)
memory usage: 12.0+ MB

4.2 缺失值检查

该步骤通过isnull().sum()统计各列的缺失值数量,确认训练数据无缺失,无需进行缺失值填充。

# 统计每列的缺失值数量(结果均为0,说明无缺失值)
train.isnull().sum()

输出结果:

stationID    0
startTime    0
endTime      0
inNums       0
outNums      0
dtype: int64

4.3 时间特征工程(提取时间维度信息)

该步骤将startTime(开始时间)解析为日期时间格式,提取“日(day)、小时(hours_in_day)、星期几(day_of_week)、日内10分钟段(ten_minutes_in_day)”等特征,为后续时间序列分析和规则建模提供时间维度支持。

# 将startTime列解析为日期时间格式(便于提取时间特征)
train['time'] = pd.to_datetime(train['startTime'])# 提取“日”(1-31,对应月份中的日期)
train['day'] = train['time'].dt.day  # 提取“小时”(0-23,对应一天中的小时)
train['hours_in_day'] = train['time'].dt.hour # 提取“星期几”(0-6,0=星期一,6=星期日)
train['day_of_week'] = train['time'].dt.dayofweek # 提取“日内10分钟段”(0-143,一天24小时×6个10分钟段=144个段)
# 计算逻辑:小时×6 + 分钟//10(如00:00→0,00:10→1,...,23:50→143)
train['ten_minutes_in_day'] = train['hours_in_day'] * 6 + train['time'].dt.minute // 10 # 删除临时的time列(已提取所需时间特征,避免冗余)
del train['time']

4.4 单个站点的日内流量趋势(0号站点1日进站量)

该步骤绘制“0号站点1日(day=1)”的进站人数(inNums)时间序列图,直观展示单个站点在一天内的流量波动(如早晚高峰),为后续多站点趋势对比做铺垫。

# 筛选“0号站点(stationID==20)、1日(day==1)”的数据,按startTime排序
# 绘制进站人数(inNums)的时间序列图,展示日内流量变化
train[train.stationID==20][train.day==1].sort_values('startTime')['inNums'].plot()

在这里插入图片描述

4.5 地铁线路连接关系分析(Metro_roadMap)

该步骤读取地铁线路地图数据(Metro_roadMap.csv),该数据为邻接矩阵格式(行/列均为站点ID,值为1表示两站相邻,0表示不相邻),用于后续分析站点连接度(如换乘站、终点站识别)。

# 读取地铁线路地图数据(邻接矩阵格式)
Metro_roadMap = pd.read_csv(path + 'Metro_roadMap.csv')# 查看邻接矩阵的前5行(了解数据格式:行/列对应站点ID,值为0/1表示是否相邻)
Metro_roadMap.head()

输出结果:

Unnamed: 0012345678...71727374757677787980
00010000000...0000000000
11101000000...0000000000
22010100000...0000000000
33001010000...0000000000
44000101000...0000000000

5 rows × 82 columns

4.6 站点连接度分析(识别换乘站与终点站)

该步骤通过计算邻接矩阵每行的和(即每个站点的相邻站点数量,称为“连接度”),排序后识别换乘站(连接度高)和终点站/始发站(连接度低,通常为1),为后续分析不同类型站点的流量特征提供依据。

# 定义站点ID列表(0-80,共81个站点)
lines = [str(i) for i in range(81)] # 计算每个站点的连接度(每行求和:1的数量即相邻站点数),按连接度升序排序,查看前20个站点
# 关键发现:0、66、67、33、34、27等站点连接度为1(终点站/始发站);10、5、51等站点连接度高(换乘站)
Metro_roadMap[lines].sum(axis=1).sort_values()[:20]

输出结果:

0     1
66    1
67    1
33    1
34    1
27    1
49    2
60    2
59    2
58    2
57    2
56    2
55    2
54    2
53    2
61    2
48    2
47    2
45    2
44    2
dtype: int64

4.7 单站点多日流量趋势对比(0号站点)

该步骤选取“0号站点”,绘制1月21-25日(工作日)和28日(下周一)的进站人数(inNums)时间序列,观察多日流量分布的相似性——发现工作日流量模式高度一致,28日与21日(上周一同期)分布相近,为后续“基于同期数据的规则建模”提供依据。

import matplotlib.pyplot as plt# 设置图像大小(宽8,高8)
plt.figure(figsize=[8,8])
# 设置x轴标签(时间)
plt.xlabel('Time')
# 设置y轴标签(进站人数)
plt.ylabel('Count')# 遍历目标日期(21-25日为上周工作日,28日为下周一)
for day in [21,22,23,24,25,28]:# 筛选“0号站点、目标日期”的数据tmp = train.loc[(train.day == day) & (train.stationID == 0)].copy()# 按站点ID和开始时间排序(确保时间序列顺序正确)tmp = tmp.sort_values(['stationID','startTime'])# 绘制该日期的进站人数时间序列plt.plot(tmp['startTime'],tmp['inNums']) 

在这里插入图片描述

4.8 同类站点流量趋势验证(1号站点)

该步骤重复0号站点的分析逻辑,绘制1号站点21-25日、28日的进站人数趋势,验证“工作日流量模式一致、同期数据相似”的规律在其他站点同样成立,进一步支撑规则建模的合理性。

# 设置图像大小(宽8,高8)
plt.figure(figsize=[8,8])
# 设置x轴标签(时间)
plt.xlabel('Time')
# 设置y轴标签(进站人数)
plt.ylabel('Count')# 遍历目标日期
for day in [21,22,23,24,25,28]:# 筛选“1号站点、目标日期”的数据tmp = train.loc[(train.day == day) & (train.stationID == 1)].copy()# 按站点ID和开始时间排序tmp = tmp.sort_values(['stationID','startTime'])# 绘制进站人数时间序列plt.plot(tmp['startTime'],tmp['inNums'])

在这里插入图片描述

4.9 高流量站点识别(误差敏感站点)

该步骤统计21-25日、28日各站点的日均进站/出站总人数,按降序排序后识别高流量站点(如15号、9号、4号站点)。这类站点的流量预测误差对整体模型性能影响更大,需在后续建模中重点关注。

# 统计21-25日、28日各站点每日的进站总人数,按降序排序(查看前25条,识别高流量站点)
train.loc[train.day.isin([21,22,23,24,25,28])].groupby(['stationID','day'])['inNums'].sum().sort_values(ascending = False)[:25]

输出结果:

stationID  day
15         25     95389.022     87758.024     87414.023     87108.021     87069.028     79595.0
9          24     55736.025     55585.023     54981.028     53287.022     52837.021     52206.0
4          25     38730.021     38088.024     37990.023     37795.022     37398.028     35810.0
7          25     34810.028     33993.023     33548.022     33509.021     33412.024     33195.0
10         25     32852.0
Name: inNums, dtype: float64
# 统计21-25日、28日各站点每日的出站总人数,按降序排序(补充高流量站点识别)
train.loc[train.day.isin([21,22,23,24,25,28])].groupby(['stationID','day'])['outNums'].sum().sort_values(ascending = False)[:25]

输出结果:

stationID day
15 28 107870.0
25 107672.0
23 97706.0
24 96199.0
22 96153.0
21 94349.0
9 25 56068.0
24 55576.0
23 55170.0
28 53516.0
7 25 53201.0
28 53168.0
9 22 52478.0
21 52342.0
7 24 49400.0
23 47418.0
22 44601.0
21 43428.0
4 21 40142.0
24 39737.0
25 39597.0
23 39348.0
22 39285.0
28 36844.0
10 25 31788.0
Name: outNums, dtype: float64

4.10 高流量站点趋势分析(15号站点)

该步骤以15号站点(进站/出站流量最高)为例,分别绘制21-25日、28日的进站/出站人数趋势,观察高流量站点的时间分布特征——发现28日峰值略低于21日(可能受春运返程影响),但整体模式一致,为后续规则建模的“流量衰减系数”提供依据。

# 设置图像大小(宽8,高8)
plt.figure(figsize=[8,8])
# 设置x轴标签(时间)
plt.xlabel('Time')
# 设置y轴标签(进站人数)
plt.ylabel('Count')# 遍历目标日期,绘制15号站点进站人数趋势
for day in [21,22,23,24,25,28]:tmp = train.loc[(train.day == day) & (train.stationID == 15)].copy()tmp = tmp.sort_values(['stationID','startTime'])plt.plot(tmp['startTime'],tmp['inNums'])

在这里插入图片描述

# 设置图像大小(宽8,高8)
plt.figure(figsize=[8,8])
# 设置x轴标签(时间)
plt.ylabel('Count')# 遍历目标日期,绘制15号站点出站人数趋势
for day in [21,22,23,24,25,28]:tmp = train.loc[(train.day == day) & (train.stationID == 15)].copy()tmp = tmp.sort_values(['stationID','startTime'])plt.plot(tmp['startTime'],tmp['outNums'])

在这里插入图片描述

4.11 同期流量对比(15号站点21日vs28日)

该步骤将15号站点21日(上周周一)和28日(本周周一)的进站/出站人数按“小时”聚合,绘制对比图,直观展示同期流量的差异——28日进站量整体低于21日,出站量部分时段高于21日,但时间分布模式一致,为规则建模的“小时级比例映射”提供支撑。

# 筛选15号站点21日和28日的数据
tmp = train.loc[(train.day.isin([21,28])) & (train.stationID == 15)]
# 按“小时(hours_in_day)”和“日期(day)”聚合,计算每小时进站人数均值
# 绘制对比图(o-表示带圆点的折线)
tmp.pivot_table(index='hours_in_day',columns='day',values='inNums').plot(style='o-') 

在这里插入图片描述

# 筛选15号站点21日和28日的数据
tmp = train.loc[(train.day.isin([21,28])) & (train.stationID == 15)]
# 按“小时”和“日期”聚合,计算每小时出站人数均值
# 绘制对比图
tmp.pivot_table(index='hours_in_day',columns='day',values='outNums').plot(style='o-') 

在这里插入图片描述

4.12 高流量站点趋势验证(9号站点)

该步骤重复15号站点的分析逻辑,绘制9号站点(第二高流量)的多日进站/出站趋势及21日vs28日小时级对比,进一步验证“同期流量模式一致、存在小幅衰减”的规律,确保规则建模适用于多个高流量站点。

# 设置图像大小(宽8,高8)
plt.figure(figsize=[8,8])
plt.xlabel('Time')
plt.ylabel('Count')# 绘制9号站点21-25日、28日进站人数趋势
for day in [21,22,23,24,25,28]:tmp = train.loc[(train.day == day) & (train.stationID == 9)].copy()tmp = tmp.sort_values(['stationID','startTime'])plt.plot(tmp['startTime'],tmp['inNums'])

在这里插入图片描述

# 设置图像大小(宽8,高8)
plt.figure(figsize=[8,8])
plt.xlabel('Time')
plt.ylabel('Count')# 绘制9号站点21-25日、28日出站人数趋势
for day in [21,22,23,24,25,28]:tmp = train.loc[(train.day == day) & (train.stationID == 9)].copy()tmp = tmp.sort_values(['stationID','startTime'])plt.plot(tmp['startTime'],tmp['outNums'])

在这里插入图片描述

# 筛选9号站点21日和28日的数据,按小时聚合进站人数,绘制对比图
tmp = train.loc[(train.day.isin([21,28])) & (train.stationID == 9)]
tmp.pivot_table(index='hours_in_day',columns='day',values='inNums').plot(style='o-') 

在这里插入图片描述

# 筛选9号站点21日和28日的数据,按小时聚合出站人数,绘制对比图
tmp = train.loc[(train.day.isin([21,28])) & (train.stationID == 9)]
tmp.pivot_table(index='hours_in_day',columns='day',values='outNums').plot(style='o-') 

在这里插入图片描述

五、规则建模(基于时间序列相似性的流量预测)

5.1 计算上周10分钟级均值(21-25日)

该步骤以21-25日(上周工作日)为基准,按“站点ID、小时、日内10分钟段”聚合,计算每个10分钟段的平均进站/出站人数(in_mean/out_mean),消除单日随机波动,提取稳定的10分钟级流量模式。

# 筛选21-25日(上周工作日)的数据
t21_25 = train[(train['day']>=21)&(train['day']<=25)]# 按“站点ID、小时(hours_in_day)、日内10分钟段(ten_minutes_in_day)”聚合,计算进站人数均值
t21_25_in = t21_25.groupby(['stationID', 'hours_in_day', 'ten_minutes_in_day'])['inNums'].mean().reset_index()
# 按相同维度聚合,计算出站人数均值
t21_25_out = t21_25.groupby(['stationID', 'hours_in_day', 'ten_minutes_in_day'])['outNums'].mean().reset_index()# 重命名列名(明确为“均值”)
t21_25_in.columns = ['stationID', 'hours_in_day', 'ten_minutes_in_day', 'in_mean']
t21_25_out.columns = ['stationID', 'hours_in_day', 'ten_minutes_in_day', 'out_mean']# 合并进站和出站均值数据(按站点、小时、10分钟段对齐)
t21_25_in_out = t21_25_in.merge(t21_25_out,on = ['stationID', 'hours_in_day', 'ten_minutes_in_day'],how = 'left')# 查看合并后的数据前5行
t21_25_in_out.head()

输出结果:

stationIDhours_in_dayten_minutes_in_dayin_meanout_mean
00000.01.8
10010.00.8
20020.00.0
30030.40.0
40040.20.2

5.2 计算上周小时级均值(21-25日)

该步骤按“站点ID、小时”聚合21-25日数据,计算每个小时的平均进站/出站人数(in_mean_hour/out_mean_hour),用于后续计算“10分钟段占小时的比例”(小时级流量更稳定,可降低随机波动影响)。

# 按“站点ID、小时”聚合,计算21-25日每小时进站人数均值
t21_25_in_hour = t21_25.groupby(['stationID', 'hours_in_day'])['inNums'].mean().reset_index()
# 按相同维度聚合,计算每小时出站人数均值
t21_25_out_hour = t21_25.groupby(['stationID', 'hours_in_day'])['outNums'].mean().reset_index()# 重命名列名(明确为“小时均值”)
t21_25_in_hour.columns = ['stationID', 'hours_in_day', 'in_mean_hour']
t21_25_out_hour.columns = ['stationID', 'hours_in_day', 'out_mean_hour']# 合并小时级进站和出站均值数据
t21_25_in_out_hour = t21_25_in_hour.merge(t21_25_out_hour,on = ['stationID', 'hours_in_day'],how = 'left')# 查看合并后的数据前5行
t21_25_in_out_hour.head()

输出结果:

stationIDhours_in_dayin_mean_hourout_mean_hour
0000.1000000.466667
1010.0000000.000000
2020.0000000.000000
3030.0000000.000000
4040.0333330.033333

5.3 计算10分钟段占小时的比例

该步骤将10分钟级均值(in_mean/out_mean)除以对应小时的均值(in_mean_hour/out_mean_hour),得到“10分钟段占小时的比例”(in_ratio/out_ratio)。该比例反映了小时内流量的时间分布模式,可用于后续将28日(本周一)的小时级流量映射到10分钟级。

# 合并10分钟级均值和小时级均值数据(对齐站点、小时维度)
t21_25_in_out = t21_25_in_out.merge(t21_25_in_out_hour,on = ['stationID', 'hours_in_day'],how = 'left')# 计算进站10分钟段占小时的比例(+0.001避免除零错误)
t21_25_in_out['in_ratio'] = t21_25_in_out['in_mean'] / (t21_25_in_out['in_mean_hour']+0.001)
# 计算出站10分钟段占小时的比例
t21_25_in_out['out_ratio'] = t21_25_in_out['out_mean'] / (t21_25_in_out['out_mean_hour']+0.001)# 提取核心列(站点ID、小时、10分钟段、进站比例、出站比例),用于后续建模
t21_25_in_out_ratio = t21_25_in_out[['stationID', 'hours_in_day', 'ten_minutes_in_day','in_ratio','out_ratio']]# 查看比例数据
t21_25_in_out_ratio

输出结果:

stationIDhours_in_dayten_minutes_in_dayin_ratioout_ratio
00000.0000003.848895
10010.0000001.710620
20020.0000000.000000
30033.9603960.000000
40041.9801980.427655
..................
1165980231391.4055912.050373
1166080231401.2181791.215036
1166180231410.2811180.607518
1166280231420.1874120.151880
1166380231430.4685300.303759

11664 rows × 5 columns

5.4 比例异常值压缩(凌晨时段)

该步骤定义函数function,对凌晨6点前(hours_in_day <=6)比例大于2的异常值进行压缩(强制设为2)。原因:凌晨流量极小,小时级均值接近0,易导致比例异常放大,而凌晨流量对整体预测误差影响小,压缩后可避免预测值失真。

# 定义异常值压缩函数:凌晨6点前(hours_in_day <=6)比例>2时,强制设为2;其他情况保留原比例
def function(a, b):if a>=2 and b <=6: return 2else:return a# 对进站比例进行异常值压缩
t21_25_in_out_ratio['in_ratio'] = t21_25_in_out_ratio.apply(lambda x: function(x.in_ratio, x.hours_in_day), axis = 1)
# 对出站比例进行异常值压缩
t21_25_in_out_ratio['out_ratio'] = t21_25_in_out_ratio.apply(lambda x: function(x.out_ratio, x.hours_in_day), axis = 1)

5.5 计算28日小时级均值(基准日流量)

该步骤筛选28日(本周一,预测日29日的前一天)数据,按“站点ID、小时”聚合,计算每小时的平均进站/出站人数(in28_mean_hour/out28_mean_hour)。28日与29日时间相近(连续两天),小时级流量更具参考性,作为预测的基准流量。

# 筛选28日(本周一)的数据
t28 = train[(train['day']==28)]# 按“站点ID、小时”聚合,计算28日每小时进站人数均值
t28_in_hour = t28.groupby(['stationID', 'hours_in_day'])['inNums'].mean().reset_index()
# 按相同维度聚合,计算28日每小时出站人数均值
t28_out_hour = t28.groupby(['stationID', 'hours_in_day'])['outNums'].mean().reset_index()# 重命名列名(明确为“28日小时均值”)
t28_in_hour.columns = ['stationID', 'hours_in_day', 'in28_mean_hour']
t28_out_hour.columns = ['stationID', 'hours_in_day', 'out28_mean_hour']# 合并28日小时级进站和出站均值数据
t28_in_out_hour = t28_in_hour.merge(t28_out_hour,on = ['stationID', 'hours_in_day'],how = 'left')# 将28日小时级均值合并到28日原始数据中(对齐站点、小时维度)
t28 = t28.merge(t28_in_out_hour,on = ['stationID', 'hours_in_day'],how = 'left')# 提取核心列(站点ID、小时、10分钟段、当日10分钟级流量、小时级均值),用于后续匹配
t_28 = t28[['stationID', 'hours_in_day', 'ten_minutes_in_day','inNums','outNums','in28_mean_hour','out28_mean_hour']]

5.6 构建预测数据集(匹配测试集与特征)

该步骤读取测试提交模板(需预测29日数据),提取测试集中的时间特征(小时、10分钟段),并将其与28日小时级均值、上周10分钟段比例数据合并,为最终计算29日10分钟级流量搭建数据框架。

# 读取测试提交模板(2019-01-29日),删除模板中默认的inNums和outNums列(后续填充预测值)
sub29=pd.read_csv('./input/testA_submit_2019-01-29.csv')
del sub29['inNums']
del sub29['outNums']# 从测试集startTime提取“小时”特征(用于匹配28日小时级均值)
sub29['hours_in_day'] = pd.to_datetime(sub29['startTime'],format='%Y-%m-%d %H:%M:%S').dt.hour
# 从测试集startTime提取“日内10分钟段”特征(用于匹配上周10分钟段比例)
sub29['ten_minutes_in_day']= pd.to_datetime(sub29['startTime'],format='%Y-%m-%d %H:%M:%S').dt.minute // 10  # 分钟//10得到0-5的段编号# 第一步:将测试集与28日数据合并,获取28日小时级均值(in28_mean_hour/out28_mean_hour)
sub29=sub29.merge(t_28,on = ['stationID', 'hours_in_day', 'ten_minutes_in_day'],how = 'left')
# 第二步:将测试集与上周10分钟段比例合并,获取in_ratio/out_ratio
sub29=sub29.merge(t21_25_in_out_ratio,on = ['stationID', 'hours_in_day', 'ten_minutes_in_day'],how = 'left')# 填充合并过程中可能产生的缺失值(若有站点-时间组合无历史数据,默认设为0)
sub29=sub29.fillna(0)

5.7 计算29日预测值(比例映射+衰减调整)

该步骤基于“时间序列相似性”核心逻辑计算预测值:
29日10分钟级流量 = 上周10分钟段比例 × 28日小时级均值 × 0.95
其中×0.95是考虑春运返程背景下,29日(周二)流量可能比28日(周一)小幅衰减,通过经验系数调整预测合理性。

# 计算29日进站人数预测值:上周10分钟段进站比例 × 28日进站小时级均值 × 0.95(衰减系数)
sub29['in29'] = sub29['in_ratio'] * sub29['in28_mean_hour'] * 0.95
# 计算29日出站人数预测值:上周10分钟段出站比例 × 28日出站小时级均值 × 0.95
sub29['out29']= sub29['out_ratio'] * sub29['out28_mean_hour'] * 0.95# 提取测试提交所需列(stationID、startTime、endTime、预测的inNums/outNums)
submition=sub29[['stationID','startTime','endTime','in29','out29']]
# 重命名预测列,匹配提交模板要求(in29→inNums,out29→outNums)
submition=submition.rename(columns = {'in29':'inNums'})
submition=submition.rename(columns = {'out29':'outNums'})# (可选)保存预测结果为CSV文件,用于提交
# submition.to_csv('submission_20190129.csv', index=False)

六、总结与补充说明

6.1 规则建模核心逻辑回顾

本案例采用“基于时间序列相似性的规则建模”,核心假设与步骤如下:

  1. 相似性假设:工作日地铁流量具有稳定的时间分布模式(如早晚高峰时段固定),且相邻日期(如28日与29日)的小时级流量趋势相近。
  2. 数据平滑:用上周(21-25日)10分钟级均值消除单日随机波动,提取稳定的“小时内流量分布比例”。
  3. 基准映射:以28日(预测日前一天)小时级均值为基准,通过“比例映射”将小时级流量拆分为10分钟级流量。
  4. 场景调整:引入0.95衰减系数,适配春运返程期间流量逐步下降的实际场景,提升预测合理性。

6.2 模型优缺点与改进方向

维度优点缺点改进方向
稳定性基于历史统计规律,避免过拟合对突发情况(如极端天气、大型活动)不敏感加入实时数据(如天气、事件日历)作为修正特征
可解释性比例映射逻辑直观,结果可追溯依赖经验系数(如0.95),主观性较强用线性回归或时间序列模型(如ARIMA)优化衰减系数
效率无需复杂训练,计算速度快未利用站点拓扑关系(如换乘站联动效应)加入站点连接度、相邻站点流量等空间特征
泛化性适用于流量模式稳定的场景(如工作日)节假日流量预测误差较大按日期类型(工作日/周末/节假日)分别建模

6.3 扩展工具与技术选型建议

若需提升预测精度,可结合以下技术:

  1. 时间序列模型:使用ARIMA、SARIMA(处理季节性)、Prophet(Facebook开源,适配节假日)等模型,捕捉长期趋势与周期性。
  2. 机器学习模型:用XGBoost/LightGBM构建集成模型,输入特征包括:
    • 时间特征:小时、星期几、是否节假日、是否月初/月末;
    • 历史特征:前1天/前7天同期流量、滚动均值(如24小时滑动平均);
    • 空间特征:站点连接度、相邻站点流量、站点所在区域(如商业区/住宅区)。
  3. 深度学习模型:用LSTM/Transformer处理时序数据,尤其适合长序列、多站点联动的复杂场景。

上一章:【竞赛系列】机器学习实操项目06——客户信用评估模型进阶流程(特征重要性分析与稳定性监控)
下一章:
机器学习核心知识点目录:机器学习核心知识点目录
机器学习实战项目目录:【从 0 到 1 落地】机器学习实操项目目录:覆盖入门到进阶,大学生就业 / 竞赛必备


文章转载自:

http://qMzJhkpN.fksxs.cn
http://TpBwGsxX.fksxs.cn
http://SbSie7kd.fksxs.cn
http://GdqWLWNm.fksxs.cn
http://JKvW0VdW.fksxs.cn
http://ROMXrBv6.fksxs.cn
http://2K0S0Duu.fksxs.cn
http://0u8JTKkH.fksxs.cn
http://cgwF8lyN.fksxs.cn
http://U23xW6hs.fksxs.cn
http://ggJbvQ8O.fksxs.cn
http://0b0p0AYg.fksxs.cn
http://PJ1N9g5D.fksxs.cn
http://UoLfuhYf.fksxs.cn
http://dwmmWJPU.fksxs.cn
http://GJ5ElvJs.fksxs.cn
http://ItIeDVAK.fksxs.cn
http://XKlmxbhr.fksxs.cn
http://ejDYovOv.fksxs.cn
http://7t92lpYr.fksxs.cn
http://kqcLI8rm.fksxs.cn
http://7RDV2Yqz.fksxs.cn
http://RC2eNPWA.fksxs.cn
http://8xYof5Ru.fksxs.cn
http://aFIqLhSM.fksxs.cn
http://YTSyh5DW.fksxs.cn
http://pROAt5Sb.fksxs.cn
http://BnoCKXbf.fksxs.cn
http://L6LbnEzd.fksxs.cn
http://GYFZolkS.fksxs.cn
http://www.dtcms.com/a/377423.html

相关文章:

  • 华为昇腾CANN开发实战:算子自定义与模型压缩技术指南
  • Java 多线程(二)
  • TCGA(The Cancer Genome Atlas)数据库是癌症基因组学研究的重要资源,包含了多种癌症类型的基因组、转录组、表观基因组和临床数据
  • 单片机与PLC:定义、异同及替代可能性解析
  • 金融知识:投资和融资
  • 重学前端013 --- 响应式网页设计 CSS网格布局
  • hCaptcha 图像识别 API 对接说明
  • 大模型应用开发八股
  • Linux进程概念(上):进程基本概念和进程状态
  • 汽车EPAS ECU功能安全建模分析:Gamma框架+深度概率编程落地ISO 26262(含寿命预测案例)
  • 深入解析:ES6 中 class 与普通构造器的区别
  • 华清远见25072班网络编程学习day3
  • QT(3)
  • 具有区域引导参考和基础的大型语言模型,用于生成 CT 报告
  • 【QT】-怎么实现瀑布图
  • 【Leetcode hot 100】94.二叉树的中序遍历
  • 渗透测试真的能发现系统漏洞吗
  • 【芯片设计-信号完整性 SI 学习 1.2 -- loopback 回环测试】
  • Android App瘦身方法介绍
  • MySQL修改字段类型避坑指南:如何应对数据截断与转换错误?
  • Linux权限以及常用热键集合
  • 成品油加油站综合监管迈入 “云时代”!智慧物联网涉税数据采集平台推行工作全面推进
  • c primer plus 第五章复习题和练习题
  • C++设计模式,高级开发,算法原理实战,系统设计与实战(视频教程)
  • Spring 统一功能处理
  • ES6基础入门教程(80问答)
  • 第3讲 机器学习入门指南
  • InnoDB 逻辑存储结构:好似 “小区管理” 得层级结构
  • copyparty 是一款使用单个 Python 文件实现的内网文件共享工具,具有跨平台、低资源占用等特点,适合需要本地化文件管理的场景
  • C# 哈希查找算法实操