4. 数据清洗与预处理:异常值检测(阈值法)、缺失值处理(插值法)、数据平滑(移动平均)
好,咱们进入实战环节的第四步。传感器数据拿到手,你千万别急着往数据库里塞。为什么?因为真实世界的数据,说白了就是一团乱麻。
我记得刚做第一个温室项目时,凌晨三点被报警电话吵醒,说温度飙到 80 度。我跑过去一看,传感器探头被蜘蛛网糊住了。数据是采集上来了,但全是垃圾。所以,数据清洗这一步,是咱们物联网工程师的必修课。
4.1 异常值检测:用阈值法给数据“体检”
异常值检测,说白了就是找出那些明显不对劲的数据。比如温室内温度正常是 15-35℃,突然蹦出个 80℃,那肯定有问题。
阈值法是最简单粗暴的方法。你根据物理常识或历史数据,设定一个上下限。超出这个范围的数据,直接标记为异常。
核心思路:
- 硬阈值:比如温度上限 45℃,下限 0℃。超出就报警。
- 软阈值:基于统计,比如均值 ± 3 倍标准差。超出这个范围,大概率是异常。
我在项目中遇到过一个问题:夏天中午,温室内温度确实会短暂超过 40℃。如果硬阈值设得太死,就会频繁误报。所以我建议,阈值要结合时间窗口来设定。比如连续 3 个采样点都超限,才判定为异常。
来看一段 Python 代码,很实用:
import pandas as pd
import numpy as np
# 假设 data 是包含 'temperature' 列的 DataFrame
def detect_outliers_threshold(data, column, lower=0, upper=45):
"""
阈值法检测异常值
"""
outliers = data[(data[column] < lower) | (data[column] > upper)]
return outliers
# 使用示例
df = pd.read_csv('sensor_data.csv')
bad_data = detect_outliers_threshold(df, 'temperature', lower=5, upper=40)
print(f"发现 {len(bad_data)} 个异常温度点")
我的小技巧:阈值不要拍脑袋定。先跑一周的历史数据,画出分布图,看看数据的自然边界在哪里。这样定出来的阈值才靠谱。
4.2 缺失值处理:用插值法“补坑”
传感器偶尔会掉线,或者网络波动导致数据没传上来。这时候数据就出现了“空洞”。你不能直接跳过,因为后续的时序分析需要连续的数据。
插值法,就是用已知数据去推算未知数据。常用的方法有几种:
| 方法 | 原理 | 适用场景 |
|---|---|---|
| 线性插值 | 用缺失点前后两个值的连线来推算 | 数据变化平缓,比如土壤湿度 |
| 多项式插值 | 用多个点拟合一条曲线来推算 | 数据有波动,但趋势明显 |
| 前向填充 | 用上一个有效值填充 | 传感器短暂离线,值变化不大 |
我个人习惯用线性插值。它简单,而且对于温室环境这种变化缓慢的数据,效果已经足够好。
我曾经犯过一个错:用多项式插值去补一个剧烈波动的数据点,结果补出来的值比真实值还离谱。后来我学乖了——插值不是万能的,缺失太多就直接丢弃那段数据。
# 线性插值示例
df['temperature'] = df['temperature'].interpolate(method='linear')
# 前向填充示例
df['humidity'] = df['humidity'].fillna(method='ffill')
注意:如果连续缺失超过 5 个点,插值的结果会严重失真。这时候我建议你直接标记为“数据不可用”,而不是强行补值。
4.3 数据平滑:用移动平均“去毛刺”
传感器数据里经常有高频噪声。比如风速传感器,因为一阵风吹过,数值会瞬间跳变。这些毛刺如果不处理,会触发很多误报警。
移动平均,就是用一个窗口内的平均值来代替当前值。窗口越大,曲线越平滑,但也会丢失细节。
你想想看,这就像你用手去抚摸一条曲线。手越宽,摸到的感觉越平滑,但那些小起伏你就感受不到了。
常用的移动平均有两种:
- 简单移动平均(SMA):窗口内所有值权重相同。
- 指数加权移动平均(EWMA):越近的数据权重越大,反应更灵敏。
嗯,这里要注意:移动平均会引入延迟。窗口越大,延迟越明显。如果你需要实时报警,窗口就不能设太大。
# 简单移动平均,窗口大小为 5
df['temp_smooth'] = df['temperature'].rolling(window=5).mean()
# 指数加权移动平均
df['temp_ewma'] = df['temperature'].ewm(span=5).mean()
实战建议:我一般先用移动平均做一次预处理,然后再用平滑后的数据去做异常检测。这样能减少很多误报。比如温度突然升高,但如果是平滑后的数据还在正常范围内,那就说明只是瞬时噪声,不用报警。
4.4 完整流程:从原始数据到干净数据
好了,咱们把这三个步骤串起来。一个典型的数据清洗流程是这样的:
- 读取原始数据,检查时间戳是否连续。
- 异常值检测,标记并剔除明显错误的数据。
- 缺失值处理,用插值法填补小范围的空洞。
- 数据平滑,用移动平均去除高频噪声。
- 输出干净数据,用于后续的报警逻辑分析。
我曾经在一个项目中,因为跳过了数据平滑这一步,导致系统一天误报了 200 多次。后来加上移动平均,误报率直接降到 0。所以,别嫌麻烦,这一步值得花时间。
def clean_sensor_data(df):
"""
完整的数据清洗流程
"""
# 1. 异常值检测:剔除温度超出 0-45℃ 的数据
df = df[(df['temperature'] >= 0) & (df['temperature'] <= 45)]
# 2. 缺失值处理:线性插值
df['temperature'] = df['temperature'].interpolate(method='linear')
# 3. 数据平滑:5点移动平均
df['temperature'] = df['temperature'].rolling(window=5, min_periods=1).mean()
return df
# 调用
clean_df = clean_sensor_data(df)
最后说一句:数据清洗没有银弹。每个温室的环境不同,传感器的特性也不同。你需要根据实际情况调整阈值、窗口大小和插值方法。多试几次,找到最适合你数据的那一套参数。
好,这一章就到这里。下一章咱们聊聊如何设计报警规则,让系统在真正出问题时才通知你。