DataWhale ML Day2 笔记——by Mr_Darcy

引入

在上期的实践尝试阶段,我们依据鱼佬给出的baseline,使用了lightgbm完成了基本的模型训练,并跑通了代码。在进阶实践部分,将在原有Baseline基础上做进阶代码,进阶版本代码。具体地,我们可以尝试提取更多特征改善最终结果,这也是数据挖掘比赛中的主要优化方向,很多情况下决定着最终的成绩

1.进阶建议

在新的一期教学中,主要给出了以下4种方案来提高成绩。主要是构建交叉特征、历史平移特征、差分特征、和窗口统计特征;每种特征具体说明如下:

(1)交叉特征:主要提取流量、上部温度设定、下部温度设定之间的关系;

(2)历史平移特征:通过历史平移获取上个阶段的信息;

(3)差分特征:可以帮助获取相邻阶段的增长差异,描述数据的涨减变化情况。在此基础上还可以构建相邻数据比值变化、二阶差分等;

(4)窗口统计特征:窗口统计可以构建不同的窗口大小,然后基于窗口范围进统计均值、最大值、最小值、中位数、方差的信息,可以反映最近阶段数据的变化情况。

2.代码实现和解读

python 复制代码
# 交叉特征
for i in range(1,18):
    train[f'流量{i}/上部温度设定{i}'] = train[f'流量{i}'] / train[f'上部温度设定{i}']
    test[f'流量{i}/上部温度设定{i}'] = test[f'流量{i}'] / test[f'上部温度设定{i}']
    
    train[f'流量{i}/下部温度设定{i}'] = train[f'流量{i}'] / train[f'下部温度设定{i}']
    test[f'流量{i}/下部温度设定{i}'] = test[f'流量{i}'] / test[f'下部温度设定{i}']
    
    train[f'上部温度设定{i}/下部温度设定{i}'] = train[f'上部温度设定{i}'] / train[f'下部温度设定{i}']
    test[f'上部温度设定{i}/下部温度设定{i}'] = test[f'上部温度设定{i}'] / test[f'下部温度设定{i}']

这部分代码为数据集(训练集和测试集)添加交叉特征。交叉特征通常是原有特征的组合,以此来创造新的特征,提高模型的表现。该代码创建了以下特征:

  • 流量i/上部温度设定i: 表示流量i除以上部温度设定i的值。
  • 流量i/下部温度设定i: 表示流量i除以下部温度设定i的值。
  • 上部温度设定i/下部温度设定i: 表示上部温度设定i除以下部温度设定i的值。
python 复制代码
# 历史平移
for i in range(1,18):
    train[f'last1_流量{i}'] = train[f'流量{i}'].shift(1)
    train[f'last1_上部温度设定{i}'] = train[f'上部温度设定{i}'].shift(1)
    train[f'last1_下部温度设定{i}'] = train[f'下部温度设定{i}'].shift(1)
    
    test[f'last1_流量{i}'] = test[f'流量{i}'].shift(1)
    test[f'last1_上部温度设定{i}'] = test[f'上部温度设定{i}'].shift(1)
    test[f'last1_下部温度设定{i}'] = test[f'下部温度设定{i}'].shift(1)

这部分代码通过.shift(1)实现特征的历史平移。这在时间序列数据中很常见,用于获得之前时间点的值。

例如,last1_流量i表示流量i在上一个时间点的值。

python 复制代码
# 差分特征
for i in range(1,18):
    train[f'last1_diff_流量{i}'] = train[f'流量{i}'].diff(1)
    train[f'last1_diff_上部温度设定{i}'] = train[f'上部温度设定{i}'].diff(1)
    train[f'last1_diff_下部温度设定{i}'] = train[f'下部温度设定{i}'].diff(1)
    
    test[f'last1_diff_流量{i}'] = test[f'流量{i}'].diff(1)
    test[f'last1_diff_上部温度设定{i}'] = test[f'上部温度设定{i}'].diff(1)
    test[f'last1_diff_下部温度设定{i}'] = test[f'下部温度设定{i}'].diff(1)

差分是时间序列分析中常见的操作,用于获得序列的一阶差异。.diff(1)实现了这个操作。

例如,last1_diff_流量i表示流量i与上一个时间点流量i的差值。

python 复制代码
# 窗口统计
for i in range(1,18):
   train[f'win3_mean_流量{i}'] = (train[f'流量{i}'].shift(1) + train[f'流量{i}'].shift(2) + train[f'流量{i}'].shift(3)) / 3
   train[f'win3_mean_上部温度设定{i}'] = (train[f'上部温度设定{i}'].shift(1) + train[f'上部温度设定{i}'].shift(2) + train[f'上部温度设定{i}'].shift(3)) / 3
   train[f'win3_mean_下部温度设定{i}'] = (train[f'下部温度设定{i}'].shift(1) + train[f'下部温度设定{i}'].shift(2) + train[f'下部温度设定{i}'].shift(3)) / 3
   
   test[f'win3_mean_流量{i}'] = (test[f'流量{i}'].shift(1) + test[f'流量{i}'].shift(2) + test[f'流量{i}'].shift(3)) / 3
   test[f'win3_mean_上部温度设定{i}'] = (test[f'上部温度设定{i}'].shift(1) + test[f'上部温度设定{i}'].shift(2) + test[f'上部温度设定{i}'].shift(3)) / 3
   test[f'win3_mean_下部温度设定{i}'] = (test[f'下部温度设定{i}'].shift(1) + test[f'下部温度设定{i}'].shift(2) + test[f'下部温度设定{i}'].shift(3)) / 3

这部分代码通过.shift()函数来计算3个时间点的滑动平均值,这是时间序列分析中的另一个常见操作。

例如,win3_mean_流量i表示流量i的3个时间点(包括当前时间点之前的两个时间点和自身)的平均值。

3. 个人思考

事实上,baseline+4项进阶对我来说已经是大神般的操作了。我本就抱着学习的心态去拜读这些代码,又怎能对其进行进一步的修改和提升呢?

所以我决定将重心放到前期的数据清洗上

是的,你会发现大佬的模型和参数都已经很不错了,但是他直接拿源数据进行了训练。我们在数据预处理上还有优化空间。

4. 数据清洗

数据清洗是确保数据质量的关键步骤。在开始清洗之前,我们首先需要对数据进行探索性数据分析 (EDA) 以识别可能的问题。这通常包括以下几个步骤:

  1. 缺失值检查: 查看数据中是否存在缺失值,以及这些缺失值的分布。
  2. 异常值检查: 使用统计方法(例如 IQR、Z-Score)或数据可视化来识别可能的异常值。
  3. 数据分布检查: 检查每个特征的数据分布,以识别是否存在偏斜或其他不寻常的分布。
  4. 重复值检查: 确保数据中没有重复的行。
  5. 一致性检查: 确保数据中的相关字段是一致的。

4.1. 缺失值统计

python 复制代码
#检查数据缺失值
missing_values = train_df.isnull().sum()
missing_values = missing_values[missing_values > 0]  # Filtering out features with no missing values
missing_values_percentage = (missing_values / len(train_df)) * 100  # Calculate the percentage of missing values

missing_info = pd.DataFrame({
    "Missing Values": missing_values,
    "Percentage": missing_values_percentage
}).sort_values(by="Percentage", ascending=False)

missing_info

在官方提供的 train.csv 数据中没有缺失值。那我们不需要进行缺失值的填充或删除操作了。看来白忙活了一趟。

4.2. 异常值处理

我们可以使用箱线图或其他统计方法来检查每个特征的异常值。

由于数据集中的特征数量可能较多,我们可以选择一些关键特征,或者先查看流量和温度数据的描述性统计来识别可能的异常值。

我们可以先对数据进行描述性统计,以便更好地了解哪些特征可能有异常值。

一种常用的方法是查看每个特征的最小值、最大值、均值、中位数以及标准差。当最小值或最大值与均值和中位数差异很大时,我们可以怀疑存在异常值。

那么接下来我们首先为数据集中的特征生成描述性统计。

python 复制代码
#存储描述性数据
descriptive_stats = train_df.describe().transpose()

#展示可能出现异常值的数据
potential_outliers = descriptive_stats[
    ((descriptive_stats['max'] - descriptive_stats['75%']) > 1.5 * (descriptive_stats['75%'] - descriptive_stats['25%'])) |
    ((descriptive_stats['25%'] - descriptive_stats['min']) > 1.5 * (descriptive_stats['75%'] - descriptive_stats['25%']))
]

potential_outliers

结果大致如下:

result 复制代码
          count        mean         std    min         25%         50%  \
流量1     26656.0   30.789741    8.749196    0.0   25.069000   32.563499   
流量2     26656.0   31.157351    9.553690    0.0   24.684000   34.664001   
流量3     26656.0   31.659505    9.724089    0.0   24.226999   32.250999   
流量4     26656.0   27.460194    7.151646    0.0   22.273750   27.118000   
流量5     26656.0   28.550216    7.496923    0.0   24.791000   28.461000   
...         ...         ...         ...    ...         ...         ...   
下部温度13  26656.0  851.670581   94.510100  348.0  829.000000  837.000000   
下部温度14  26656.0  850.215186  101.237117  297.0  829.000000  837.000000   
下部温度15  26656.0  848.684011  108.438835  246.0  829.000000  837.000000   
下部温度16  26656.0  844.247074  114.867509  196.0  829.000000  837.000000   
下部温度17  26656.0  763.231318  110.615706  145.0  750.000000  758.000000   

               75%         max  
流量1      35.558001   79.261002  
流量2      38.547001   79.261002  
流量3      39.324001   70.323998  
流量4      31.002001   65.071999  
流量5      32.597000   70.435997  
...            ...         ...  
下部温度13  931.000000  945.000000  
下部温度14  931.000000  946.000000  
下部温度15  931.000000  947.000000  
下部温度16  931.000000  947.000000  
下部温度17  850.000000  854.000000  

[63 rows x 8 columns]

这样看来,以下特征可能会出现异常值:

  • 进气流量:如 流量1流量2
  • 加热棒上下部的设定温度和测量温度:如 下部温度13下部温度14

用箱线图可以很好地看出具体的问题:

python 复制代码
import matplotlib.pyplot as plt
import seaborn as sns

# 选择目标
selected_features = ['流量1', '流量2', '流量3', '下部温度13', '下部温度14', '下部温度15']

# Plotting boxplots for the selected features
plt.figure(figsize=(15, 10))
for i, feature in enumerate(selected_features, 1):
    plt.subplot(2, 3, i)
    sns.boxplot(y=train_df[feature])
    plt.title(f'Boxplot for {feature}')
    plt.ylabel(feature)
    plt.tight_layout()

plt.show()

看来是需要进行异常值处理的

python 复制代码
import numpy as np

# 定义数据清洗函数
def handle_outliers(data, column):
    # 计算特征值(上下四分位值和IQR)
    Q1 = data[column].quantile(0.25)
    Q3 = data[column].quantile(0.75)
    IQR = Q3 - Q1
    
    # 计算上下限值
    lower_bound = Q1 - 1.5 * IQR
    upper_bound = Q3 + 1.5 * IQR
    
    # 替换数据
    data[column] = np.where(data[column] < lower_bound, lower_bound, data[column])
    data[column] = np.where(data[column] > upper_bound, upper_bound, data[column])
    
    return data

# 调用函数处理数据
for feature in selected_features:
    train_df = handle_outliers(train_df, feature)

# 展示清洗后数据
plt.figure(figsize=(15, 10))
for i, (feature, eng_feature) in enumerate(zip(selected_features, selected_features_english), 1):
    plt.subplot(2, 3, i)
    sns.boxplot(y=train_df[feature])
    plt.title(f'Boxplot for {eng_feature} (After Outlier Handling)')
    plt.ylabel(eng_feature)
    plt.tight_layout()

plt.show()

由此,异常值已经全部被平滑掉了。采用上下限替换的方式来"平滑"异常值对于树型模型(例如随机森林、决策树或梯度提升机)的影响较小,因为这些模型天然对异常值具有较好的鲁棒性。但对于其他模型(例如线性回归或神经网络),异常值可能会产生更大的影响。

总结

清洗是非常重要的一环,这样做之后的很多操作才有意义(比如差分等)。不过今天我的提交次数已经用完了(狗头)。到时候再来分享成绩吧~

相关推荐
秋夫人20 分钟前
B+树(B+TREE)索引
数据结构·算法
梦想科研社1 小时前
【无人机设计与控制】四旋翼无人机俯仰姿态保持模糊PID控制(带说明报告)
开发语言·算法·数学建模·matlab·无人机
Milo_K1 小时前
今日 leetCode 15.三数之和
算法·leetcode
Darling_001 小时前
LeetCode_sql_day28(1767.寻找没有被执行的任务对)
sql·算法·leetcode
AlexMercer10121 小时前
【C++】二、数据类型 (同C)
c语言·开发语言·数据结构·c++·笔记·算法
Greyplayground1 小时前
【算法基础实验】图论-BellmanFord最短路径
算法·图论·最短路径
蓑 羽1 小时前
力扣438 找到字符串中所有字母异位词 Java版本
java·算法·leetcode
源代码:趴菜1 小时前
LeetCode63:不同路径II
算法·leetcode·职场和发展
儿创社ErChaungClub1 小时前
解锁编程新境界:GitHub Copilot 让效率翻倍
人工智能·算法
前端西瓜哥1 小时前
贝塞尔曲线算法:求贝塞尔曲线和直线的交点
前端·算法