第四章:数据读取与预处理——文件格式解析与清洗实战
各位工程师朋友,大家好。今天我们来聊聊MOCVD数据分析中最基础、也最容易被忽视的一环——数据读取与预处理。
说实话,我见过太多人一上来就急着建模、画图,结果数据本身就有问题。嗯,这就像盖房子不打地基,迟早要塌。我自己在早期做MOCVD工艺开发时,就吃过这个亏。有一次花了整整一周分析反射率曲线,最后发现是原始数据里混入了几个异常点,导致整个结论都是错的。从那以后,我养成了一个习惯:拿到数据,先花30%的时间做预处理。
4.1 文件格式解析:CSV/Excel/TXT
MOCVD原位监测的数据来源五花八门。有的设备输出CSV,有的用Excel,还有的老设备直接给TXT。我个人的习惯是,先统一转成CSV格式再处理。为什么?因为CSV轻量、跨平台、Python读取最快。
咱们直接看代码。这是我最常用的读取方式:
import pandas as pd
import numpy as np
# 读取CSV——最常见的情况
df_csv = pd.read_csv('mocvd_data.csv', encoding='utf-8')
# 读取Excel——注意sheet_name参数
df_excel = pd.read_excel('mocvd_data.xlsx', sheet_name='Sheet1')
# 读取TXT——有时候分隔符是空格或制表符
df_txt = pd.read_csv('mocvd_data.txt', sep='\t', header=None)
这里有个小坑。我在项目中遇到过,有些老设备的TXT文件第一行是注释,不是列名。这时候要加个skiprows=1参数跳过它。你想想看,如果不跳过,第一行数据就被当成列名了,后面全乱套。
df.head() 看一眼前5行。这能帮你快速确认数据格式对不对。
4.2 缺失值处理——别急着删
缺失值在MOCVD数据里太常见了。比如温度传感器偶尔掉线,或者反射率信号在切换波长时出现空白。很多人一看到NaN就删掉整行,我建议你先想想:这个缺失值是怎么来的?
我一般分三种情况处理:
- 随机缺失:比如传感器偶尔丢包。可以用前后均值填充。
- 系统性缺失:比如某个工艺步骤不采集数据。这时候填充反而会引入偏差,我建议保留NaN。
- 关键参数缺失:比如生长速率数据缺失。嗯,这种情况我倾向于用插值法。
看代码:
# 检查缺失值
print(df.isnull().sum())
# 方法1:删除缺失值过多的列(比如缺失超过50%)
df_clean = df.dropna(thresh=len(df)*0.5, axis=1)
# 方法2:用前后均值填充(适合连续数据)
df['temperature'] = df['temperature'].fillna(method='ffill').fillna(method='bfill')
# 方法3:线性插值(我最推荐的方法)
df['reflectance'] = df['reflectance'].interpolate(method='linear')
4.3 异常值检测——别让一个点毁了整条曲线
MOCVD数据里的异常值,说白了就是那些明显偏离正常范围的点。比如反射率突然跳到1.5以上(正常范围0-1),或者温度瞬间变化超过50°C。这些点往往是传感器故障或者信号干扰造成的。
我常用的检测方法有三种:
| 方法 | 适用场景 | 我的经验 |
|---|---|---|
| Z-score法 | 数据近似正态分布 | 适合温度、压力这类参数 |
| IQR四分位法 | 数据分布偏态 | 适合反射率、生长速率 |
| 移动窗口法 | 时间序列数据 | 我最常用,能捕捉局部异常 |
代码实现:
from scipy import stats
# Z-score法——适合正态分布数据
z_scores = np.abs(stats.zscore(df['temperature']))
df_no_outlier = df[(z_scores < 3)] # 保留Z-score小于3的数据
# IQR法——适合偏态分布
Q1 = df['reflectance'].quantile(0.25)
Q3 = df['reflectance'].quantile(0.75)
IQR = Q3 - Q1
df_no_outlier = df[(df['reflectance'] >= Q1 - 1.5*IQR) &
(df['reflectance'] <= Q3 + 1.5*IQR)]
# 移动窗口法——我最推荐的方法
window_size = 10
rolling_mean = df['reflectance'].rolling(window=window_size).mean()
rolling_std = df['reflectance'].rolling(window=window_size).std()
df_no_outlier = df[np.abs(df['reflectance'] - rolling_mean) <= 3*rolling_std]
4.4 数据标准化——让不同量纲的参数能放在一起比较
MOCVD数据里,温度是几百到一千多度,反射率是0到1,生长速率是纳米/分钟。量纲完全不同。如果你直接拿这些数据做分析,温度会主导一切,反射率几乎没影响。这显然不合理。
标准化的目的,说白了就是把所有参数拉到同一个尺度上。我常用的有两种:
- Z-score标准化:减去均值,除以标准差。结果均值为0,标准差为1。适合数据分布比较对称的情况。
- Min-Max归一化:缩放到[0,1]区间。适合有明确上下限的参数,比如反射率。
代码:
from sklearn.preprocessing import StandardScaler, MinMaxScaler
# Z-score标准化——我最常用
scaler = StandardScaler()
df_scaled = pd.DataFrame(
scaler.fit_transform(df[['temperature', 'reflectance', 'growth_rate']]),
columns=['temperature', 'reflectance', 'growth_rate']
)
# Min-Max归一化——适合有物理边界的参数
scaler = MinMaxScaler()
df_normalized = pd.DataFrame(
scaler.fit_transform(df[['temperature', 'reflectance', 'growth_rate']]),
columns=['temperature', 'reflectance', 'growth_rate']
)
知识体系总览
下面这张图是我自己总结的数据预处理流程。每次拿到新数据,我就按这个步骤走一遍,基本不会出大问题。
好了,以上就是数据读取与预处理的全部内容。说实话,这些步骤看起来简单,但真正做好并不容易。我自己的经验是:多花时间在预处理上,后面分析建模会省很多事。记住一句话——垃圾进,垃圾出。数据质量决定了分析结果的上限。