1.赛题理解
目标建立站点充电量预测模型,属于时间序列回归问题。
目前推测站点充电量很可能与收费情况、天气情况、站点所在的地理位置有关。
思路:树模型(决策树、lightgbm等提升树方法)或者时间序列神经网络(LSTM、GRU)
树模型优势:季节性这些其实可以通过月份的特征来捕获, 而周期性特点可以通过 weekday 来捕获, 而纯时间序列模型不能加特征的, 反而有较大局限性。
2.关于本次比赛的衡量指标
RMSE 是预测值P与实际值O之间的平均平方根误差,相对于MSE来说,RMSE在开完根号之后,误差的结果就和数据成为一个单位级别,可以更好的描述数据。
RSME对一组测量中对特大/特小误差反映特别敏感,由于这种局限性,要特别注意离群值和短时间内变化比较大的数据。
3.数据探索
3.1数据准备
3.1.1数据载入方法
1)结构化数据(表格)
常用pandas库读取和处理
train_power_forecast_history = pd.read_csv('./power_forecast_history.csv')
train_power = pd.read_csv('./train/power.csv')
train_stub_info = pd.read_csv('./train/stub_info.csv')
test_power_forecast_history = pd.read_csv('./power_forecast_history.csv')
test_stub_info = pd.read_csv('./stub_info.csv')
2)非结构化数据(这里不展开介绍)
图片:常配合应用opencv、Pillow、torch库
3.1.2数据简略观测与总览方法
1)head()函数
用于显示DataFrame或Series对象的前n行数据,只是用来查看,但不会改变原DataFrame和Series对象
import pandas as pd
data = {'A': [1, 2, 3, 4, 5],
'B': [6, 7, 8, 9, 10],
'C': [11, 12, 13, 14, 15]}
df = pd.DataFrame(data)
# 显示前1行数据
print(df.head(1))
2)shape
shape
函数是Python中NumPy库的一个函数,用于获取数组的形状(即维度)。它返回一个元组,表示数组在每个维度上的大小
import numpy as np
arr = np.array([[1, 2, 3], [4, 5, 6]])
shape = arr.shape
print(shape) # 输出:(2, 3)
3)相关统计量查看describe()
describe()
函数是Python中Pandas库的一个函数,用于生成描述性统计信息,包括计数、均值、标准差、最小值、25%分位数、中位数、75%分位数和最大值,可以帮助我们快速了解数据集的基本情况。
import pandas as pd
data = {'A': [1, 2, 3, 4, 5],
'B': [6, 7, 8, 9, 10],
'C': [11, 12, 13, 14, 15]}
df = pd.DataFrame(data)
desc = df.describe()
print(desc)
'''
输出结果
A B C
count 5.000000 5.0 5.0
mean 3.000000 8.0 13.0
std 1.581139 1.5 1.5
min 1.000000 6.0 11.0
25% 2.000000 7.0 12.0
50% 3.000000 8.0 13.0
75% 4.000000 9.0 14.0
max 5.000000 10.0 15.0
'''
4)数据类型查看info()
info()
函数是Python中Pandas库的一个方法,用于显示DataFrame或Series对象的基本信息,包括索引、数据类型、非空值数量等。
import pandas as pd
data = {'A': [1, 2, 3, 4, 5],
'B': [6, 7, 8, 9, 10],
'C': [11, 12, 13, 14, 15]}
df = pd.DataFrame(data)
df.info()
'''
输出结果
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 5 entries, 0 to 4
Data columns (total 3 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 A 5 non-null int64
1 B 5 non-null int64
2 C 5 non-null int64
dtypes: int64(3)
memory usage: 458.0+ bytes
'''
3.1.3数据合并
pandas库中常用的函数
1)groupby() 聚合函数
groupby()
函数是Python中Pandas库的一个方法,用于对数据进行分组。可以将数据集按照指定的列或条件进行分组,并对每个分组应用聚合函数(如求和、计数、平均值等)。
import pandas as pd
data = {'A': ['foo', 'bar', 'baz', 'foo', 'bar', 'baz'],
'B': ['one', 'two', 'three', 'two', 'three', 'one'],
'C': [1, 2, 3, 4, 5, 6],
'D': [10, 20, 30, 40, 50, 60]}
df = pd.DataFrame(data)
#按先B后A进行类型分组
grouped = df.groupby(['B','A'])
#计算每个分组的'C'列的平均值
result1 = grouped['C'].mean()
#等价于 result1 = df.groupby(['B','A'])['C'].mean()
#计算每个分组的行数
result2 = grouped.size()
print(result1)
print(result2)
'''
输出结果result1
B A
one baz 6
foo 1
three bar 5
baz 3
two bar 2
foo 4
Name: C, dtype: int64
输出结果result2
B A
one baz 1
foo 1
three bar 1
baz 1
two bar 1
foo 1
dtype: int64
'''
2)merge()函数
merge()
函数是Python中pandas库的一个函数,用于将两个DataFrame对象按照指定的列进行合并。
示例:
import pandas as pd
data1 = {'key': ['A', 'B', 'C', 'D'],
'value': [1, 2, 3, 4]}
df1 = pd.DataFrame(data1)
data2 = {'key': ['B', 'D', 'E', 'F'],
'value': [5, 6, 7, 8]}
df2 = pd.DataFrame(data2)
merged_df = pd.merge(df1, df2, on='key', how='inner')
'''
输出结果
key value_x value_y
0 B 2 5
1 D 4 6
'''
基本语法:
pd.merge(dataframe1, dataframe1, how='left', on=None, left_on=None, right_on=None)
on
参数指定要合并的列名,如果你要基于多列进行合并,可以传入一个列表;而left_on
和right_on
则是分别对应左右两个DataFrame中用于合并的列名。how
参数指定合并类型,可以是'left'(左连接)、'right'(右连接)、'outer'(全外连接)或'inner'(内连接)
内连接inner:在合并DataFrame对象时,内连接会保留两个表中都有的键值对,并返回这些键值对组成的新DataFrame对象。如果某个键在其中一个表中不存在,则该键对应的行将不会出现在新的结果集中。
左连接left:是指在合并后的结果中,保留左侧DataFrame的所有行,以及与右侧DataFrame匹配的行。如果右侧DataFrame中没有匹配的行,则结果中的对应位置为NaN。
import pandas as pd
# 创建两个DataFrame对象
df1 = pd.DataFrame({'key': ['A', 'B', 'C', 'D'],
'value': [1, 2, 3, 4]})
df2 = pd.DataFrame({'key': ['B', 'D', 'E', 'F'],
'value': [5, 6, 7, 8]})
# 使用merge函数进行左连接
merged_df = pd.merge(df1, df2, on='key', how='left')
print(merged_df)
#使用merge函数进行左连接的另一种写法
merged_df = df1.merge(df2, on='key', how='left')
print(merged_df)
'''
输出结果
key value_x value_y
0 A 1 NaN
1 B 2 5
2 C 3 NaN
3 D 4 6
'''
右连接right:在合并后的结果中,保留右侧DataFrame的所有行,以及与左侧DataFrame匹配的行。如果左侧DataFrame中没有匹配的行,则结果中的对应位置为NaN。
全外连接outer:在合并后的结果中,保留左侧DataFrame和右侧DataFrame的所有行,如果某一侧没有匹配的行,则结果中的对应位置为NaN。
3.2数据探索
时间序列预测的数据探索更多的是观测历史数据的分布情况,从分布中分析是否存在周期性、趋势性、相关性和异常性。所以可以采取时间序列数据可视化的方法。
4.特征工程
特征工程指的是把原始数据转变为模型训练数据的过程,目的是获取更好的训练数据特征。特征工程能使得模型的性能得到提升,有时甚至在简单的模型上也能取得不错的效果。
4.1数据清洗
4.1.1数据清洗作用
数据清洗的作用是利用有关技术如数理统计、数据挖掘或预定义的清理规则将脏数据转化为满足数据质量要求的数据。主要包括缺失值处理、异常值处理、重复值处理、数据类型转换、特征归一化/标准化等流程。
4.1.2数据清洗的常用方法
1)重复值的捕捉处理
2)异常值的捕捉处理
3)缺失值的捕捉处理
4)特征编码
将类别特征进行顺序编码或者独热编码。
5)特征归一化/标准化
归一化是将样本的特征值转换到同一量纲下,也就是把数据映射到固定的区间内,通常是 [0, 1]。常用Min-Max Scaling法。
标准化则是按照特征矩阵的列处理数据,通过Z-score方法,将样本特征值转换为标准正态分布,其值域范围通常不会很大。
4.2特征提取
对于结构化数据来说,在经过有效的特征预处理之后,使用现有的机器学习模型就能够达到比较好的精度了。但是特征预处理步骤只是将特征进行了有效的编码,并没有提取新的特征。还是要从业务逻辑上提取一些新的特征,继续挖掘在这个步骤没有特定规则,需要具体问题具体分析,还要结合数据分析来完成。总体来提取的新特征应该满足如下要求:
1.新特征应该是有效的,有业务背景的意义的;
2.新特征不应该加入很多噪音;
4.3特征筛选
4.2.1过滤式Filter
互信息法、Pearson系数、方差选择
4.2.2包裹式Wrapper
完全搜索、随机搜索、递归特征消除法、向前/向后选择
4.2.3嵌入式Embedded
Lasso法
4.4降维
PCA、LDA、LCA
4.5特征构建
1)特征间的加减乘除
2)特征的指数、对数变换
3)特征的交叉组合
4.6特征优化
历史平移特征、差分特征、窗口统计特征
参考代码
# 合并训练数据和测试数据
df = pd.concat([train_df, test_df], axis=0).reset_index(drop=True)
# 历史平移
for i in range(7,36):
df[f'power_shift{i}'] = df.groupby('id_encode')['power'].shift(i)
# 历史平移 + 差分特征
for i in range(1,4):
df[f'power_shift7_diff{i}'] = df.groupby('id_encode')['power_shift7'].diff(i)
# 窗口统计
for win in [7,14,28,35,50,70]:
df[f'power_win{win}_mean'] = df.groupby('id_encode')['power'].rolling(window=win, min_periods=3, closed='left').mean().values
df[f'power_win{win}_max'] = df.groupby('id_encode')['power'].rolling(window=win, min_periods=3, closed='left').max().values
df[f'power_win{win}_min'] = df.groupby('id_encode')['power'].rolling(window=win, min_periods=3, closed='left').min().values
df[f'power_win{win}_std'] = df.groupby('id_encode')['power'].rolling(window=win, min_periods=3, closed='left').std().values
# 历史平移 + 窗口统计
for win in [7,14,28,35,50,70]:
df[f'power_shift7_win{win}_mean'] = df.groupby('id_encode')['power_shift7'].rolling(window=win, min_periods=3, closed='left').mean().values
df[f'power_shift7_win{win}_max'] = df.groupby('id_encode')['power_shift7'].rolling(window=win, min_periods=3, closed='left').max().values
df[f'power_shift7_win{win}_min'] = df.groupby('id_encode')['power_shift7'].rolling(window=win, min_periods=3, closed='left').min().values
df[f'power_shift7_win{win}_sum'] = df.groupby('id_encode')['power_shift7'].rolling(window=win, min_periods=3, closed='left').sum().values
df[f'power_shift7_win{win}_std'] = df.groupby('id_encode')['power_shift7'].rolling(window=win, min_periods=3, closed='left').std().values