Datawhale AI夏令营 社会科学赛道baseline详解

赛题数据

electricity price.csv:电力市场的市场出清价格,市场需求等信息。训练集范围为2021年12月1日到2023年7月1日,共计55392个点;测试集范围为2023年7月1日到2024年4月18日,共计28228个点

  1. Day/Time:交易时间,中国电力现货市场15分钟结算一次,一天共96个交易点
  2. demand:区域内电力总负荷(总需求),单位为MW
  3. clearing price (CNY/MWh):市场出清电价,单位为元/MW·h
daytimedemandclearing price (CNY/MWh)
2021/12/10:1540334.18350.8
2021/12/10:3040523.15350.8
2021/12/10:4540374.74350.8

💡 其中,测试集的市场出清价格为空

code.csv:存放市场供给者(各发电机组)的参数信息

机组数据包含549个不同的火电机组

  1. unit ID:每个机组唯一的ID
  2. Capacity(MW):机组的额定容量(额定功率),越高机组的发电能力越强
  3. utilization hour (h) :电厂的年平均运行小时数,需要注意多个机组可能共同属于一个电厂,有相同的值
  4. coal consumption (g coal/KWh):每发一度电需要耗费多少煤炭,为成本参数
  5. 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)
  1. 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
  1. electricity_price[electricity_price["clearing price (CNY/MWh)"].isna()]这部分代码使用上面生成的布尔序列来筛选出DataFrame中"clearing price (CNY/MWh)"列中值为NaN的所有行,即测试集。
  2. 去掉demand列即符合提交格式
  3. 使用 pandas DataFrame 对象的to_csv方法,将sample_submit 保存为 CSV 文件,文件名为 sample_submit.csvindex=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:00Timestamp 对象,然后提取其时间部分。结果是一个 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)"] 指定了选择的列。
  • 创建新 DataFrameelectricity_price[...] 通过列选择操作,创建一个只包含指定列的新 DataFrame。
electricity_price.head()  # 显示前5行数据

在这里插入图片描述

4.查看机组数据

  1. unit ID:每个机组唯一的ID
  2. Capacity(MW):机组的额定容量(额定功率),越高机组的发电能力越强
  3. utilization hour (h) :电厂的年平均运行小时数,需要注意多个机组可能共同属于一个电厂,有相同的utilization hour (h)
  4. coal consumption (g coal/KWh):每发一度电需要耗费多少煤炭,为成本参数
  5. 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)

上分攻略

  1. 尝试更复杂的ABM策略:
  2. 高电价时,机组是否可以抬价
  3. 当上轮机组价格远低于出清价/竞标失败时,下一轮会做出什么调整?
  4. 是否能参考相似机组调整自己价格?
  5. 能否参考历史相似日期价格调整价格
  6. 利用外部数据(天气、煤价……)
  7. 思考unit中电厂总开机时间、机组发电效率的用法
  8. unit中的电厂均为火电厂,是否会在不同时段受其他替代品(新能源)影响
  9. 能否推断出数据集是北方还是南方,具体气候特征是什么(水电、风电、太阳能会受地区影响,进而竞争火电)
  10. 总需求和出清价格线性关系很高,尝试直接线性回归
  11. 进一步挖掘数据时序信息
  12. 进一步了解电力现货市场交易规则
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值