一、数据预处理
1.1 聚合数据
train_df = train_power_forecast_history.groupby(['id_encode','ds']).head(1)
# .groupby(['id_encode','ds'])将数据框按照'id_encode'和'ds'列的唯一组合进行分组,并选择每个组中的第一个行作为结果
# 唯一组合指,对于'id_encode'列和'ds'列的每个唯一值的组合,都会形成一个独立的组。例如,如果'id_encode'列有3个唯一值(1、2、3),'ds'列有4个唯一值(2023-01-01、2023-01-02、2023-01-03、2023-01-04),那么根据这两列的唯一组合,将会形成12个不同的组
# 最后得到每个站点每日的相关信息
del train_df['hour']
# 删除了train_df数据框中的'hour'列
tmp_df = train_power.groupby(['id_encode','ds'])['power'].sum()
#.groupby(['id_encode','ds'])将数据框按照'id_encode'和'ds'列的唯一组合进行分组。然后,['power'].sum()将对每个组中的'power'列进行求和操作。
# 此步是为了提取每个站点每日的总充电量
1.2 合并充电量数据
train_df = train_df.merge(tmp_df, on=['id_encode','ds'], how='left')
# merge()函数将根据指定的列(在这里是'id_encode'和'ds')将两个数据框进行合并。左连接意味着结果数据框将包含左侧数据框(train_df)中的所有行,并将右侧数据框(tmp_df)中与左侧数据框匹配的行合并在一起
# on=['id_encode','ds'],根据'id_encode'和'ds'列的匹配关系进行对应。如果在tmp_df中找不到与train_df中的'id_encode'和'ds'列匹配的行,对应的列将会填充为缺失值(NaN)
1.3 合并数据
train_df = train_df.merge(train_stub_info, on='id_encode', how='left')
# 同2. train_stub_info不包含ds 所以不需要匹配“ds”列
tmp_df
id_encode ds
0 20220415 2288.2240
20220416 2398.5730
20220417 2313.0330
20220418 2095.3259
20220419 1834.3590
...
499 20230410 653.9099
20230411 663.0800
20230412 678.3201
20230413 704.5300
20230414 658.4100
合并后,得到每个站点每日的总充电量和其他信息 train_df
id_encode ele_price ser_price after_ser_price total_price f1 \
0 0 0.64 0.95 0.31 1.59 0.0
1 0 0.64 0.95 0.31 1.59 0.0
2 0 0.64 0.95 0.31 1.59 0.0
3 0 0.64 0.95 0.31 1.59 0.0
4 0 0.64 0.95 0.31 1.59 0.0
... ... ... ... ... ... ...
149039 499 0.00 0.51 0.51 0.51 3.0
f2 f3 ds power
0 0.0 1.0 20220415 2288.2240
1 0.0 1.0 20220416 2398.5730
2 0.0 1.0 20220417 2313.0330
3 0.0 1.0 20220418 2095.3259
4 0.0 1.0 20220419 1834.3590
... ... ... ... ...
149039 1240.0 NaN 20230410 653.9099
思考:train_df = train_power_forecast_history.groupby(['id_encode','ds']).head(1) baseline中为什么要取head(1) ?
可能是前面每个站点一天中ele_price等信息基本相同?所以忽略了每时的变化可以进行数据处理,如均值,归一化等,而不是采用第一个小时的信息作为参考标准
数据预处理 将数据中的字符映射成数字,便于模型回归计算
#数据预处理
train_df['flag'] = train_df['flag'].map({'A':0,'B':1})
test_df['flag'] = test_df['flag'].map({'A':0,'B':1})
# 映射操作 train_df['flag']表示选择train_df数据框中的'flag'列。.map({'A':0,'B':1})表示将'flag'列中的'A'替换为0,将'B'替换为1
二、获取时间特征
def get_time_feature(df, col):
# df表示输入的数据框,col表示要提取时间特征的日期时间列名
df_copy = df.copy() #为了避免修改原始数据框
prefix = col + "_"
df_copy['new_'+col] = df_copy[col].astype(str)
col = 'new_'+col
df_copy[col] = pd.to_datetime(df_copy[col], format='%Y%m%d')
# 使用pd.to_datetime()函数将new_col列中的字符串日期时间转换为datetime类型。这里使用了format='%Y%m%d'参数来指定日期时间的格式
# format='%Y%m%d'中%Y表示四位数的年份,%m表示两位数的月份,%d表示两位数的日期
# 根据ds信息添加时间特征列,即年份、月份、星期几、月初、月末等
df_copy[prefix + 'year'] = df_copy[col].dt.year
df_copy[prefix + 'month'] = df_copy[col].dt.month
df_copy[prefix + 'day'] = df_copy[col].dt.day
# df_copy[prefix + 'weekofyear'] = df_copy[col].dt.weekofyear
df_copy[prefix + 'dayofweek'] = df_copy[col].dt.dayofweek
df_copy[prefix + 'is_wknd'] = df_copy[col].dt.dayofweek // 6
df_copy[prefix + 'quarter'] = df_copy[col].dt.quarter
df_copy[prefix + 'is_month_start'] = df_copy[col].dt.is_month_start.astype(int)
df_copy[prefix + 'is_month_end'] = df_copy[col].dt.is_month_end.astype(int)
del df_copy[col]
return df_copy
train_df = get_time_feature(train_df, 'ds')
test_df = get_time_feature(test_df, 'ds')
三、模型训练
3.1 K折交叉验证
K折交叉验证 sklearn.model_selection.KFold
KFold(n_splits=几折 , random_state=随机种子 , shuffle=是否打乱 )
eg:
KFold(n_splits=2, random_state=None, shuffle=False)
>>> for i, (train_index, test_index) in enumerate(kf.split(X)):
... print(f"Fold {i}:")
... print(f" Train: index={train_index}")
... print(f" Test: index={test_index}")
Fold 0:
Train: index=[2 3]
Test: index=[0 1]
Fold 1:
Train: index=[0 1]
Test: index=[2 3]
实战:
folds = 5
kf = KFold(n_splits=folds, shuffle=True, random_state=seed)
# 初始化oof预测和测试集预测
oof = np.zeros(train_x.shape[0])
test_predict = np.zeros(test_x.shape[0])
cv_scores = []
# KFold交叉验证
for i, (train_index, valid_index) in enumerate(kf.split(train_x, train_y)):
trn_x, trn_y, val_x, val_y = train_x.iloc[train_index], train_y[train_index], train_x.iloc[valid_index], train_y[valid_index]