阿里云天池社科赛题:市场博弈与价格预测#Datawhale#Datawhale夏令营

赛题来自阿里云天池平台“第二届世界科学智能大赛社会科学赛道:市场博弈和价格预测”,主要针对“电力期货市场”(可类比证券交易市场),指的是短时间内的电能量交易市场 。

话不多说,直接开跑!

速通baseline+个人进阶思路

准备环节

Datawhale准备了一份十分详细的文档: Datawhale 要用电脑打开哦

代码环节可以在魔搭上部署,使用CPU即可:概览 · 魔搭社区 (modelscope.cn)

前言(如果了解ABM与边际成本定价可直接跳过进入step1~):

虽然赛题本质上是一个回归类问题,使用时间序列模型可以完成本次任务,但是赛题方期待选手们使用ABM(Agent-Based-Modeling)模拟不同发电厂之间复杂的博弈(影响复赛30%分数)。

Datawhale的baseline使用了边际成本定价策略作为每个Agent的策略,个人的进阶思路是从最简单的线性回归方向进行尝试~再慢慢去模拟更复杂的博弈行为。

不知道什么是ABM和边际成本定价?别急!下面是科普时间!

ABM—基于个体的建模(Agent-Based-Modeling,Agent更接近强化学习方向的而不是LLM)

论文地址:Agent-based modeling: Methods and techniques for simulating human systems (pnas.org)

在baseline中作者们很简单的提到了ABM(在生态学中又称为IBMs),即基于个体的建模,是一种模拟系统复杂行为的工具。它通过模拟个体(代理)的行为和相互作用,来研究系统整体的动态变化。

十分著名的例子有康威生命游戏_百度百科 (baidu.com)(也叫元胞自动机),在这场游戏中没有玩家的竞争,每个方格中都可放置一个生命细胞,每个生命细胞只有两种状态:“生”或“死”。用黑色方格表示该细胞为“生”,空格(白色)表示该细胞为“死”。或者说方格网中黑色部分表示某个时候某种“生命”的分布图。生命游戏想要模拟的是:随着时间的流逝,这个分布图将如何一代一代地变化。

边际成本定价:

边际成本定价是一种理想化的策略。边际成本定价法也叫边际贡献定价法,该方法以变动成本作为定价基础,只要定价高于变动成本,企业就可以获得边际收益(边际贡献),用以抵补固定成本,剩余即为盈利。它只考虑成本(本题中为煤炭价格)作为报价的参考。但这是一种十分理想化的。在现实市场中,参考的方面会更加复杂。

如下图~MC为边际成本,AVC为平均可变成本,AFC为平均固定成本,ATC为平均总成本。

接下来,我将会带领大家了解baseline中的示例代码,并在文末展示我的进阶版本与一些上分思路。

step1:库的安装与数据预处理

baseline中运用到了pandas,numpy,pathlib与sklearn:

pip install scikit-learn
pip install pathlib
#为了防止大家的电脑中有可能出现没有pandas与numpy的情况我还是附上~
pip install pandas
pip install numpy

数据预处理

Baseline中提供了两个数据集:electricity price.csv(注意哦是空格)与unit.csv,运用pandas导入数据集:

#需要将Path()中的路径替换成自己的哦
BASE = Path(r"D:/Alldataset/data")
# 读取市场数据
electricity_p = pd.read_csv( BASE/"electricity_price.csv")

使用electricity_p.head()显示前五行,检查一下导入情况:

赛题要求选手们提交csv格式的文件,提前准备sample_submit.csv。去除demand列,并使用isna()函数找到出清价格中缺失的值,即我们要预测的值:

# 跟原文一样,生成一个提交文件,去掉demand列并在clearing price中寻找缺失值,作为我们要预测的目标
sample_submit = electricity_p[electricity_p["clearing price (CNY/MWh)"].isna()].drop(columns="demand")
sample_submit.to_csv(BASE/"sample_submit.csv", index=False)

读取unit.csv,并去除过于理想的数据:

#读取unit数据,注意到小伙伴们提到过损耗率为0的情况,这个过于理想,所以降序排列并把power consumption rate为0的数据删除 
unit = pd.read_csv(BASE / "unit.csv")
# # 执行完即可关闭
# unit.sort_values(by='power consumption rate (%)',inplace=True)
# unit = unit.iloc[10:]
unit.head()

输出结果如下,可以注意到power consumption rate中为0的数据已被去除:

注意到electricity price中day,time两种时间数据被分开保存,这样不利于我们后续的调用,选择将两列时间数据合并为timestamp列,便于后续提取时间戳特征:

# 将日期转换为时间戳
electricity_p["timestamp"] = pd.to_datetime(
    electricity_p["day"] +  " "  + electricity_p["time"].str.replace("24:00:00" , "0:00"))

# 把 24:00变更为第二天的 0:00
nask = electricity_p["timestamp"].dt.time == pd.Timestamp("00:00:00").time()

# 需要将这些行的日期加一天
electricity_p.loc[nask,"timestamp"] +=  pd.Timedelta(days=1)
 
# 设置列的顺序,同时去除day和time列
electricity_p = electricity_p[["timestamp","demand","clearing price (CNY/MWh)"]]
electricity_p.head()

head()显示结果如下:

step2:边际成本定价与ABM

在前文中我们提到,baseline的代码使用了边际成本定价作为每一个agent的策略,并用ABM进行模拟。我们将一度电的耗煤量近似为边际成本进行降序排序,这样就可以从最低开始进行出清:

sorted_unit = unit.sort_values("coal consumption (g coal/KWh)")  # 按照一度电的耗煤量(近似为边际成本)降序排序
sorted_unit.head()

显示结果如下:

对发电机组的容量进行排序,并计算累积容量的和,存储在'cumulative_capacity'列中。然后,通过遍历电力价格列表中的每个电力需求,找到第一个满足该需求的发电机组的耗煤量,并将其添加到prices列表中:

# 预先计算 sorted_unit 的累积和
sorted_unit['cumulative_capacity'] = sorted_unit['Capacity(MW)'].cumsum()

prices = []

# 找到最后一个满足总需求的机组报价
for demand in electricity_price["demand"]:
    price = sorted_unit[sorted_unit['cumulative_capacity'] >= demand]["coal consumption (g coal/KWh)"].iloc[0]
    prices.append(price)

print(len(prices))
prices[:5]

step3:线性回归

然而,每一度电的耗煤量不能简单近似为边际成本定价,还存在其他的开销。将耗煤量与市场出清价格进行简单的线性回归,试图拟合估计价格到接近真实市场价:

model = LinearRegression()
# 55392为训练集的长度,用来划分数据集长度
train_length = 55392
prices = np.array(prices).reshape(-1, 1)
X = prices[:train_length]
#将electricity_price中的出清价格筛掉tran_length的长度并reshape
y = electricity_price["clearing price (CNY/MWh)"].iloc[:train_length].values.reshape(-1, 1)
model.fit(X, y)
# y[5:]

调取截距:

model.coef_, model.intercept_

可以看出我们的拟合方程式是:P=11.26*N-2763.16,其中N为耗煤量,P为机组报价

用边际成本定价的报价作为出清价格:

y_pred = model.predict(prices[train_length:])
y_pred = y_pred.flatten()  # 转换维度,2维矩阵转为1维
y_pred[:5]
# print(len(y_pred))

step4:保存提交

最后再用head函数检查一眼~将出清价格的值用预测值代替:

sample_submit["clearing price (CNY/MWh)"] = y_pred
sample_submit.head()

保存:

sample_submit.to_csv("submit.csv", index=False)

到此,baseline的整个代码运行就结束啦,最后得分大概在12939左右,算是一个很不错的初始成绩

进阶思路

想必大家也肯定注意到了,本次赛题的本质就是回归类问题。我的思路就是从最简单的线性回归开始做起。但口说无凭,咱们先来画个图看看~

step1:导入相应库(前面相同的部分此处不再提及)

import matplotlib.pyplot as plt  #导入绘图工具
from sklearn.metrics import r2_score #这个加不加都没关系

为了证明市场需求(demand)与出清价格(clearing price)存在线性相关,我们分别导入两组数据并进行绘图:

# 简单画个图看看,导出clearing price列的数据
n = electricity_p['clearing price (CNY/MWh)'][:60]
m = electricity_p['timestamp'][:60]  #为了防止数据量太大不明显,只取60条

plt.figure(figsize=(10,5))
plt.scatter(m,n,color = 'red')
plt.ylabel('clearing price (CNY/MWh)')
plt.xlabel('Timestamp')
plt.grid(True)
plt.show()

很好,继续导入demand列:

#demand列数据
n1 = electricity_p['demand'][:70]
n2 = electricity_p['timestamp'][:70]
plt.figure(figsize=(10,5))
plt.scatter(n2,n1, color = 'blue')
plt.xlabel("timestamp")
plt.ylabel("demand")
plt.grid(True)
plt.show()

可以明显的看出是有线性相关的

step2:线性回归

猜想得以验证后,开始线性回归!

model = LinearRegression()
train_length = 55392
n3 = electricity_p["demand"]
n3 = np.array(n3).reshape(-1,1)
X = n3[:train_length]
y = electricity_p["clearing price (CNY/MWh)"].iloc[:train_length].values.reshape(-1,1)
model.fit(X,y)

好像有戏,取截距得到方程式:

# 好像有戏
model.coef_, model.intercept_

预测,降维,并输出前五个数据来看看:

y_pred = model.predict(n3[train_length:])
y_pred = y_pred.flatten()
y_pred[:5]
# print(len(y_pred))

emmm......优化力度不大嘛

sample_submit["clearing price (CNY/MWh)"] = y_pred
sample_submit.head()

提交一个看看:

sample_submit.to_csv("submit.csv", index=False)

有.........但不多,仅供参考

  • 21
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值