学习日记——SEED新能源赛道之电动汽车充电站充电需求预测

本人小白,Baseline跑通后便开始了断断续续长达两天的读代码工作,在大致吃透后在代码旁作了一些注解。

1、首先安装lightgbm机器学习模型,它是一个基于决策树的快速算法框架。

      ps:在这之前如果python没装过pip得事先安装一下

!pip install -U lightgbm

2、相关库导入,这里用KFold(K折交叉验证方法)来评估模型,评估指标为RMSE(均方根误差)。

#import 相关库
import numpy as np
import pandas as pd
import lightgbm as lgb
from sklearn.model_selection import StratifiedKFold, KFold, GroupKFold
from sklearn.metrics import mean_squared_error, mean_absolute_error
import matplotlib.pyplot as plt
import tqdm
import sys
import os
import gc
import argparse
import warnings
warnings.filterwarnings('ignore')#滤除警告

3、csv数据导入。

train_power_forecast_history = pd.read_csv('./data1/train/power_forecast_history.csv')
train_power = pd.read_csv('./data1/train/power.csv')
train_stub_info = pd.read_csv('./data1/train/stub_info.csv')

test_power_forecast_history = pd.read_csv('./data1/test/power_forecast_history.csv')
test_stub_info = pd.read_csv('./data1/test/stub_info.csv')

4、数据清洗与处理 ,区分训练集与测试集。

train_df=train_power_forecast_history.groupby(['id_encode','ds']).head(1)#head1表示只取每个站点第一行的数据
del train_df['hour']
#print(train_df)

test_df = test_power_forecast_history.groupby(['id_encode','ds']).head(1)
del test_df['hour']

#将一个站点每小时的发电量相加
tmp_df=train_power.groupby(['id_encode','ds'])['power'].sum()
tmp_df.columns=['id_encode','ds','power']
print(tmp_df)

#合并充电量数据
train_df=train_df.merge(tmp_df,on=['id_encode','ds'],how='left')
#print(train_df)

### 合并数据
train_df = train_df.merge(train_stub_info, on='id_encode', how='left')
test_df = test_df.merge(test_stub_info, on='id_encode', how='left')
print(train_df)

      由于给定的数据中各个站点的历史充电数据是以小时为统计单位的,而题目要求以天为预测单位 ,所以先分出各个站点(500个)的数据组,再对各自24小时的充电量进行求和,将表格进行合并,得到方便之后进行训练的数据集。


id_encode  ds          power
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
Name: power, Length: 149044, dtype: float64

        id_encode  ele_price  ser_price  after_ser_price  total_price   f1  \
0               0       0.64       0.95             0.31         0.95  0.0   
1               0       0.64       0.95             0.31         0.95  0.0   
2               0       0.64       0.95             0.31         0.95  0.0   
3               0       0.64       0.95             0.31         0.95  0.0   
4               0       0.64       0.95             0.31         0.95  0.0   
...           ...        ...        ...              ...          ...  ...   
149039        499       0.00       0.51             0.51         0.51  3.0   
149040        499       0.00       0.51             0.51         0.51  3.0   
149041        499       0.00       0.51             0.51         0.51  3.0   
149042        499       0.00       0.51             0.51         0.51  3.0   
149043        499       0.00       0.51             0.51         0.51  3.0   

            f2   f3        ds      power  parking_free flag               h3  \
0          0.0  1.0  20220415  2288.2240           1.0    A  85309ea7fffffff   
1          0.0  1.0  20220416  2398.5730           1.0    A  85309ea7fffffff   
2          0.0  1.0  20220417  2313.0330           1.0    A  85309ea7fffffff   
3          0.0  1.0  20220418  2095.3259           1.0    A  85309ea7fffffff   
4          0.0  1.0  20220419  1834.3590           1.0    A  85309ea7fffffff   
...        ...  ...       ...        ...           ...  ...              ...   
149039  1240.0  NaN  20230410   653.9099           1.0    A  85309ca3fffffff   
149040  1240.0  NaN  20230411   663.0800           1.0    A  85309ca3fffffff   
149041  1240.0  NaN  20230412   678.3201           1.0    A  85309ca3fffffff   
149042  1240.0  NaN  20230413   704.5300           1.0    A  85309ca3fffffff   
149043  1240.0  NaN  20230414   658.4100           1.0    A  85309ca3fffffff   

        ac_equipment_kw  dc_equipment_kw  
0                   0.0           1440.0  
1                   0.0           1440.0  
2                   0.0           1440.0  
3                   0.0           1440.0  
4                   0.0           1440.0  
...                 ...              ...  
149039              0.0            540.0  
149040              0.0            540.0  
149041              0.0            540.0  
149042              0.0            540.0  
149043              0.0            540.0  

[149044 rows x 15 columns]

5、观测历史数据的分布情况,从分布中分析是否存在周期性、趋势性、相关性和异常性。 

cols =['power']
for ie in [0,1,2,3,4]:
    # 获取train_df中id_encode为当前值ie的所有行(前5个站点),并重置索引
    tmp_df=train_df[train_df['id_encode']==ie].reset_index(drop=True)
    tmp_df = tmp_df.reset_index(drop=True).reset_index()

    for num,col in enumerate(cols):
        plt.figure(figsize=(20,10))
        plt.subplot(4,1,num+1)
        plt.plot(tmp_df['index'],tmp_df[col])
        plt.title(col)
plt.show()
plt.figure(figsize=(20,5))

6、数据预处理:类型转换(数字化flag)。

#将训练集与测试集中flag的A,B全部替换成0,1
train_df['flag']=train_df['flag'].map({'A':0,'B':1})
test_df['flag']=test_df['flag'].map({'A':0,'B':1})
train_df.head()

7、提取时间特征:具体为提取月、日、小时、周等相关特征。

def get_time_feature(df, col):#定义一个函数,输入为一个数组和列索引

    df_copy=df.copy() #复制数组
    prefix=col+"_" #在列索引名称后加下划线
    df_copy['new_'+col]=df_copy[col].astype(str) #astype为数据类型转换函数,将col这一列的数据转换为字符串

    col='new_'+col
    df_copy[col]=pd.to_datetime(df_copy[col],format='%Y%m%d') #将col数据转换为日期时间格式
    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 + 'dayofweek'] = df_copy[col].dt.dayofweek #提取星期,0为星期一,6为星期天
    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')

cols = [f for f in test_df.columns if f not in ['ds','power','h3']] #去除了日期,充电量和h3编码的列

8、模型训练,这里想多遍历,折数K设置为8。

#K折交叉验证训练,验证模型
def cv_model(clf,train_x,train_y,test_x,seed=2023):
    folds=8 #定义折数
    kf=KFold(n_splits=folds,shuffle=True,random_state=seed) #初始化KFold

    #初始化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)):
        #enumerate() 函数:它用于遍历一个可迭代对象,并同时获取索引和值。在这段代码中,i 是循环的计数器,而 (train_index, valid_index) 则是每次循环中取出的训练集和验证集的索引。
        #kf.split(train_x, train_y):这个方法是交叉验证对象 kf 的分割函数,将训练数据集 train_x 和目标变量 train_y 进行划分,并返回相应的训练集和验证集索引。
        #for 循环语句,它会遍历交叉验证对象生成的训练集和验证集索引,并将每一轮次的索引赋值给 train_index 和 valid_index,并使用 enumerate() 函数获取循环计数器 i

        print('************************{}*************************'.format(str(i+1)))
        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]
        
        # 转换数据为lightgbm数据格式
        train_matrix = clf.Dataset(trn_x, label=trn_y)
        valid_matrix = clf.Dataset(val_x, label=val_y)

        # 定义lightgbm参数
        params = {
            'boosting_type': 'gbdt',     #'boosting_type':指定使用的 boosting 类型,一般为 'gbdt'(梯度提升树)。
            'objective': 'regression',   #'objective':指定优化问题类型,这里是回归问题,使用了 'regression'(回归)。
            'metric': 'rmse',    #'metric':评估指标,用于衡量模型性能,此处使用了 'rmse'(均方根误差)来表示回归任务中的损失函数。
            'min_child_weight': 5,  #'min_child_weight':树节点最小实例权重和,用于控制过拟合。
            'num_leaves': 2 ** 7,  #'num_leaves':决策树的叶子节点数目。
            'lambda_l2': 10,  #'lambda_l2':L2 正则化系数,用于控制模型的复杂度。
'feature_fraction': 0.8,  #'feature_fraction':特征抽样比例,用于控制每轮迭代中使用的特征比例。
            'bagging_fraction': 0.8,  #'bagging_fraction':数据抽样比例,用于控制每轮迭代中使用的训练样本比例。
            'bagging_freq': 4,  #'bagging_freq':bagging 的频率,表示执行 bagging 的步长。
            'learning_rate': 0.1,  #'learning_rate':学习率,控制每次迭代的更新步长。
            'seed': 2023,   #'seed':随机种子,用于复现实验结果。
            'nthread' : 16, #'nthread':线程数,用于并行计算的线程数目。
            'verbose' : -1,  #'verbose':详细程度,决定训练过程中输出信息的详细程度。在这里,-1 表示不输出任何信息
            }

        # 训练模型
        model = clf.train(params, train_matrix, 3000, valid_sets=[train_matrix, valid_matrix], categorical_feature=[])
        
        # 获取验证和测试集的预测值
        val_pred = model.predict(val_x, num_iteration=model.best_iteration)
        test_pred = model.predict(test_x, num_iteration=model.best_iteration)

        oof[valid_index] = val_pred
        test_predict += test_pred / kf.n_splits
        
        # 计算并打印当前折的分数
        score = np.sqrt(mean_squared_error(val_pred, val_y))
        cv_scores.append(score)
        print(cv_scores)

    return oof, test_predict

# 调用上面的函数进行模型训练和预测
lgb_oof, lgb_test = cv_model(lgb, train_df[cols], train_df['power'], test_df[cols])

9、输出结果

test_df['power'] = lgb_test
test_df['power'] = test_df['power'].apply(lambda x: 0 if x<0 else x)
# "power" 列中的每个值应用了一个 lambda 函数, 如果 "power" 列中的值小于0,则将其替换为0,否则保留原值。
test_df[['id_encode','ds','power']].to_csv('result.csv', index=False)

后续任务:

1、尝试多模型融合

2、尚未尝试异常值处理

3、h3_to_geo,h3编码的转换,这样就能了解500个站点的大致地理位置

4、特征工程继续优化,细化一些。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值