You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何基于不同信号模式自动聚类拆分多模式深度序列数据?

如何基于不同信号模式自动聚类拆分多模式深度序列数据?

看起来你遇到的是典型的序列数据变点检测/模式分割问题——因为深度是有序的(从浅到深),信号模式在某些深度发生突变,需要自动识别这些分界点,给每个模式段打标签。手动设置阈值肯定不现实,尤其是数据量多、模式数量不确定的情况。下面分享几个实用的思路和代码示例,直接适配你的数据场景:

方法一:变点检测(最适合有序深度序列)

变点检测专门用来识别时间/有序序列中统计特性突然变化的位置,完美匹配你的深度-信号序列。这里推荐用ruptures库(专门做变点检测的工具),它能自动识别变点数量,不需要提前指定模式个数。

步骤示例:

  1. 先安装依赖(如果没装的话):
pip install ruptures numpy pandas matplotlib
  1. 针对你的示例数据,实现变点检测并打标签:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import ruptures as rpt

# 先复现你的示例数据
df1 = pd.DataFrame({"DEPTH (m)":np.arange(0, 2000, 2),
                    "SIGNAL":np.random.uniform(low=-6, high=10, size=(1000,))})
df2 = pd.DataFrame({"DEPTH (m)":np.arange(2000, 3000, 2),
                    "SIGNAL":np.random.uniform(low=0, high=5, size=(500,))})
for i, row in df2.iterrows():
    df2.loc[i, "SIGNAL"] = row["SIGNAL"] * (i / 100)
df_final = pd.concat([df1, df2]).reset_index(drop=True)

# 准备数据:提取信号序列(因为深度是等间隔的,直接用信号值即可)
signal = df_final["SIGNAL"].values.reshape(-1, 1)

# 初始化变点检测模型:用"rbf"核适合检测非线性变化,也可以试试"linear"
model = rpt.Pelt(model="rbf").fit(signal)
# 自动检测变点(penalty参数控制灵敏度,值越小检测到的变点越多,可根据数据调整)
breaks = model.predict(penalty=10)

# 把变点转换成深度值(因为每个数据点间隔2m,breaks是索引位置)
depth_breaks = [df_final["DEPTH (m)"].iloc[b-1] for b in breaks[:-1]]
print("检测到的变点深度:", depth_breaks)  # 示例里应该会接近2000m

# 给每个数据点打聚类/标签
df_final["PATTERN_FLAG"] = 0
current_flag = 0
for break_idx in breaks[:-1]:
    df_final.loc[break_idx:, "PATTERN_FLAG"] = current_flag + 1
    current_flag += 1

# 可视化验证
plt.figure(figsize=(8, 6))
plt.plot(df_final["SIGNAL"], df_final["DEPTH (m)"], linewidth=0.5)
# 绘制变点线
for depth in depth_breaks:
    plt.axhline(y=depth, color="r", linestyle="--", linewidth=1)
plt.ylim(df_final["DEPTH (m)"].max(), df_final["DEPTH (m)"].min())
plt.xlabel("SIGNAL")
plt.ylabel("DEPTH (m)")
plt.title("信号模式拆分结果(红色虚线为变点)")
plt.show()

这个方法的优势是不需要提前知道模式数量,完全自动识别突变点,而且对非线性的模式变化(比如你的递增模式)适配性很好。如果你的数据有噪声,可以先对信号做平滑处理(比如用pd.Series.rolling),再检测变点,结果会更稳定。

方法二:基于信号变化率的聚类

如果你的模式差异主要体现在信号随深度的变化率上(比如一个段信号波动小,另一个段信号持续递增),可以先计算信号的一阶差分(变化率),再用聚类算法(比如DBSCAN)把变化率相似的段归为一类。

步骤示例:

# 计算信号的一阶差分(相邻深度的信号变化量)
df_final["SIGNAL_DIFF"] = df_final["SIGNAL"].diff().fillna(0)

# 用DBSCAN聚类:基于变化率的密度聚类,适合自动识别不同的变化模式
from sklearn.cluster import DBSCAN
from sklearn.preprocessing import StandardScaler

# 标准化变化率数据
scaled_diff = StandardScaler().fit_transform(df_final[["SIGNAL_DIFF"]])
# 训练DBSCAN(eps和min_samples可以根据数据调整)
dbscan = DBSCAN(eps=0.3, min_samples=5)
df_final["PATTERN_FLAG"] = dbscan.fit_predict(scaled_diff)

# 可视化
plt.figure(figsize=(8, 6))
# 用不同颜色区分不同模式
for flag in df_final["PATTERN_FLAG"].unique():
    subset = df_final[df_final["PATTERN_FLAG"] == flag]
    plt.plot(subset["SIGNAL"], subset["DEPTH (m)"], linewidth=0.5, label=f"模式{flag}")
plt.ylim(df_final["DEPTH (m)"].max(), df_final["DEPTH (m)"].min())
plt.xlabel("SIGNAL")
plt.ylabel("DEPTH (m)")
plt.legend()
plt.title("基于变化率的聚类拆分结果")
plt.show()

这个方法适合模式差异体现在信号变化趋势上的场景,比如平稳波动vs持续递增/递减。需要注意的是,DBSCAN对参数比较敏感,可能需要根据你的实际数据调整eps和min_samples。

方法三:K-Means聚类(需预估模式数量)

如果你能大致预估每个数据框的模式数量(比如最多3-4种),也可以用K-Means聚类,但需要结合信号的统计特征(比如均值、方差、斜率)来聚类,而不是直接用原始信号值(因为原始信号是连续序列,直接聚类效果不好)。

步骤示例:

# 先对数据做滑动窗口统计,提取每个窗口的特征
window_size = 50  # 滑动窗口大小,根据数据间隔调整
df_final["SIGNAL_MEAN"] = df_final["SIGNAL"].rolling(window_size).mean().fillna(method="bfill")
df_final["SIGNAL_STD"] = df_final["SIGNAL"].rolling(window_size).std().fillna(method="bfill")
# 计算窗口内的信号斜率(线性拟合的系数)
def get_slope(window):
    x = np.arange(len(window))
    y = window.values
    slope, _ = np.polyfit(x, y, 1)
    return slope
df_final["SIGNAL_SLOPE"] = df_final["SIGNAL"].rolling(window_size).apply(get_slope).fillna(method="bfill")

# 提取特征矩阵
features = df_final[["SIGNAL_MEAN", "SIGNAL_STD", "SIGNAL_SLOPE"]]
scaled_features = StandardScaler().fit_transform(features)

# 用K-Means聚类(假设我们知道有2种模式,实际可以用肘部法确定k值)
from sklearn.cluster import KMeans
kmeans = KMeans(n_clusters=2, random_state=42)
df_final["PATTERN_FLAG"] = kmeans.fit_predict(scaled_features)

# 可视化
plt.figure(figsize=(8, 6))
for flag in df_final["PATTERN_FLAG"].unique():
    subset = df_final[df_final["PATTERN_FLAG"] == flag]
    plt.plot(subset["SIGNAL"], subset["DEPTH (m)"], linewidth=0.5, label=f"模式{flag}")
plt.ylim(df_final["DEPTH (m)"].max(), df_final["DEPTH (m)"].min())
plt.xlabel("SIGNAL")
plt.ylabel("DEPTH (m)")
plt.legend()
plt.title("基于统计特征的K-Means聚类结果")
plt.show()

这个方法适合模式差异体现在统计特性上的场景,但缺点是需要预估聚类数量(或者用肘部法自动确定),不如变点检测灵活。


总结一下,最推荐的是变点检测方法,因为它完美适配深度这种有序序列,不需要提前知道模式数量,而且能准确找到模式突变的分界点,完全满足你“自动处理大量数据框”的需求。如果你的数据噪声比较大,记得先做平滑处理,再检测变点哦。

备注:内容来源于stack exchange,提问作者user026

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.23 15:22:40