如何让孤立森林在差分峰值处检测异常而非首个触发点
问题:孤立森林异常标记位置偏移的解决办法
我用孤立森林(Isolation Forest)在大型数据框里识别异常。数据存在噪声,所以执行了多项滤波操作去噪以凸显真实异常;随后对数据集使用.diff()生成异常发生时会出现峰值的序列,再用孤立森林识别异常。目前的问题是:孤立森林会在最早检测到异常的位置标记,而我需要它在差分峰值处标记异常。我尝试用.shift()手动调整,但仅对部分数据有效,且不愿调整平滑数据的窗口大小(这会严重影响精度)。
相关代码:
df["Ref Wt. Denoised"] = denoise(df["Ref Wt."].values, level=2) df["Ref Wt. Savgol"] = apply_savgol_filter(df["Ref Wt. Denoised"], window_length=101, polyorder=3) df["Ref Wt. Smoothed"] = df["Ref Wt. Savgol"].rolling(window=indexer).mean() df["Ref Wt. Diff"] = df["Ref Wt. Smoothed"].diff(periods=300).fillna(0) df["WOB Anomaly"] = detect_wob.predict(df["Ref Wt. Diff"].values.reshape(-1, 1)) df["WOB Zero Event"] = df["WOB Anomaly"] == -1
问题及期望修复效果见示意图(原链接已移除)
解决思路
核心是让异常标记和差分峰值位置对齐,不用手动调shift或修改平滑窗口,试试以下几种方案:
1. 先定位差分峰值,再映射异常标记
先找出差分序列的峰值位置,再把孤立森林检测到的异常映射到最近的峰值上:
import numpy as np # 识别差分序列的局部峰值(可根据数据调整阈值和判断逻辑) def find_local_peaks(arr, threshold=0): peaks = [] for i in range(1, len(arr)-1): if arr[i] > arr[i-1] and arr[i] > arr[i+1] and arr[i] > threshold: peaks.append(i) return peaks # 获取差分序列的峰值索引 diff_peaks = find_local_peaks(df["Ref Wt. Diff"].values) # 把孤立森林标记的异常位置,匹配到最近的峰值 anomaly_idx = df[df["WOB Zero Event"]].index.tolist() aligned_anomalies = [min(diff_peaks, key=lambda x: abs(x - idx)) for idx in anomaly_idx] # 更新对齐后的异常标记列 df["WOB Zero Event Aligned"] = False df.loc[aligned_anomalies, "WOB Zero Event Aligned"] = True
2. 给模型添加峰值位置特征
如果孤立森林误判是因为单特征信息不足,试试同时输入差分序列和滑动窗口内的峰值相对位置特征,让模型更关注峰值区域:
# 添加滑动窗口内差分最大值的相对位置特征(窗口大小可根据diff的periods=300调整) window_size = 500 df["Diff Peak_Rel_Pos"] = df["Ref Wt. Diff"].rolling(window=window_size).apply( lambda x: x.argmax() - window_size//2 # 标记窗口内最大值相对中心的偏移 ).fillna(0) # 用多特征训练和预测 features = df[["Ref Wt. Diff", "Diff Peak_Rel_Pos"]].values df["WOB Anomaly"] = detect_wob.predict(features) df["WOB Zero Event"] = df["WOB Anomaly"] == -1
3. 先识别异常段,再在段内找峰值标记
不用孤立森林直接标记单个点,而是先识别连续的异常段,再在每个段内找出差分峰值作为最终标记点:
# 给连续的异常段分组 df["Anomaly_Segment_ID"] = (df["WOB Anomaly"] == -1).astype(int).diff().ne(0).cumsum() anomaly_segments = df[df["WOB Anomaly"] == -1].groupby("Anomaly_Segment_ID") # 每个异常段内取差分最大的位置作为标记 aligned_indices = [] for _, seg in anomaly_segments: peak_idx = seg["Ref Wt. Diff"].idxmax() aligned_indices.append(peak_idx) df["WOB Zero Event Aligned"] = False df.loc[aligned_indices, "WOB Zero Event Aligned"] = True
内容的提问来源于stack exchange,提问作者Zach Tynes
相关产品推荐
相关产品推荐

