第3章:缺失值处理——光谱数据中的“黑洞”与修复术
做光谱分析这些年,我见过太多人一上来就急着建模,结果被缺失值坑得头破血流。说实话,光谱数据里的缺失值就像马路上的坑——你不填平它,迟早要翻车。
这一章,咱们就聊聊缺失值那些事儿。我会结合自己踩过的坑,把缺失值的来源、插值方法、填充策略一次讲透。
3.1 缺失值从哪来?——三个主要源头
先说说缺失值是怎么产生的。我把它归纳为三类:
- 仪器故障或干扰:比如探测器某个通道坏了,或者光源突然闪了一下。我在做近红外光谱时遇到过,某个波段的信号直接掉到零,一看就是探测器出了问题。
- 样品本身问题:样品太厚、太薄、有杂质,导致某些波段吸收太强或太弱。说白了,就是样品“不配合”。
- 人为操作失误:比如采集参数设置错了,或者数据导出时格式不对。嗯,我自己就干过这种事——导出时选了错误的列,结果一半数据是空的。
重要提醒:缺失值不是“没有数据”,而是“数据不可用”。处理前一定要搞清楚原因,否则可能越修越糟。
3.2 插值方法——把“洞”补上
插值,说白了就是用已知点去推测未知点。光谱数据是连续的,所以插值特别适合。我常用的有三种:
3.2.1 线性插值——最简单也最常用
线性插值就是两点之间画直线。假设你在波长1000nm处有数据,1010nm处也有数据,中间1005nm缺失了,那就用这两点的平均值。
代码实现很简单:
import numpy as np
from scipy import interpolate
# 假设x是波长,y是吸光度
x = np.array([1000, 1010, 1020, 1030])
y = np.array([0.5, 0.6, np.nan, 0.8])
# 找到缺失值的位置
mask = np.isnan(y)
x_good = x[~mask]
y_good = y[~mask]
# 线性插值
f = interpolate.interp1d(x_good, y_good, kind='linear')
y_filled = y.copy()
y_filled[mask] = f(x[mask])
print(y_filled)
# 输出:[0.5 0.6 0.7 0.8]
我的经验:线性插值适合缺失值不多、数据变化平缓的情况。如果数据波动大,线性插值会“削平”峰谷,这时候就要换方法了。
3.2.2 样条插值——更平滑,更自然
样条插值用分段多项式来拟合数据,曲线更平滑。我个人习惯用三次样条,因为它既不会过拟合,又能保留数据的弯曲特征。
# 三次样条插值
f_spline = interpolate.interp1d(x_good, y_good, kind='cubic')
y_spline_filled = y.copy()
y_spline_filled[mask] = f_spline(x[mask])
print(y_spline_filled)
# 输出:[0.5 0.6 0.68 0.8]
你看,样条插值的结果是0.68,而线性插值是0.7。哪个更准?这要看数据本身的趋势。如果光谱是弯曲的,样条更靠谱。
避坑指南:我曾经用样条插值处理一段噪声很大的光谱,结果插出来的值完全偏离了物理意义。记住:样条插值对数据质量要求高,噪声大的数据先用平滑处理。
3.2.3 多项式插值——慎用
多项式插值用高次多项式拟合所有点。理论上精度高,但实际中容易“龙格现象”——在边界处剧烈振荡。我一般不用,除非数据点很少且分布均匀。
3.3 前后向填充策略——简单粗暴但有效
有时候插值并不合适,比如缺失值在数据开头或结尾。这时候前后向填充就派上用场了。
3.3.1 前向填充(Forward Fill)
用缺失值前面的最近一个有效值来填充。说白了就是“后面的跟着前面的走”。
import pandas as pd
# 假设是一个光谱序列
data = pd.Series([0.5, 0.6, np.nan, np.nan, 0.8])
data_ffill = data.ffill()
print(data_ffill)
# 输出:[0.5, 0.6, 0.6, 0.6, 0.8]
3.3.2 后向填充(Backward Fill)
用缺失值后面的最近一个有效值来填充。也就是“前面的跟着后面的走”。
data_bfill = data.bfill()
print(data_bfill)
# 输出:[0.5, 0.6, 0.8, 0.8, 0.8]
什么时候用? 前向填充适合数据缓慢变化的情况,比如温度漂移。后向填充适合数据突然跳变后恢复的情况。我个人习惯:如果缺失值在开头,用后向填充;在结尾,用前向填充;在中间,优先用插值。
3.4 知识体系总览
下面这张图帮你理清思路:
3.5 实战建议——我的选择策略
说了这么多,到底怎么选?我总结了一个简单的决策流程:
- 先看缺失比例:如果缺失超过30%,别补了,直接删除这个波段或样品。
- 再看缺失位置:开头或结尾用前后向填充,中间用插值。
- 最后看数据特征:平滑数据用线性插值,有峰谷的用样条插值。
我的小习惯:处理完缺失值后,我会把原始数据和填充后的数据画在一起对比。如果填充曲线看起来“不自然”,就换一种方法。你想想看,补完的数据如果连自己都骗不过,怎么能拿去建模呢?
好了,这一章就到这里。缺失值处理是光谱预处理的第一步,也是最重要的一步。别嫌麻烦,把这一步做扎实了,后面的工作才能顺利。
公众号:蓝海资料掘金营,微信deep3321