目录
赛题数据
electricity price.csv
:电力市场的市场出清价格,市场需求等信息。训练集范围为2021年12月1日到2023年7月1日,共计55392个点;测试集范围为2023年7月1日到2024年4月18日,共计28228个点
- Day/Time:交易时间,中国电力现货市场15分钟结算一次,一天共96个交易点
- demand:区域内电力总负荷(总需求),单位为MW
- clearing price (CNY/MWh):市场出清电价,单位为元/MW·h
day | time | demand | clearing price (CNY/MWh) |
---|---|---|---|
2021/12/1 | 0:15 | 40334.18 | 350.8 |
2021/12/1 | 0:30 | 40523.15 | 350.8 |
2021/12/1 | 0:45 | 40374.74 | 350.8 |
💡 其中,测试集的市场出清价格为空
code.csv
:存放市场供给者(各发电机组)的参数信息
机组数据包含549个不同的火电机组
- unit ID:每个机组唯一的ID
- Capacity(MW):机组的额定容量(额定功率),越高机组的发电能力越强
- utilization hour (h) :电厂的年平均运行小时数,需要注意多个机组可能共同属于一个电厂,有相同的值
- coal consumption (g coal/KWh):每发一度电需要耗费多少煤炭,为成本参数
- power consumption rate:电厂单位时间内耗电量与发电量的百分比,例如单位时间耗电量为500度电,发电量为10000度电,利用率就是500/10000=5%。
比赛任务
预测2023年7月1日到2024年4月18日每15分钟的市场出清价格
submit.csv
提交格式
day,time,clearing price (CNY/MWh)
2024/4/1 , 0:15 , 352.334
2024/4/1 , 0:30 , 355.536
baseline详解
数据预处理
1.读数据
base_path = Path("data") # 创建Path对象,base_path为data,确保数据都放在同级的data目录下
# 读取市场数据
electricity_price = pd.read_csv(base_path / "electricity price.csv")
# 读取市场主体(各发电机组)数据
unit = pd.read_csv(base_path / "unit.csv")
- pathlib.Path:将文件或者文件夹路径(str)转换为Path对象
2.准备示例提交数据sample_submit
"""
准备示例提交数据sample_submit
1. electricity_price["clearing price (CNY/MWh)"].isna()找到出清价格为缺失值的行,即要预测的目标
2. 去除demand列,符合最后的提交格式
"""
sample_submit = electricity_price[electricity_price["clearing price (CNY/MWh)"].isna()].drop(columns="demand")
sample_submit.to_csv(base_path / "sample_submit.csv", index=False)
lectricity_price["clearing price (CNY/MWh)"].isna()
检查DataFrame中名为"clearing price (CNY/MWh)"的列,并返回一个布尔序列,表示对应位置数据是否为NaN,是NaN为True,不是为False。
0 False
1 False
2 False
3 False
4 False
...
83515 True
83516 True
83517 True
83518 True
83519 True
Name: clearing price (CNY/MWh), Length: 83520, dtype: bool
electricity_price[electricity_price["clearing price (CNY/MWh)"].isna()]
这部分代码使用上面生成的布尔序列来筛选出DataFrame中"clearing price (CNY/MWh)"列中值为NaN的所有行,即测试集。- 去掉demand列即符合提交格式
- 使用 pandas DataFrame 对象的to_csv方法,将
sample_submit
保存为 CSV 文件,文件名为sample_submit.csv
,index=False
: 指定在导出 CSV 文件时不包含行索引(index)
3.合并electricity_price中day和time
# 将day和time列合并成timestamp列,便于提取时间戳特征
electricity_price["timestamp"] = pd.to_datetime(
electricity_price["day"] + " " + electricity_price["time"].str.replace("24:00:00", "00:00"))
pd.to_datetime(...)
: 用于将字符串转换为日期时间格式electricity_price["time"].str
是用来访问electricity_price
DataFrame 中time
列的字符串处理方法。.str
属性允许你对这一列中的每个字符串元素进行向量化的字符串操作。也就是说,.str
提供了一系列字符串操作方法,可以方便地对列中的每个元素执行相同的字符串操作,而无需显式地遍历这些元素。replace("24:00:00", "00:00")
将time
列中的 “24:00:00” 替换为 “00:00”,因为 “24:00:00” 在日期时间格式中无效,应该转换为下一天的 “00:00”。
# 处理24:00:00的情况,即表示第二天的00:00:00
mask = electricity_price['timestamp'].dt.time == pd.Timestamp('00:00:00').time()
- 创建一个布尔掩码(mask),用于标识
electricity_price
DataFrame 中timestamp
列中时间部分等于00:00:00
的行。 electricity_price['timestamp'].dt.time
: 这是从electricity_price
DataFrame 的timestamp
列中提取时间部分(即时、分、秒)pd.Timestamp('00:00:00').time()
: 这是创建一个表示时间00:00:00
的Timestamp
对象,然后提取其时间部分。结果是一个datetime.time
对象,表示时间00:00:00
。- 生成的布尔掩码与之前的
lectricity_price["clearing price (CNY/MWh)"].isna()
效果等同,可将时间为00:00:00的行标识为True,可用于进一步筛选
# 需要将这些行的日期部分加一天
electricity_price.loc[mask, 'timestamp'] += pd.Timedelta(days=1)
- 将时间为00:00:00的行日期加一天
- electricity_price.loc[mask, ‘timestamp’]选中所有
timestamp
列中时间为00:00:00
的行 pd.Timedelta
是 pandas 中表示时间间隔(时间差)的对象,允许你在时间戳上进行加减运算,pd.Timedelta(days=1)
创建了一个表示1天的时间间隔的对象。
# 设置列的顺序,同时去除day和time列
electricity_price = electricity_price[["timestamp", "demand", "clearing price (CNY/MWh)"]]
- 选择列:
["timestamp", "demand", "clearing price (CNY/MWh)"]
指定了选择的列。 - 创建新 DataFrame:
electricity_price[...]
通过列选择操作,创建一个只包含指定列的新 DataFrame。
electricity_price.head() # 显示前5行数据
4.查看机组数据
- unit ID:每个机组唯一的ID
- Capacity(MW):机组的额定容量(额定功率),越高机组的发电能力越强
- utilization hour (h) :电厂的年平均运行小时数,需要注意多个机组可能共同属于一个电厂,有相同的utilization hour (h)
- coal consumption (g coal/KWh):每发一度电需要耗费多少煤炭,为成本参数
- power consumption rate:电厂单位时间内耗电量与发电量的百分比,例如单位时间耗电量为500度电,发电量为10000度电,利用率就是500/10000=5%。
unit.head()
使用ABM估计市场出清价格
💡 用边际成本定价法作为baseline,即每个机组都以其自身每生产一度电的成本进行报价,而不考虑市场信息和其他机组信息。
随后,按报价从低到高排序,依次接受报价,直到累计的功率超过了总需求,市场达到出清状态,并以最后一个满足的报价作为市场出清价格的估计。
1.将报价从低到高排序
coal consumption (g coal/KWh)
表示每发一度电需要耗费多少煤炭,近似于边际成本
sorted_unit = unit.sort_values("coal consumption (g coal/KWh)") # 按照一度电的耗煤量(近似为边际成本)升序排序
sorted_unit.head()
2.计算累计功率,直到累计的功率超过了总需求,市场达到出清状态,并以最后一个满足的报价作为市场出清价格的估计
# 预先计算 sorted_unit 的累积和
sorted_unit['cumulative_capacity'] = sorted_unit['Capacity(MW)'].cumsum()
.cumsum()
: 这是 pandas 的一个方法,用于计算累积和。它返回一个新的 Series,每个元素是从 DataFrame 的开始到当前行的Capacity(MW)
列的累积和。
例如,如果 sorted_unit
的数据如下:
Capacity(MW)
0 100
1 200
2 150
在执行 cumsum()
后,cumulative_capacity
列将是:
Capacity(MW) cumulative_capacity
0 100 100
1 200 300
2 150 450
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]
sorted_unit[sorted_unit['cumulative_capacity'] >= demand]
用于找到累计和大于需求的行["coal consumption (g coal/KWh)"].iloc[0]
,由于累计和大于需求的行可能有多个,选择第一个coal consumption (g coal/KWh)
3.使用线性回归转换耗煤量为机组报价
model = LinearRegression()
# 训练集长度
train_length = 55392
# 用 reshape 方法将数组重塑为一个二维数组。参数 -1 表示自动计算这一维的大小,而 1 表示数组将有一列。这样,所有元素都将排列在单独的一行中。
prices = np.array(prices).reshape(-1, 1)
X = prices[:train_lengh]
# .values 的使用是为了将 pandas Series 转换为 NumPy 数组,机器学习库和算法(scikit-learn)期望输入是 NumPy 数组,而不是 pandas 对象。将 pandas Series 转换为 NumPy 数组确保与库的兼容性
y = electricity_price["clearing price (CNY/Mwh)"].iloc[:train_length].values
model.fit(X,y)
reshape(-1, 1)
假设 prices
列表如下:
prices = [10, 20, 30, 40]
执行该行代码后,prices
将被转换为:
array([[10],
[20],
[30],
[40]])
# 输出线性模型的系数与截距
model.coef_, model.intercept_
可以看出我们的拟合方程式是:P=11.26*N-2763.16
,其中N为耗煤量,P为机组报价
# 用线性模型预估出清价格
y_pred = model.predict(prices[train_length:])
# 将 y_pred 数组展平为一维数组
y_pred = y_pred.flatten()
y_pred[:5]
保存结果
sample_submit["clearing price (CNY/Mwh)"] = y_pred
sample_submit.head()
sample_submit.to_csv("submit.csv", index = False)
上分攻略
- 尝试更复杂的ABM策略:
- 高电价时,机组是否可以抬价
- 当上轮机组价格远低于出清价/竞标失败时,下一轮会做出什么调整?
- 是否能参考相似机组调整自己价格?
- 能否参考历史相似日期价格调整价格
- 利用外部数据(天气、煤价……)
- 思考unit中电厂总开机时间、机组发电效率的用法
- unit中的电厂均为火电厂,是否会在不同时段受其他替代品(新能源)影响
- 能否推断出数据集是北方还是南方,具体气候特征是什么(水电、风电、太阳能会受地区影响,进而竞争火电)
- 总需求和出清价格线性关系很高,尝试直接线性回归
- 进一步挖掘数据时序信息
- 进一步了解电力现货市场交易规则