You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何判定极值曲线间Y轴距离低于平均距离的设定阈值?

解决极值区间距离阈值判定问题

看起来你已经成功提取了流量数据的局部极值并完成可视化,接下来咱们一步步实现「最大值与最小值曲线间的Y轴距离低于平均距离阈值」的判定需求~

核心思路

因为你提取的极值点X坐标不对应、数量也不一致,直接计算距离会有困难。我们可以通过插值将离散的极值点扩展为与主数据对齐的连续曲线,再逐点(或滑动窗口)计算距离,最后和设定的阈值对比。

步骤1:提取极值点的坐标对

先把已找到的极值点的X(索引)和Y(流量值)单独提取出来,方便后续插值处理:

import numpy as np
from scipy.signal import argrelextrema
from scipy.interpolate import interp1d
import pandas as pd
import matplotlib.pyplot as plt

# 你的原有代码
c_max_index = argrelextrema(df.flow.values, np.greater, order=3)
c_min_index = argrelextrema(df.flow.values, np.less, order=3)
df['min_extreme'] = df.flow[c_min_index[0]]
df['max_extreme'] = df.flow[c_max_index[0]]

# 提取极值点的X和Y坐标
max_x = c_max_index[0]
max_y = df.flow.values[max_x]
min_x = c_min_index[0]
min_y = df.flow.values[min_x]

步骤2:插值生成连续的极值曲线

用线性插值(或非线性插值,比如三次样条)将离散的极值点扩展为覆盖主数据所有索引的连续曲线,这样每个主数据点都能匹配到对应的上下极值估计值:

# 创建插值函数,fill_value="extrapolate"支持超出极值点范围的外插
f_max = interp1d(max_x, max_y, kind='linear', fill_value="extrapolate")
f_min = interp1d(min_x, min_y, kind='linear', fill_value="extrapolate")

# 生成主数据每个索引对应的插值后极值
df['interp_max'] = f_max(df.index)
df['interp_min'] = f_min(df.index)

如果你的数据是非线性趋势,可以把kind改成'cubic'获得更平滑的插值结果;如果不想外插超出极值点范围的数据,可将fill_value设为np.nan,后续用df.dropna()处理缺失值。

步骤3:计算距离并判定阈值

逐点距离判定

计算每个时间点的上下极值距离,再对比「平均距离的10%」阈值:

# 计算每个点的Y轴距离(最大值-最小值)
df['distance'] = df['interp_max'] - df['interp_min']

# 计算整体平均距离
avg_distance = df['distance'].mean()

# 设定阈值(这里是10%,可自行修改比例)
threshold_ratio = 0.1
threshold = threshold_ratio * avg_distance

# 标记低于阈值的点
df['below_threshold'] = df['distance'] < threshold

滑动窗口积分判定(对应你提到的「积分」需求)

如果想判断**一段区间内的总距离(积分)**是否低于阈值,可以用滑动窗口计算积分(即窗口内距离的累加和):

# 设定滑动窗口大小(根据你的数据频率调整,比如10个时间步)
window_size = 10

# 计算窗口内的距离积分
df['rolling_integral'] = df['distance'].rolling(window=window_size).sum()

# 计算积分的平均值,再生成阈值
avg_integral = df['rolling_integral'].mean()
integral_threshold = threshold_ratio * avg_integral

# 标记积分低于阈值的区间
df['integral_below_threshold'] = df['rolling_integral'] < integral_threshold

步骤4:可视化验证

将结果叠加到原有图表上,直观查看阈值判定的效果:

plt.figure(figsize=(12,6))
plt.plot(df.flow.values, label='Main Flow', c='blue')
plt.plot(max_x, max_y, linewidth=0.8, c='g', label='Max Extremes')
plt.plot(min_x, min_y, linewidth=0.8, c='r', label='Min Extremes')

# 标记逐点低于阈值的点
plt.scatter(df[df['below_threshold']].index, df[df['below_threshold']].flow, 
            c='orange', s=15, alpha=0.6, label='Distance < 10% Avg')

# (可选)标记积分低于阈值的区间
plt.fill_between(df.index, df.flow.min(), df.flow.max(), 
                 where=df['integral_below_threshold'], 
                 color='yellow', alpha=0.2, label='Integral < 10% Avg')

plt.legend()
plt.xlabel('Index')
plt.ylabel('Flow')
plt.title('Flow Data with Extreme Value Threshold Detection')
plt.show()

注意事项

  • argrelextremaorder参数决定了局部极值的邻域大小,如果极值点太密集或太稀疏,可以调整这个值(比如改成5)。
  • 插值方法的选择取决于你的数据特性,线性插值适合趋势平缓的数据,三次样条适合非线性波动的数据。
  • 如果你不需要外插超出极值点范围的数据,记得处理插值后的NaN值,避免影响后续计算。

内容的提问来源于stack exchange,提问作者Andrew Graham-Yooll

火山引擎 最新活动