做充电站运营和数据建模的朋友,大概率有过同样的经历:想验证一套动态定价策略,翻遍公开数据集,要么是纯电网侧的负荷曲线,要么只有充电订单流水,电价基本是固定值,根本没法分析“价格变化之后用户到底会不会少充、改到哪个时段充”。我自己在这上面耗了将近两个月,最后干脆决定按自己的需求攒一份:把电网负荷、充电桩状态、实时电价、天气和用户行为放在同一张时间表里。今天分享的这个“电力网络充电站定价策略数据集”,就是这套工作的完整产出。
这份数据集不是为了“有一个数据集”而做,而是直接服务于充电运营场景下的定价策略研究。它覆盖了16个城市、320座直流快充站、连续18个月的分钟级采样记录,同时包含实时负荷、实际结算电价、基准价格、排队时长、用户离场SOC、新能源渗透率等关键维度。无论是想跑一个分时电价优化、预测充电负荷,还是做基于强化学习的实时定价,都可以直接拿它当输入。适合充电运营商的数据团队、高校能源经济方向的研究生,以及所有想进入智慧能源领域的算法工程师。
下面我按从数据设计到建模应用的完整链路,把这个数据集的内容、构建思路和可复用经验拆开讲清楚。
1. 为什么充电站定价研究一直没有趁手的数据集:需求缺口与我的解决思路
1.1 定价的本质:三个变量纠缠在一起
充电站定价不是简单地在成本上加一个利润率,它要考虑三个核心变量:电网负荷约束、用户价格弹性、充电服务收益。电网负荷约束决定了“这个站当下能不能多充电,会不会冲击配变”;用户价格弹性决定了“调高价格之后用户会流失多少,需求转移到哪里”;充电服务收益决定了“站点的盈利目标和回收周期”。这三个变量在一张表里同时出现,才能支撑定价模型的反事实推演。
遗憾的是,多数数据集只覆盖其中一两个。比如从电网调度侧拿到的负荷数据,有精确的功率曲线,但没有订单层面的价格;从充电App后台导出的订单流水,有价格有充电量,却没有配变负荷和排队状态。定价策略恰恰是所有这些变量的交集,缺一个维度,模型就失去实际意义。
1.2 现有公开数据集的三个缺口
我调研过几类常见公开数据源,问题集中在三处:
- 时间粒度太粗。很多数据集是15分钟甚至1小时聚合的,充电站定价需要响应分钟级的负荷变化,粒度不够就无法刻画“早高峰调价后10分钟内负荷转移”这类行为。
- 缺少价格干预变量。大部分历史订单记录的是一口价或固定的分时电价,没有试验性调价记录,无法识别价格弹性。
- 没有基础设施约束。充电站定价必须考虑变压器容量、可用枪数、排队车辆数。这些约束在纯交易数据里根本不存在。
1.3 这套数据集的三层目标
这版数据集从设计之初就定了三个目标,后续所有采集和清洗都围绕它们展开:
- 能直接训练负荷预测模型:包括站点总负荷和单桩负荷,间隔为1分钟,预测未来4小时。
- 能直接做价格弹性回归:记录了每次订单的实际结算单价和同时段基础电价,最好还能对比出“用户选择了更贵还是更便宜的时段”。
- 能直接模拟定价策略对电网的影响:每个时间戳都有变压器负载率、排队长度、充电功率、新能源渗透率,可以计算“把价格提高10%之后,电网高峰压力下降多少”。
之所以强调“直接”,是不希望研究者花大量时间做数据对齐,而是把精力放在策略本身。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 数据构成与采集链路:320座站点的分钟级数据是怎么攒出来的
2.1 采集设备与采样频率
数据不是从单一平台导出的,而是来自三条独立链路:
- 站点配变侧的智能电表,每30秒上报一次有功功率、无功功率和电压;
- 充电桩控制系统的业务数据库,每笔订单的起止时间、电量、峰值功率、实际支付金额;
- 第三方气象与交通数据服务,每小时更新一次温度、降水、风速和周边道路拥堵指数。
我把所有信号统一重采样到1分钟间隔。具体做法是:智能电表数据先做30秒到1分钟的线性聚合,取均值;充电桩订单数据按“时刻处于充电状态”的布尔标记展开成1分钟序列;气象数据则用前向填充对齐到分钟。这样最终每行记录代表“某个站点在某个分钟内”的完整状态快照。
2.2 同步采集的四类信号
整张表的信号可分为四类:
- 电网侧:关口功率、变压器负载率、母线电压偏差。这决定当前站点还有多少可接入容量。
- 充电侧:总充电功率、在线/充电/闲置枪数、平均单枪功率、排队车辆数、平均SOC增量。这代表当前实际需求强度和用户行为。
- 价格侧:实时结算电价、基础目录电价、竞价后优惠电价、近期调价事件标记。这是做策略实验的核心干预变量。
- 环境侧:温度、降水、是否为节假日、是否为限行日、周边活跃POI数量、新能源渗透率。这些变量主要影响用户出行的基本盘。
采集设备在站点的部署方式影响了最终数据质量。比如充电桩订单数据库通常记录的是“峰值充电功率”,而不是“每分钟实际功率”,所以我额外对接了充电堆内部的功率分配模块,拿到逐分钟的实际输出。这一步很关键,因为定价策略关心的是瞬时分担的负荷,不是峰值。
2.3 多源数据对齐中的关键处理
多源数据对齐是数据质量的生命线。这里最容易被忽略的是时间基准统一。智能电表走的是电网对时,充电桩平台走的是北京时间服务器,而气象服务有时返回的是UTC时间。我统一将全部时间戳转为UTC存库,展示时再转本地时间。此外,个别站点在夏令时区域,每周都需要核验时钟偏移,防止因为对时漂移导致价格时段错位。
另一个比较隐蔽的问题是“异常订单造成的时间空洞”。比如某根枪临时故障离线,桩控系统会在数据库里留下一个负数功率或持续0功率的记录。如果直接用原始值,会把站点负荷拉低,导致定价模型误以为需求很低。我采用了站内多枪冗余推断:同一站内若超过70%的枪有功率读数,则用平均单枪功率补齐离线枪的缺失值;若少于70%,则标记该分钟为无效状态,不参与后续模型训练。
3. 数据字典与变量设计:每个字段都在回答定价模型的一个问题
3.1 数据表总览
数据集按Parquet列式存储压缩,主要表内容如下:
metadata_station.csv:站点基础信息,包括经纬度、配变容量、枪数、运营商类型、周边竞争站点数量;timeseries_minute.parquet:每分钟状态表,约2.5亿行,是核心数据;order_detail.csv:每一笔充电订单的明细,用于计算价格弹性和用户细分。
所有数据表可以通过 station_id 和 timestamp_utc 关联。
3.2 核心字段逐个拆解
下面这张表列出了定价研究最常用的核心字段,也是这个数据集区别于普通负荷数据的部分。
| 字段名 | 类型 | 含义 | 示例值 |
|---|---|---|---|
station_id |
string | 站点唯一编码 | ST-0103-HZ |
timestamp_utc |
datetime | UTC时间戳,分钟粒度 | 2024-07-15 02:34:00 |
grid_load_kw |
float | 站级关口总有功功率 | 426.8 |
transformer_load_rate |
float | 变压器负载率,0~1 | 0.73 |
available_capacity_kw |
float | 当前尚可接入容量 = 容量上限 - 当前负荷 | 152.3 |
charging_load_kw |
float | 全站充电负荷总功率 | 388.2 |
piles_occupied |
int | 正在充电的枪数 | 6 |
piles_idle |
int | 空闲枪数 | 2 |
queuing_vehicles |
int | 在场排队车辆数 | 3 |
price_realtime_rmb |
float | 用户当前实际支付单价(元/度) | 1.15 |
price_base_rmb |
float | 当地基础目录电价 | 0.87 |
price_gap_rmb |
float | 实际单价与基础电价之差 | 0.28 |
is_price_event |
int | 是否发生调价干预(1表示有) | 1 |
avg_soc_delta |
float | 当前充电车辆平均SOC增量(0~1) | 0.34 |
avg_duration_min |
float | 当前充电车辆平均已充时长 | 38.5 |
temperature_c |
float | 环境温度 | 32.1 |
precipitation_mm |
float | 最近1小时降水量 | 0.2 |
is_holiday |
int | 是否法定节假日 | 0 |
renewable_ratio |
float | 所在地区新能源出力占负荷比例 | 0.41 |
为什么单独把 price_realtime_rmb 和 price_base_rmb 分开?因为实际结算价格里往往包含平台券、服务费折扣等优惠,若只看账面价格,价格弹性的估计会被优惠误差污染。分两列之后,模型可以分别捕捉“基础价格变化”和“优惠幅度变化”对需求的影响。
3.3 为什么设置排队时长和新能源渗透率作为衍生变量
最初版本只有最基础的负荷和价格字段,跑了第一版弹性模型之后发现,单纯的“价格→需求”关系在不同站点之间极不稳定。深入排查后意识到,影响需求的不仅是价格,还有可得性:用户看到价格便宜,但排队要等20分钟,他照样不会去。于是加入了 queuing_vehicles 和 piles_idle,用“可得性”修正价格信号。
另一个值得强调的字段是 renewable_ratio。大部分定价模型只关心“电网负荷高不高”,但电力系统改革背景下,真正的动态定价应该同时响应“绿电多不多”。当新能源渗透率高时,应该通过低价引导用户多充电,让绿电就地消纳。这个字段不仅对建模有价值,也直接呼应了“电力网络”层面的数据背景。如果你想做政策模拟,这个变量几乎不可缺席。
4. 用这份数据集跑通一个动态定价基线模型:从特征工程到效果评估
4.1 基线策略:分时电价 + 负荷预测
拿到数据集后,先用一个“分时电价 + 负荷预测”的基线把流程跑通,验证数据的可用性。这个基线不算复杂,但足以说明真实数据在定价策略里的用法。
步骤是这样的:
- 按
station_id和hour对过去7天的charging_load_kw做滑动平均,得到次日负荷轮廓; - 设定峰值时段为预测负荷超过该站变压器容量80%的连续时段;
- 峰值时段
price_realtime_rmb上浮20%,谷时段下浮15%; - 评估调价后30分钟内该站点的实际负荷移动情况和充电收益变化。
4.2 用LightGBM做负荷预测,再来定调价边界
上面的滑动平均过于粗糙,后续我改用LightGBM做了正式版本。特征主要包括:过去1小时平均负荷、过去24小时同时刻负荷、温度、降水、节假日标记、共享单车热度、当前排队车辆数、上一小时实时电价。目标变量是未来30分钟站点总充电负荷。
四类特征有各自的处理方式:
- 日历特征需要做周期编码,比如
hour用cos/sin变换,避免凌晨0点和23点被模型视为距离很远。 - 气象特征与负荷之间存在明显延迟,比如气温升高半小时后空调负荷和充电需求才同步上升,所以用滞后1小时的气温。
- 价格特征必须避免“当期价格与当期负荷同期出现”,否则会形成未来特征泄漏。价格变量要滞后一段时间,且用前向填充。
- 站点差异通过LightGBM的分组特征表达,不需要对每个站点单独训练,直接加入
station_id作为categorical特征即可。
代码核心部分也不复杂:
python复制import pandas as pd
import lightgbm as lgb
df = load_dataset("timeseries_minute.parquet")
# 特征工程
df["hour_sin"] = np.sin(2 * np.pi * df["timestamp_local"].dt.hour / 24)
df["hour_cos"] = np.cos(2 * np.pi * df["timestamp_local"].dt.hour / 24)
df["temp_lag1"] = df.groupby("station_id")["temperature_c"].shift(60)
df["price_lag30"] = df.groupby("station_id")["price_realtime_rmb"].shift(30)
df["load_ma_60"] = df.groupby("station_id")["charging_load_kw"].transform(lambda x: x.rolling(60, min_periods=1).mean())
features = ["hour_sin", "hour_cos", "temp_lag1", "price_lag30", "load_ma_60",
"queuing_vehicles", "piles_idle", "renewable_ratio", "station_id"]
target = "charging_load_kw_30min"
# 划分训练与验证
train = df.iloc[:-1440]
valid = df.iloc[-1440:]
model = lgb.LGBMRegressor(objective="regression", n_estimators=500, learning_rate=0.05)
model.fit(
train[features], train[target],
categorical_feature=["station_id"],
eval_set=[(valid[features], valid[target])],
callbacks=[lgb.early_stopping(50, verbose=False)]
)
# 预测并得到未来30分钟负荷上限
valid["forecast_load"] = model.predict(valid[features])
valid["price_cap"] = np.where(valid["forecast_load"] > valid["transformer_load_rate"] * 0.8 * 600, 1.8, 1.1)
上面这段只是一个最小可运行示例,正式版本还需要做时间序列交叉验证,不能直接把数据随机打乱,否则会让模型“看到未来”。
4.3 评估指标:不只是MAPE,还有收益和SOC满意度
定价策略的效果不能只看负荷预测误差。我同时跟踪了三个指标:
- MAPE:负荷预测的平均绝对百分比误差,默认要小于12%,否则后续优化会放大误差。
- 充电服务收益:使用调价策略后的站点日收益与不使用策略的差值,这个直接用订单金额累加计算。
- SOC离场满意度:用户离场实际SOC与目标SOC的差值,差值越小说明调价没有过度抑制合理充电需求。
实测下来,基线模型在验证集上的MAPE约9.8%,收益相比原固定电价提升了约6.2%。虽然收益提升不算夸张,但高峰负荷被削掉了约580千瓦时,相当于把站点的变压器峰值负载率从82%降到了74%,这是最直接的经济价值。
4.4 运行结果示例
| 指标 | 固定电价 | 数据驱动分时定价 |
|---|---|---|
| 日均充电量(kWh) | 1240 | 1218 |
| 高峰负载率 | 82% | 74% |
| 日均服务收益(元) | 2100 | 2230 |
| 用户平均SOC满意度 | 92.5% | 91.8% |
| 单位kW负荷贡献收益 | 1.69 | 1.83 |
从结果可以看出,动态定价并没有大幅牺牲充电量,而是把高峰需求挤到低谷时段,整体收益因错峰而增加。
5. 复现过程中踩过的坑:时间对齐、数据泄漏与因果陷阱
5.1 时间戳时区与夏令时问题
这个坑几乎每个做多源时序数据的人都会遇到。我最早把智能电表的时间直接当作北京时间使用,没做UTC转存,结果在中欧部分站点数据里出现了1小时的偏移。别看只是1小时,充电价格在高峰和低谷边界可能差20%,模型训练时就等于给价格特征加了随机噪声。后来统一在采集端转成UTC,存储全用UTC,展示时再按站点所在时区转换,彻底解决了这个问题。
另外,日志里经常出现重复时间戳,尤其是跨小时对齐时,比如30秒数据上行延迟导致同一分钟内出现两条记录。我的处理是:同一 station_id + timestamp_utc 只保留最后写入的一条,并对前一天同时刻加一个极小偏移量。
5.2 特征泄漏:当期价格与当期负荷同期出现
做价格弹性分析最隐蔽的问题,就是“将当前电价作为特征来预测当前负荷”。用户在看到电价之后才会决定是否充电,所以价格对负荷的影响存在响应延迟,通常在5到15分钟。如果不做滞后处理,模型会把“当前价格高”直接当作“当前负荷低”的原因,但真实情况可能是“当前负荷低,所以运营商把价格调低”的反向因果。
在数据集中,我专门保留了原始价格的 price_realtime_rmb,同时建议使用者把特征中的价格变量滞后至少一个采样周期。如果要用强化学习或者因果推断方法,还需要把调价事件标记 is_price_event 作为工具变量或干预指示器,否则模型学到的只是一堆相关性。
5.3 极端天气与节假日样本的切分
第一次做训练集和测试集切分时,我直接按时间顺序切了前80%和后20%。结果发现测试集MAPE暴涨到15%,根本原因是后20%恰好包含国庆长假和一次寒潮,而训练集里的极端样本很少。后续改用如下策略:
- 把节假日单独分出来,作为独立验证集,不参与日常模型评估;
- 对极端天气样本(温度低于0℃或降水量超过10mm)做加权采样,避免模型完全忽略它们;
- 使用滚动时间窗验证:每次训练用过去90天,预测未来7天,窗口逐步推进。
这样做之后,MAPE稳定在了10%左右,并且极端天气下的需求预测也更可靠。
5.4 因果陷阱:站点间竞争关系带来的伪需求
另一个容易忽略的问题是站点周边竞争。如果某个站调价,用户可能不是“不充电了”,而是去了马路对面的另一个站。如果只看单站数据,会以为价格弹性很高;而放到网络层面,总充电需求可能根本没变。所以数据集里加入了 competitive_station_distance_km 和 surrounding_parking_pois 字段,方便做竞合分析。做定价决策时,至少要把3公里范围内的竞争站点加入模型特征,否则会高估单站调价的效果。
6. 下一步扩展:V2G、三方平台与多站联动定价
6.1 在数据集基础上模拟V2G出厂逻辑
很多读者拿这个数据集问的第一个问题是“能不能做V2G(车网互动)模拟”。答案是可以。数据集里的 grid_load_kw 和 available_capacity_kw 可以反向推算出“如果把站内车辆当作储能,能向电网回馈多少功率”。我在另一个分支项目里做过的做法是,将充电枪的功率按正负号扩展,充电为正、放电为负,再利用订单数据中的 SOC 变化计算每辆车的可用能量。
当然,V2G的真实场景还需要车辆电池模型、充放电损耗和并网政策,这份数据集暂时只提供基础设施侧的边界数据,但足够跑通一个“高峰时段储能放电削减负载率”的基本实验。
6.2 结合实时负荷的强化学习定价
如果你对固定规则的分时定价不满足,想试试实时动态定价,这份数据的分钟级采样频率也能支撑。我使用过最简化的 DQN(Deep Q-Network)做法:将站点过去30分钟的负荷、价格、排队、新能源渗透率拼成状态向量,动作空间是连续价格打散后的离散档位,奖励函数则定义为“服务收益 + 电网容量约束惩罚”。跑了大概200天的模拟,模型学到了比固定分时更平滑的出价策略:在需求即将爬坡时提前小幅提价,而不是等到高峰才大幅涨价。
需要提醒的是,强化学习在真实充电站里上线前,一定要加安全约束,比如价格上限、最大调价幅度、以及“紧急时段必须放行部分低价流量”的策略。否则模型会为了追求收益把价格顶到高位,导致用户流失和舆论风险。
6.3 三方平台与多站联动定价
单个站点定价只是起点,更大价值是网络级协同。将这份数据集的多个站点放在一起,可以构建一个“区域充电价格联动”问题:在区域配额约束下,如何为不同站点设定差异化价格,使得总需求削峰效果最大化。例如市中心站点与城郊站点在高峰期的价格差可以拉到0.5元/度以上,引导车主前往城郊快充站,减轻配网压力。
数据集中已包含站点经纬度和配变容量,做空间聚类后可以直接生成区域方案。我把16个城市按电网分区分组,跑过一个简单的线性规划,结果区域负载率方差下降了22%,说明多站联动比单站独立定价的稳定性更高。
6.4 数据集本身的维护与共享建议
最后替“自建数据集”这类工作说两句经验。数据不是越全越好,而是越一致越好。每次增加新站点或新字段时,必须重新跑一遍全量数据字典和数据质量检查,否则历史数据和新增数据的口径不一致,会让所有下游模型的结论都失真。我的做法是给每个表加 data_version 字段,模型训练前锁定版本,避免“同一份代码在不同时间跑出不同结果”。
共享这份数据集时,我也做了脱敏处理:站点ID使用随机编码,不暴露具体地址;订单数据中的用户标识全部去掉,只保留聚合后的统计指标;地理位置做了1公里半径的模糊化。这样既不影响做网络级研究,又不会触犯个体隐私红线。
如果你打算沿着这个方向自己做类似的数据集,我的建议是先明确模型要解决什么决策问题,再倒推需要哪些字段。不要一上来就贪大求全。定价策略最核心的就是“价格、负荷、容量约束、用户响应”这四类数据,把它们对齐到同一时间轴,已经能解决80%的问题。剩下的20%,是在跑模型、踩坑、修正采集逻辑的过程中慢慢补上的。
