3、数据准备与清洗:数据来源、采集方法与质量治理
旅游需求预测模型的精度,往往不取决于算法多么先进,而取决于输入数据的质量与结构。真实业务场景中的数据分散在预订系统、搜索引擎、气象服务、票务平台等多个异构源中,存在缺失、异常、时区不一致、粒度不统一等问题。本章围绕数据来源、采集方法、缺失值处理、异常值检测、时间对齐与重采样五个核心环节,构建一套可落地的数据工程链路,为后续特征工程与模型训练奠定坚实基础。
3.1 数据来源:多源异构数据的价值分层
旅游需求预测的数据来源可划分为三个层级:核心交易层(历史预订、订单取消、入住/出票记录)、行为意图层(搜索指数、页面浏览、收藏加购、社交媒体舆情)、环境驱动层(天气、节假日、经济指标、重大事件、航班运力)。三个层级对预测模型的贡献不同:交易层提供需求基准,行为层提供短期波动信号,环境层提供结构性变化解释。
| 数据类别 | 典型字段 | 采集频率 | 预测价值 | 主要挑战 |
|---|---|---|---|---|
| 历史预订 | 订单号、入住日期、预订日期、渠道、房型、价格、取消标记 | 实时/每日 | 核心需求基准 | 取消率波动、提前预订天数分布 |
| 搜索指数 | 关键词、设备类型、地域、时间戳 | 小时级 | 短期需求先导指标 | 噪声大、关键词覆盖不全 |
| 天气数据 | 温度、降水、风速、空气质量、预警 | 小时级 | 影响当日/次日需求 | 预报与实况差异、空间粒度 |
| 节假日 | 日期、类型、调休安排、连休天数 | 年度 | 结构性峰值 | 调休政策变化、地域差异 |
| 经济指标 | GDP、CPI、消费者信心指数、汇率 | 月度/季度 | 中长期趋势 | 发布滞后、与旅游需求传导路径长 |
3.2 数据采集方法:管道设计与工程实现
数据采集的核心矛盾是时效性与系统稳定性之间的平衡。对于预订数据,推荐使用增量同步方式,每日凌晨拉取前一日全量变更;对于搜索指数,采用小时级定时任务调用第三方API;对于天气数据,使用预报数据而非实况数据,因为预测时只能获取预报值。
# 伪代码:预订数据增量采集管道
def fetch_booking_data(last_sync_time):
# 1. 从业务库读取增量变更
changes = db.query("""
SELECT * FROM booking_orders
WHERE updated_at > %(last_sync_time)s
""", {"last_sync_time": last_sync_time})
# 2. 写入数据湖临时区
staging_path = f"s3://data-lake/staging/booking/{date.today()}"
write_parquet(changes, staging_path)
# 3. 执行数据质量检查
quality_report = run_quality_checks(staging_path)
# 4. 原子性写入正式分区
if quality_report.pass_rate > 0.95:
move_to_formal_partition(staging_path)
update_sync_checkpoint(datetime.now())
else:
alert_engineer("数据质量未达标", quality_report)
采集方法对比
| 方法 | 适用场景 | 时效性 | 成本 | 风险 |
|---|---|---|---|---|
| API 直连 | 内部系统、有官方接口的第三方 | 实时/准实时 | 低 | 接口限流、字段变更 |
| 数据库同步 | 业务库到数仓 | T+1 | 低 | 主库性能影响 |
| 爬虫采集 | 无官方接口的公开数据 | 小时级 | 中 | 反爬、法律风险 |
| 日志采集 | 用户行为数据 | 准实时 | 中 | 数据量大、清洗复杂 |
| 手工导入 | 线下渠道、合作伙伴数据 | 不定期 | 高 | 格式不统一、易出错 |
3.3 缺失值处理:从删除到插补的决策框架
缺失值处理不能一刀切。首先需要判断缺失机制:完全随机缺失(MCAR)、随机缺失(MAR)、非随机缺失(MNAR)。在旅游场景中,天气数据缺失往往与设备故障有关(MCAR),而高端酒店预订数据缺失可能与渠道覆盖不足有关(MNAR),后者需要业务侧介入。
处理策略选择
- 删除策略:当缺失率 <5% 且为 MCAR 时,可直接删除缺失样本;当某特征缺失率 >30% 时,建议剔除该特征。
- 统计插补:均值/中位数/众数插补适用于数值型低缺失率场景,但会降低方差,导致预测区间偏窄。
- 时间序列插补:线性插值、样条插值适用于连续时间序列;前向填充(ffill)适用于预订量这类具有强自相关的数据。
- 模型插补:使用 KNN、随机森林等模型基于其他特征预测缺失值,适用于特征间相关性较强的场景。
- 业务规则插补:节假日数据缺失时,可根据国务院放假安排手工维护;天气数据缺失时,可回退到气候平均值。
3.4 异常值检测:区分真实波动与数据错误
旅游需求数据天然具有高波动性,节假日峰值可能是正常值的 3-5 倍。因此,异常值检测必须结合业务日历和历史同期进行判断,不能仅依赖统计阈值。
三层检测法
| 层级 | 方法 | 适用场景 | 示例 |
|---|---|---|---|
| 全局统计 | Z-score、IQR | 平稳序列的初步筛查 | 某日预订量超过均值+3σ |
| 局部窗口 | 移动平均残差、STL分解 | 识别趋势中的突变点 | 连续7天平稳后突然下降50% |
| 业务规则 | 日历事件、竞品动态 | 判断异常是否合理 | 演唱会/赛事/极端天气导致的激增 |
3.5 时间对齐与重采样:构建统一的时间索引
多源数据的时间粒度天然不一致:预订数据精确到秒,搜索指数为小时级,天气数据为3小时预报,经济指标为月度。建模前必须将所有数据对齐到统一的时间索引上。
对齐与重采样操作流程
- 统一时区:所有时间戳转换为 UTC 存储,展示时按目标市场时区转换。中国区业务建议使用 Asia/Shanghai。
- 确定基准粒度:根据预测目标决定。日粒度预测是最常见的选择,周粒度适合长线产品,小时粒度适合景区门票预约。
- 重采样规则:预订量使用求和(sum),价格使用均值(mean),天气使用最大值/最频繁值,节假日使用布尔标记。
- 滞后特征构造:对齐后,为搜索指数构造 7/14/21 天滞后特征,为天气构造当日/前一日特征。
- 验证对齐结果:检查重采样后是否存在空窗期,尤其是跨月、跨年边界。
import pandas as pd
# 对齐示例:将小时级搜索指数重采样为日粒度
search_daily = search_hourly.resample('D', label='right', closed='right').agg({
'search_volume': 'sum',
'device_type': lambda x: x.mode()[0] if not x.empty else 'unknown'
})
# 合并预订数据与搜索数据
merged = booking_daily.join(search_daily, how='left', lsuffix='_booking', rsuffix='_search')
# 检查对齐后的缺失情况
missing_report = merged.isnull().sum() / len(merged)
print(f"搜索指数缺失率: {missing_report['search_volume']:.2%}")
3.6 案例拆解:某海岛度假酒店的需求预测数据工程
背景与约束:某海岛度假酒店(300间客房)需要搭建未来 90 天的入住率预测模型。酒店 60% 订单来自 OTA 渠道,40% 来自官网直销。历史数据从 2019 年至今,中间经历了疫情导致的停业期(2020年2-4月)。业务方要求预测结果每日更新,且需区分平日与周末的差异。
初始现象与目标:初步建模时发现,直接使用原始预订数据训练,模型在节假日预测误差高达 40%。排查发现三个问题:① 2020年疫情停业期数据未剔除,导致模型低估旺季需求;② OTA 渠道的预订提前期(lead time)平均为 45 天,而官网直销仅 15 天,混合建模导致特征失效;③ 天气数据使用的是实况而非预报,造成特征泄漏。
关键操作步骤:
- 数据清洗:剔除 2020年2-4月停业期数据,单独标记为"异常经营期";对取消标记缺失的订单,按渠道分别计算取消率并随机插补。
- 异常值处理:发现 2023年国庆期间预订量是历史均值的 4.2 倍,经核实为真实需求,保留并添加"黄金周"事件标签。
- 时间对齐:将搜索指数(小时级)重采样为日粒度,并构造 7/14/21 天滞后特征;天气数据使用预报值而非实况值,避免特征泄漏。
- 渠道分离:分别构建 OTA 渠道和直销渠道的预测模型,最终加权融合。OTA 模型侧重提前期特征,直销模型侧重会员活跃度特征。
结果对比:优化后,节假日预测 MAPE 从 40% 降至 18%,平日预测 MAPE 从 15% 降至 9%。代价是数据管道复杂度提升,每日调度时间从 20 分钟增加到 45 分钟,但仍在业务可接受范围内。
复盘经验:数据清洗不是一次性工作,而是需要建立数据质量监控看板,每日跟踪缺失率、异常值数量、时间对齐完整性三个核心指标。当新增数据源或业务规则变化时,必须重新评估清洗策略。