4. 用户响应行为建模:价格弹性矩阵、消费者心理学模型、用户舒适度约束
好,咱们进入第四章。说实话,这一章是整个需求响应算法里最“软”的部分,但也是最关键的部分。你想想看,电网侧的约束、市场规则,这些都是硬邦邦的数学公式。可用户呢?用户是人,人的行为是有情绪的、有惯性的、有舒适度底线的。
我刚开始做需求响应项目时,犯过一个错误。我假设用户都是“理性经济人”——电价涨了,他就自动少用电。结果呢?实际数据啪啪打脸。用户根本没那么敏感,或者说,他的敏感是有条件的。今天咱们就把这块硬骨头啃下来。
4.1 价格弹性矩阵:从“一刀切”到“精细化”
价格弹性,说白了就是“电价变化1%,用电量变化百分之几”。但这里有个坑——不同时段的弹性是不一样的。
举个例子:
- 峰时段:用户已经开空调了,你电价涨20%,他可能也就忍了,弹性很小。
- 谷时段:用户本来就在睡觉,电价再降,他也起不来开洗衣机,弹性几乎为零。
- 平时段:用户有选择空间,弹性相对大一些。
所以,我们不能用一个数字描述所有时段的弹性。得用矩阵。
价格弹性矩阵:一个 n×n 的矩阵,n 是时段数。对角线元素是自弹性(本时段电价对本时段用电的影响),非对角线元素是交叉弹性(其他时段电价对本时段用电的影响)。
数学上长这样:
E = [[ε₁₁, ε₁₂, ..., ε₁ₙ],
[ε₂₁, ε₂₂, ..., ε₂ₙ],
...
[εₙ₁, εₙ₂, ..., εₙₙ]]
其中 εᵢⱼ = (∂Qᵢ / ∂Pⱼ) × (Pⱼ / Qᵢ)。
我个人习惯用对数线性模型来估计这个矩阵。为什么?因为稳定。线性模型容易跑出负的用电量,对数模型不会。
import numpy as np
import pandas as pd
def estimate_elasticity_matrix(price_data, demand_data):
"""
用最小二乘法估计价格弹性矩阵
price_data: DataFrame, 每列是一个时段的价格
demand_data: DataFrame, 每列是对应时段的用电量
"""
n_periods = price_data.shape[1]
E = np.zeros((n_periods, n_periods))
for i in range(n_periods):
# 对每个时段的需求,用所有时段的价格做回归
log_demand = np.log(demand_data.iloc[:, i])
log_prices = np.log(price_data)
log_prices = sm.add_constant(log_prices)
model = sm.OLS(log_demand, log_prices).fit()
# 系数就是弹性
E[i, :] = model.params[1:] # 去掉常数项
return E
避坑指南:我曾经用这个模型跑一个工业用户的数据,结果交叉弹性全是负的。后来发现,这个用户的生产流程是连续的,峰时段电价高了,他只能把生产挪到谷时段,所以交叉弹性应该是正的(替代效应)。嗯,这里要注意——交叉弹性的符号取决于用户类型。居民用户一般是负的(互补效应),工业用户可能是正的(替代效应)。
4.2 消费者心理学模型:用户不是机器人
价格弹性矩阵是纯数学的,但用户的行为里藏着心理学。我总结了三类常见的心理效应:
- 阈值效应:电价变化必须超过某个阈值,用户才会反应。比如电价涨5%,用户可能没感觉;涨20%,他才开始关灯。
- 惯性效应:用户不会立刻响应。今天电价涨了,他可能拖到明天才调整。这个滞后时间,短的几小时,长的好几天。
- 饱和效应:用户的响应是有上限的。你电价涨到天上去,他最多也就关掉非必要负荷,不可能把冰箱也拔了。
怎么建模?我常用一个带死区的S型曲线模型:
def psychological_response(price_change, threshold=0.1, max_response=0.3, steepness=10):
"""
消费者心理学响应模型
price_change: 电价变化比例(如0.2表示涨20%)
threshold: 响应阈值
max_response: 最大响应比例
steepness: 曲线陡峭程度
"""
if abs(price_change) < threshold:
return 0 # 死区内,无响应
# S型曲线:sigmoid函数
x = (abs(price_change) - threshold) * steepness
response = max_response / (1 + np.exp(-x))
# 根据涨价还是降价,决定响应方向
return response if price_change > 0 else -response
关键参数:阈值、最大响应、滞后时间。这三个参数,我建议用历史数据做网格搜索来标定。别拍脑袋定,我吃过这个亏。
4.3 用户舒适度约束:别把用户逼急了
这是最容易被忽视的一点。很多算法只想着“怎么让用户少用电”,却忘了问“用户愿不愿意”。
我参与过一个项目,算法把空调温度设到30度,用户直接手动override了。为什么?因为舒适度被突破了。
常见的舒适度约束包括:
- 温度范围:夏季空调,一般24-28度是可接受的。低于24度或高于28度,用户会投诉。
- 光照强度:调光控制,不能低于某个照度值。
- 设备启停次数:空调频繁启停,用户会烦。一般每小时不超过4次。
- 等待时间:热水器、洗衣机,用户能接受的最长等待时间。
数学上,这些约束可以写成:
T_min ≤ T(t) ≤ T_max # 温度约束
L(t) ≥ L_min # 光照约束
∑ on_off_events(t) ≤ N_max # 启停次数约束
wait_time ≤ W_max # 等待时间约束
注意:舒适度约束不是死的。不同用户、不同场景,容忍度不一样。我建议在系统中留一个“用户偏好配置”接口,让用户自己调。别替用户做决定。
4.4 三者如何协同?一张图说清楚
价格弹性矩阵、心理学模型、舒适度约束,这三者不是孤立的。它们的关系是这样的:
你看这个流程:电价信号进来,先经过价格弹性矩阵算出一个“理论响应量”。但这个量是纯数学的,用户不一定买账。所以再经过心理学模型,加上阈值、惯性、饱和这些“人性化”的修正。最后,还要检查舒适度约束——如果响应方案让用户不舒服,那就得调整。
这三层走完,才是用户真正会执行的响应行为。
4.5 实战中的几个坑
最后,分享几个我踩过的坑:
- 数据粒度不够:价格弹性矩阵需要至少1年的历史数据,而且粒度要到15分钟。如果只有小时级数据,弹性估计会偏差很大。
- 忽略用户异质性:别把所有用户当成一类。居民、商业、工业,弹性完全不同。我建议至少分3类建模。
- 舒适度约束太硬:有些约束可以软化的。比如温度,不是非得24-28度,可以设一个“惩罚函数”,超出范围就加惩罚项,而不是直接禁止。
- 忘了反馈:用户的行为会改变他的习惯。今天响应了,明天可能就不响应了。模型需要在线更新。
我的建议:刚开始做的时候,别追求完美。先用一个简单的自弹性模型跑起来,然后逐步加入交叉弹性、心理学模型、舒适度约束。迭代着来,比一次性搞个大而全的模型靠谱得多。
好了,这一章就到这儿。用户响应行为建模,说白了就是“理解用户、尊重用户、引导用户”。别想着控制用户,那行不通。
公众号:蓝海资料掘金营,微信deep3321