数据采集与预处理:数据源类型、数据清洗方法、缺失值处理、异常值初步过滤

大家好,我是老张。做告警阈值设定这么多年,我最大的体会是:阈值设定好不好,七分靠数据,三分靠算法。你想想看,如果喂给模型的数据本身就是脏的、乱的、缺胳膊少腿的,那后面再牛的算法也白搭。

这一章,咱们就聊聊数据采集和预处理。说白了,就是怎么把原始数据收拾干净,让它能干活。

2.1 数据源类型:你的数据从哪来?

我见过不少团队,一上来就急着调阈值,结果发现数据源都没搞清楚。常见的监控数据源,我归纳为三类:

数据源类型 典型代表 特点 我踩过的坑
指标型 CPU使用率、内存、QPS、延迟 数值连续、周期性强、噪声相对少 曾经有个业务,QPS 白天晚上差 10 倍,没做周期分解直接设阈值,误报率飙到 60%
日志型 错误日志、访问日志、系统日志 文本非结构化、突发性强、量巨大 有一次日志采集器挂了三天,恢复后数据回填,把整个异常检测模型搞崩了
事件型 变更事件、发布事件、告警事件 离散、稀疏、上下文关联强 发布事件和告警时间戳对不上,排查了半天发现是时区没统一

我个人习惯,拿到数据第一件事不是看数值,而是先搞清楚数据源的类型和采集方式。是 agent 采集的?是 API 拉取的?还是日志文件 tail 出来的?采集频率是多少?这些信息,直接决定了后面怎么清洗。

一个小技巧: 建议在数据采集层就加上元数据标签。比如数据源 ID、采集时间戳、采集节点、数据版本。别小看这几个字段,出问题排查时能救命。

2.2 数据清洗方法:把脏数据筛出去

数据清洗,说白了就是「去伪存真」。我把它拆成四个步骤,每一步都有讲究。

2.2.1 格式统一

不同数据源,时间格式可能不一样。有的用 2024-01-15 10:30:00,有的用 2024/01/15 10:30:00,还有的用时间戳。不统一的话,后面做时间序列分析直接报错。

# 我常用的时间格式统一方法
import pandas as pd

# 强制转换为统一格式
df['timestamp'] = pd.to_datetime(df['timestamp'], errors='coerce')

# 转换失败的行会被设为 NaT,后面统一丢弃
df = df.dropna(subset=['timestamp'])

2.2.2 去重

重复数据在监控场景里很常见。网络抖动、采集重试、数据回填,都可能导致同一条数据被采集多次。

嗯,这里要注意:去重不能简单粗暴地按时间戳去重。我遇到过一种情况,同一秒内确实有两条不同的指标值,都是合法的。所以我的做法是:按「时间戳 + 指标名 + 数据源ID」联合去重。

2.2.3 噪声过滤

什么是噪声?就是那些明显不合理的数据。比如 CPU 使用率突然变成 -5%,或者内存占用突然跳到 1000%。

我一般用两种方法:

  • 范围过滤: 设定物理上下界。CPU 只能在 0~100% 之间,延迟不能为负数。
  • 变化率过滤: 相邻两个点的变化率超过某个阈值(比如 10 倍),就标记为可疑。
注意: 变化率过滤要小心。业务突发流量时,QPS 从 100 跳到 1000 是正常的。所以变化率阈值要根据业务场景动态调整,不能一刀切。

2.3 缺失值处理:数据缺了怎么办?

数据缺失,在运维场景里太常见了。采集器挂了、网络断了、存储满了,都可能导致数据断档。处理缺失值,我总结了三种策略:

策略 适用场景 方法 风险
直接丢弃 缺失比例低(<5%),且缺失随机 df.dropna() 如果缺失集中在某个时段,会破坏时序连续性
前向填充 指标变化缓慢,如磁盘使用率 df.fillna(method='ffill') 突发变化时,填充值会滞后
插值填充 指标变化平滑,如温度、CPU df.interpolate() 长段缺失时,插值结果不可靠

我个人习惯,先看缺失的分布。如果缺失是零散的、随机的,直接丢弃最省事。如果缺失是连续的(比如连续 10 分钟没数据),我会用插值,但会标记这些插值点,后续做异常检测时降低它们的权重。

核心原则: 缺失值处理不是「填上就行」,而是要保留「这里缺过数据」这个信息。我建议在数据集中增加一个 is_imputed 字段,标记哪些值是填充的。

2.4 异常值初步过滤:别把「真异常」当噪声扔了

这一步,是预处理里最考验经验的。为什么叫「初步过滤」?因为我们要区分两类异常:

  • 脏数据异常: 采集错误、格式错误、明显不合理的数据。这些要坚决剔除。
  • 真实业务异常: 比如流量突增、服务降级。这些正是我们要检测的目标,不能过滤掉。

我常用的初步过滤方法有三个:

2.4.1 3σ 法则

假设数据服从正态分布,超过均值 ± 3 倍标准差的值视为异常。这个方法简单,但有个前提:数据不能有太多真实异常。否则均值和标准差本身就被污染了。

# 3σ 过滤示例
mean = df['value'].mean()
std = df['value'].std()
df_filtered = df[(df['value'] >= mean - 3*std) & (df['value'] <= mean + 3*std)]

2.4.2 IQR 方法

四分位距法,对数据分布不那么敏感。把低于 Q1 - 1.5*IQR 或高于 Q3 + 1.5*IQR 的值视为异常。我在处理延迟数据时特别喜欢用这个方法,因为延迟分布往往偏态严重,3σ 效果不好。

2.4.3 业务规则过滤

这个最靠谱,但也最费功夫。比如:

  • 磁盘使用率不能超过 100%
  • 响应时间不能为负数
  • QPS 不能超过物理机极限

我曾经遇到过一个案例:某服务的错误率突然从 1% 飙升到 99%,3σ 和 IQR 都把它标记为异常。但排查后发现,是采集器把「成功」和「失败」的标签搞反了。这就是典型的脏数据异常,必须过滤掉。

避坑指南: 我曾经因为过度过滤,把一个真实的服务降级事件给滤掉了。那次之后我定了个规矩:被过滤的数据不能直接丢弃,要写入一个「异常数据池」,定期人工 review。这样既能保证训练数据干净,又不会漏掉真实问题。

2.5 本章知识体系总览

说了这么多,我画了张图,把数据采集与预处理的整个流程串起来。你看一眼就明白了。

数据采集与预处理流程 指标型数据 CPU/内存/QPS/延迟 日志型数据 错误日志/访问日志 事件型数据 变更/发布/告警事件 数据清洗 格式统一 去重 噪声过滤 范围/变化率 缺失值处理 直接丢弃 前向填充 插值填充 异常值初步过滤 3σ 法则 IQR 方法 业务规则过滤 核心原则:保留「脏数据」标记,不丢失「真实异常」信息

这张图把整个流程串起来了。从数据源到清洗,再到缺失值处理,最后做异常值初步过滤。每一步的输出,都是下一步的输入。你按照这个流程走,基本不会出大问题。