电动汽车充电站充电量预测_学习笔记(一)——跑通baseline

导入相关库

# 数据格式处理
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 warnings
warnings.filterwarnings('ignore')

数据预处理

数据准备:数据载入与简略观测
# 设置数据集路径
path = r'E:\编程\电动汽车充电站充电量预测'

# 读取数据:载入训练集和测试集数据,后续同步处理
train_power_forecast_history = pd.read_csv(path + r'\训练集\power_forecast_history.csv')
train_power = pd.read_csv(path + r'\训练集\power.csv')
train_stub_info = pd.read_csv(path + r'\训练集\stub_info.csv')

test_power_forecast_history = pd.read_csv(path + r'\测试集\power_forecast_history.csv')
test_stub_info = pd.read_csv(path + r'\测试集\stub_info.csv')

# 观察数据
train_power_forecast_history.head(10)
train_power_forecast_history.tail(10)

         观察发现,训练集的站点运营数据表共有约有356.6万条记录,其中,场站编码'id_encode' 范围是0~499,表中还包含 小时'hour',电费‘ele_price',服务费'ser_price',折后服务费'after_ser_price',总费用'total_price',三个业务指标'f',日期'ds' 共10个字段。

train_power.head(10)
train_power.tail(10)

 

         站点充电量数据表中有一个新字段 充电量'power',是我们的预测目标字段,只在训练集中出现。

train_stub_info.head()    # 默认参数为5
train_stub_info.tail()

        站点静态数据表中新出现的字段有 交流桩额定功率'ac_equipment_kw', 直流桩额定功率'dc_equipment_kw',分层地理编码'h3'(将地球划分为不同的六边形网格),场站标签'flag'(脱敏字段,无描述的有效数据),停车收费'parking_free'(0:收费,1~9:免费停车时长/h)。

数据准备:数据集成
# 聚合数据
'''groupby.head(1):将'id_encode''ds'字段(列)值均相同的记录(行)放入一个组,
生成一个新的表格(dataframe),并纵向拼接新表,且只取新表第一条记录拼接'''
train_df = train_power_forecast_history.groupby(['id_encode', 'ds']).head(1)
del train_df['hour']    # 删除字段 'hour'

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

'''groudby;将站点充电量数据表'id_encode''ds'组合作为标签,统计分组的 'power' 字段值的和'''
tmp_df = train_power.groupby(['id_encode', 'ds'])['power'].sum()
tmp_df.columns = ['id_encode', 'ds', 'power']   # 字段重命名

# 合并充电量数据
'''merge:以左参数train_df的'id_encode'-'ds'组合字段为索引行,与右参数tmp_df合并'''
train_df = train_df.merge(tmp_df, on=['id_encode','ds'], how='left')

# 合并站点静态数据
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')

 数据探索:可视化
# 选中要绘制的列
cols = ['power']

# 遍历id_encode的五个值
for ie in [0, 1, 2, 3, 4]:

    # 获取train_df中id_encode为当前值ie的所有行,并重置索引(不添新列)
    tmp_df = train_df[train_df['id_encode'] == ie].reset_index(drop=True)

    # 两次重置索引,第二次新添了 'index' 列
    tmp_df = tmp_df.reset_index(drop=True).reset_index()

    # 遍历要绘制的列
    '''enumerate:将一个可迭代对象重组为索引序列,列出数据及其下标,常用于for循环结构'''
    for num, col in enumerate(cols):    # num为按日期排序的索引,col为数值

        # 设置折线图的大小
        plt.figure(figsize=(20, 10))

        # 创建子图,将整个图像窗口分为4行1列,当前为第num+1个子图
        plt.subplot(2, 2, num+1)

        # 绘制折线图:x轴为 'index',y轴为当前列的值
        plt.plot(tmp_df['index'], tmp_df[col])

        # 子图标题设置为当前列的名称
        plt.title(col)

# 显示图形
plt.show()

        以上是0~5场站编码分组的充电量随时间变化的折线图。

数据清洗:数据变换
# 数据变换:对含义不明的二元特征,规范化处理成数值,便于模型计算
'''map:对选中列的所有数据进行检索,用定义的哈希表(字典)中的值覆盖相应的键'''
train_df['flag'] = train_df['flag'].map({'A': 0, 'B': 1})
test_df['flag'] = test_df['flag'].map({'A': 0, 'B': 1})

 特征工程:提取时序特征

# 定义提取时间戳的方法
def get_time_feature(df, col):
    # 浅拷贝原表,保护原数据
    df_copy = df.copy()
    # 统一格式:ds_
    prefix = col + "_"
    # 命名新字段,用于储存日期数据
    df_copy['new_' + col] = df_copy[col].astype(str)
    # 统一格式:new_ds
    col = 'new_' + col
    # 将MySQL类型日期数据转化为’年月日‘日期格式
    df_copy[col] = pd.to_datetime(df_copy[col], format='%Y%m%d')
    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       # 日
    # 星期:1=日,2=星期一,3=星期二,4=星期三,5=星期四,6=星期五,7=星期六
    df_copy[prefix + 'dayofweek'] = df_copy[col].dt.dayofweek
    df_copy[prefix + 'is_wknd'] = df_copy[col].dt.dayofweek // 6  # 周末判断:1=周末
    df_copy[prefix + 'quarter'] = df_copy[col].dt.quarter         # 季度:1~4
    # 月初判断:返回值为布尔值,再转化为int整数类型
    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')

# 选中测试集中'ds', 'power', 'h3'以外的所有字段
cols = [f for f in test_df.columns if f not in ['ds', 'power', 'h3']]

模型训练与验证

# 使用K折交叉验证训练和验证模型
'''定义模型训练方法,参数为:
所训模型,训练集中分割出的训练集、测试集,测试集中用于预测的数据,随机种子'''
def cv_model(clf, train_x, train_y, test_x, seed=2023):
    # 定义折数并初始化KFold
    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交叉验证:训练数据为train划分的两个数据集
    for i, (train_index, valid_index) in enumerate(kf.split(train_x, train_y)):
        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',
            'objective': 'regression',
            'metric': 'rmse',
            'min_child_weight': 5,
            'num_leaves': 2 ** 7,
            'lambda_l2': 10,
            'feature_fraction': 0.8,
            'bagging_fraction': 0.8,
            'bagging_freq': 4,
            'learning_rate': 0.1,
            'seed': 2023,
            'nthread': 16,
            'verbose': -1,
            # 'device':'gpu'
        }

        # 训练模型
        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])

提交结果

# 输出赛题提交格式的结果
test_df['power'] = lgb_test
test_df['power'] = test_df['power'].apply(lambda x: 0 if x < 0 else x)
test_df[['id_encode', 'ds', 'power']].to_csv('result.csv', index=False)

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值