数据采集与清洗:打好电价预测的地基
说实话,很多做电价预测的朋友,一上来就急着调模型、跑算法。我见过太多这样的案例了——模型结构花里胡哨,结果预测效果一塌糊涂。为什么?因为数据没处理好。数据采集与清洗,说白了就是给模型喂饭。饭馊了,再好的厨师也白搭。
今天咱们就聊聊这个环节。我个人习惯把数据准备分成三步:找对来源、洗掉脏数据、对齐时间戳。每一步都有坑,我踩过不少,今天一并分享给你。
核心观点:数据清洗占整个项目时间的60%-80%,模型调优只占20%。别搞反了。
1. 数据来源:去哪儿找电价数据?
电价预测的数据源,全球就那么几个主流市场。我重点讲两个:PJM 和 Nord Pool。
PJM(宾夕法尼亚-新泽西-马里兰联合电力市场)
PJM 是美国最大的电力市场,覆盖13个州。它的数据公开、完整、历史长。我个人最喜欢它的 实时电价(Real-Time LMP) 和 日前电价(Day-Ahead LMP)。
- 数据获取方式:PJM 官网提供 CSV 下载,也有 API 接口
- 数据频率:5分钟、1小时、1天
- 字段示例:时间戳、节点ID、LMP价格、电量、拥堵成本
我的小技巧:PJM 的数据量很大,别一次性全拉。我一般按年分批次下载,避免内存爆炸。
Nord Pool(北欧电力市场)
Nord Pool 覆盖挪威、瑞典、芬兰、丹麦等国家。它的特点是水电占比高,电价受天气影响大。我做过一个项目,需要预测挪威某区域电价,Nord Pool 的数据是首选。
- 数据获取方式:Nord Pool 官网、ENTSO-E 透明度平台
- 数据频率:1小时(主流)、1天
- 字段示例:区域、时间、系统电价、区域电价、负荷
| 数据源 | 地区 | 频率 | 获取难度 |
|---|---|---|---|
| PJM | 美国东部 | 5分钟/1小时 | 低(公开API) |
| Nord Pool | 北欧 | 1小时 | 中(需注册) |
| ENTSO-E | 欧洲 | 1小时/15分钟 | 中(需API密钥) |
注意:不同数据源的字段命名、时间格式、时区都不一样。我曾经因为时区没对齐,导致模型训练了三天,结果全废了。嗯,这个坑我替你们踩过了。
2. 缺失值处理:数据不完整怎么办?
电价数据缺失,太常见了。传感器故障、网络中断、节假日...原因五花八门。我遇到过最离谱的一次,某区域连续72小时数据全空,后来发现是数据库迁移时搞丢了。
缺失值处理,我一般分三步走:
- 先诊断:看看缺失比例。低于5%的,直接填充;超过20%的,考虑删除该特征
- 再分类:是随机缺失?还是连续缺失?处理方式不同
- 最后填充:选合适的方法
常用填充方法
- 前向填充(ffill):用上一个有效值填充。适合短时间缺失,比如5分钟的数据缺了1个点
- 后向填充(bfill):用下一个有效值填充。适合数据末尾缺失
- 线性插值:用前后两个点的平均值。我个人最常用,简单有效
- 季节性插值:用昨天同一时刻的值。电价有24小时周期,这个方法很实用
# 我常用的缺失值处理代码
import pandas as pd
# 读取数据
df = pd.read_csv('pjm_data.csv', parse_dates=['timestamp'])
df.set_index('timestamp', inplace=True)
# 检查缺失
print(df.isnull().sum())
# 短时间缺失:前向填充
df['price'].fillna(method='ffill', inplace=True)
# 长时间缺失:用昨天同一时刻的值
df['price'] = df['price'].fillna(df['price'].shift(24))
避坑指南:我曾经直接用均值填充,结果模型预测值全往均值靠拢,完全失去了波动性。后来改用季节性插值,效果好了很多。
3. 异常值检测:揪出那些离谱的数据
电价数据里,异常值很常见。比如某天电价突然飙到10000美元/MWh,或者跌到负数。这些是真实的市场现象吗?还是数据错误?
我一般用两种方法:统计法 和 业务规则法。
统计法:3σ 原则
假设数据服从正态分布,超过均值±3倍标准差的值,视为异常。这个方法简单,但电价数据往往不服从正态分布,所以我会先用对数变换。
import numpy as np
# 对数变换后检测异常
df['log_price'] = np.log(df['price'] + 1) # +1避免log(0)
mean = df['log_price'].mean()
std = df['log_price'].std()
# 标记异常
df['is_outlier'] = (np.abs(df['log_price'] - mean) > 3 * std)
业务规则法:结合市场常识
统计法会漏掉一些异常。比如某天电价突然从50涨到200,虽然没超过3σ,但1小时内涨了4倍,这明显不合理。我一般会加一条规则:相邻时刻价格变化超过300%的,标记为可疑。
我的经验:异常值不要直接删除。先标记,然后人工核查。有些异常其实是真实的市场事件,比如极端天气导致的电价飙升。删了反而丢失信息。
4. 时间对齐:让所有数据在一个节奏上
不同数据源的时间频率不一样。PJM 有5分钟数据,Nord Pool 是1小时数据,天气数据可能是3小时一次。怎么对齐?
我一般以预测目标的时间频率为准。比如我要预测小时电价,就把所有数据重采样到1小时。
重采样方法
- 降采样(高频→低频):用均值、中位数或加权平均。比如5分钟数据转1小时,取60个点的均值
- 升采样(低频→高频):用插值或前向填充。比如3小时天气数据转1小时,用线性插值
# 重采样示例:5分钟数据转1小时
df_hourly = df.resample('1H').mean()
# 升采样:3小时数据转1小时
df_weather_hourly = df_weather.resample('1H').interpolate(method='linear')
注意:重采样时,时区问题一定要处理。PJM 用的是美国东部时间,Nord Pool 用的是欧洲中部时间。我建议统一转为 UTC,避免夏令时带来的混乱。
知识体系总览
下面这张图,是我自己总结的数据采集与清洗流程。你照着这个走,基本不会出大问题。
数据清洗这件事,看起来琐碎,但它是整个预测项目的基石。我见过太多人在这上面栽跟头。你想想看,如果数据里藏着10%的异常值,模型学到的规律能对吗?
好了,这一章就到这里。数据准备好了,下一章咱们就可以开始特征工程了。到时候我会分享一些我自己总结的、特别有效的电价特征构造方法。