数据探索与可视化(EDA):加载C-MAPSS数据集
做剩余寿命预测,第一步不是调模型,而是先看看数据长什么样。我见过不少新手,拿到数据集就急着跑LSTM,结果折腾半天效果奇差——回头一看,数据本身就有问题。所以,咱们先沉下心来,把C-MAPSS数据集摸透。
加载C-MAPSS数据集
C-MAPSS数据集来自NASA,模拟了航空发动机从健康到失效的全过程。它包含四个子集:FD001、FD002、FD003、FD004。每个子集又分训练集和测试集。
我个人习惯用Pandas直接读取。文件是文本格式,没有表头,需要手动指定列名。
import pandas as pd
import numpy as np
# 列名定义
columns = ['engine_id', 'cycle', 'op_setting_1', 'op_setting_2', 'op_setting_3'] + \
[f'sensor_{i}' for i in range(1, 22)]
# 加载训练集
train_df = pd.read_csv('train_FD001.txt', sep=' ', header=None, names=columns)
# 去掉末尾的空列(文件里有多余空格)
train_df = train_df.dropna(axis=1, how='all')
print(train_df.head())
print(train_df.shape)
嗯,这里要注意:文件里每行末尾有多余的空格,读进来会多出空列。用dropna(axis=1, how='all')直接干掉就行。我在项目中遇到过类似情况,当时没注意,结果后面算特征时全乱了。
数据清洗与缺失值处理
加载完数据,第一件事就是检查缺失值。C-MAPSS数据集本身比较干净,但工业现场的数据可没这么友好。
# 检查缺失值
missing = train_df.isnull().sum()
print(missing[missing > 0])
如果发现缺失值,怎么处理?我一般分情况讨论:
- 传感器数据缺失:用前向填充(ffill)或线性插值。发动机运行是连续的,前一时刻的值很有参考价值。
- 运行设置缺失:这个比较棘手。我曾经遇到过一个案例,某台发动机的运行设置全为空,后来发现是传感器故障。这种直接剔除该发动机的数据。
# 前向填充示例
train_df = train_df.fillna(method='ffill')
# 或者线性插值
train_df = train_df.interpolate(method='linear', limit_direction='forward')
传感器信号可视化
数据洗干净了,接下来就是看信号。21个传感器,不是每个都有用。我习惯先挑几个典型的画出来看看。
import matplotlib.pyplot as plt
# 选一台发动机
engine_1 = train_df[train_df['engine_id'] == 1]
# 绘制几个关键传感器
sensors_to_plot = ['sensor_2', 'sensor_3', 'sensor_4', 'sensor_7', 'sensor_11', 'sensor_15']
plt.figure(figsize=(12, 8))
for i, sensor in enumerate(sensors_to_plot, 1):
plt.subplot(2, 3, i)
plt.plot(engine_1['cycle'], engine_1[sensor])
plt.title(sensor)
plt.xlabel('Cycle')
plt.ylabel('Value')
plt.tight_layout()
plt.show()
你想想看,这些传感器信号有什么规律?有的传感器(比如sensor_2)随着运行周期增加,趋势很明显——要么上升,要么下降。这种就是好特征。有的传感器(比如sensor_3)基本是一条直线,没什么变化,那它对预测就没啥贡献。
运行周期分布分析
接下来看看每台发动机能跑多久。这个分布直接决定了我们的预测任务难度。
# 计算每台发动机的最大周期(即寿命)
life_span = train_df.groupby('engine_id')['cycle'].max().reset_index()
life_span.columns = ['engine_id', 'max_cycle']
# 绘制直方图
plt.figure(figsize=(10, 6))
plt.hist(life_span['max_cycle'], bins=30, edgecolor='black', alpha=0.7)
plt.xlabel('Max Cycle (Life Span)')
plt.ylabel('Number of Engines')
plt.title('Distribution of Engine Life Spans in FD001')
plt.grid(True, alpha=0.3)
plt.show()
# 统计信息
print(life_span['max_cycle'].describe())
结果很有意思:FD001中发动机寿命最短的只有几十个周期,最长的能到300多个周期。分布大致呈右偏态,大部分发动机集中在150-250周期之间。
| 统计量 | 值 |
|---|---|
| 发动机数量 | 100 |
| 最短寿命 | 128 周期 |
| 最长寿命 | 362 周期 |
| 平均寿命 | 206 周期 |
| 标准差 | 46 周期 |
知识体系总览
说了这么多,咱们用一张图把EDA的核心逻辑串起来:
这张图把EDA的流程串起来了。从加载数据开始,到清洗、可视化、分布分析,最后筛选出有效特征并构建标签。每一步都环环相扣。
说白了,EDA不是走形式。它是帮你建立对数据的直觉。我做了这么多年工业AI,发现一个规律:EDA做得越扎实,后面建模就越顺。那些上来就调模型的人,往往会在数据上栽跟头。
好了,数据探索这部分就到这儿。数据准备好了,下一步就是构建训练样本和标签了。