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

非迭代式识别趋势斜率偏离:时间序列阈值分析需求

问题描述

我拥有如下时间序列数据(val列为63日移动平均除以252日移动平均的结果):

Date,val
02/06/1980,0.9866
03/06/1980,0.9859
04/06/1980,0.9657
05/06/1980,0.9859
06/06/1980,0.9864
09/06/1980,0.9871
10/06/1980,0.9877
11/06/1980,0.9885
12/06/1980,0.9895
13/06/1980,0.9905
16/06/1980,0.9921
17/06/1980,0.9933
18/06/1980,0.9945
19/06/1980,0.9958
20/06/1980,0.9971
23/06/1980,0.9989
24/06/1980,1.0244
25/06/1980,1.0029
26/06/1980,1.005
27/06/1980,1.0068
30/06/1980,1.0082
01/07/1980,1.0096
02/07/1980,1.011
03/07/1980,1.0127
07/07/1980,1.0149
08/07/1980,1.0168
09/07/1980,1.0185
10/07/1980,1.0198
11/07/1980,1.0213
14/07/1980,1.0232
15/07/1980,1.025
16/07/1980,1.027
17/07/1980,1.0292
18/07/1980,1.0316
21/07/1980,1.0341
22/07/1980,1.0361
23/07/1980,1.038
24/07/1980,1.0399
25/07/1980,1.0415
28/07/1980,1.0431
29/07/1980,1.0448
30/07/1980,1.0464
31/07/1980,1.0481
01/08/1980,1.0497
04/08/1980,1.0512
05/08/1980,1.0528
06/08/1980,1.0543
07/08/1980,1.0561
08/08/1980,1.0582
11/08/1980,1.0604
12/08/1980,1.0623
13/08/1980,1.0641
14/08/1980,1.0661
15/08/1980,1.0681
18/08/1980,1.0698
19/08/1980,1.0714
20/08/1980,1.0732
21/08/1980,1.0749
22/08/1980,1.0764
25/08/1980,1.0778
26/08/1980,1.079

我希望识别出两类时间段:

  • 斜率从近期底部上升5%及以上的区间
  • 斜率从近期顶部下降5%及以上的区间

但我不想用迭代遍历DataFrame并定位顶底后逐一比较的方法——这个方法操作复杂,逻辑很难梳理清楚,请问有没有更简洁的实现方式?


解决方案

嘿,我完全懂你的顾虑——手动迭代找顶底再逐个比较确实太繁琐,还容易出错。下面分享几个利用矢量化操作和工具库的简洁方案,效率更高,逻辑也更清晰:

1. 批量识别局部顶底(无需迭代)

我们可以用scipy.signal.argrelextrema一次性找出所有局部极值点(顶/底),不用手动遍历每个数据点判断。这个函数会根据你设定的order参数(控制极值点前后多少个点内是最大/最小值),批量返回所有符合条件的极值位置。

代码示例:

import pandas as pd
import numpy as np
from scipy.signal import argrelextrema

# 加载并预处理数据
df = pd.read_csv("your_data.csv")  # 也可以直接用你提供的列表构建DataFrame
df["Date"] = pd.to_datetime(df["Date"], format="%d/%m/%Y")
df.set_index("Date", inplace=True)

# 识别局部底部(order=5表示前后5个点的数值都比当前点高,才判定为底部)
local_bottom_indices = argrelextrema(df["val"].values, np.less, order=5)[0]
# 识别局部顶部(order=5表示前后5个点的数值都比当前点低,才判定为顶部)
local_top_indices = argrelextrema(df["val"].values, np.greater, order=5)[0]

# 转换为包含日期和数值的DataFrame
bottoms = df.iloc[local_bottom_indices].rename(columns={"val": "bottom_val"})
tops = df.iloc[local_top_indices].rename(columns={"val": "top_val"})

2. 矢量化计算涨幅/跌幅,筛选符合条件的时间段

找到顶底后,我们可以用pd.merge_asof把每个数据点匹配到最近的顶/底,然后批量计算相对于该顶底的变化率,最后用布尔索引筛选出符合5%阈值的区间。

代码示例:

# 合并顶底数据,标记类型
extrema = pd.concat([
    bottoms.assign(type="bottom"),
    tops.assign(type="top")
]).sort_index().reset_index().rename(columns={"Date": "date"})

# 将原始数据与最近的顶底匹配(backward表示取当前时间之前最近的极值)
df_reset = df.reset_index().rename(columns={"Date": "date"})
merged = pd.merge_asof(df_reset, extrema, on="date", direction="backward")

# 计算相对于最近底部的涨幅、相对于最近顶部的跌幅
merged["pct_from_bottom"] = (merged["val"] - merged["bottom_val"]) / merged["bottom_val"]
merged["pct_from_top"] = (merged["val"] - merged["top_val"]) / merged["top_val"]

# 筛选出上升≥5%的时间段(按每个底部分组,取第一个达标点到最后一个达标点的区间)
up_periods = merged[merged["pct_from_bottom"] >= 0.05].groupby("bottom_val")["date"].agg(["first", "last"])
print("从底部上升5%及以上的时间段:")
print(up_periods)

# 筛选出下降≥5%的时间段
down_periods = merged[merged["pct_from_top"] <= -0.05].groupby("top_val")["date"].agg(["first", "last"])
print("\n从顶部下降5%及以上的时间段:")
print(down_periods)

3. 可选:用TA-Lib简化顶底识别

如果你经常做技术分析,TA-Lib库提供了更专业的顶底形态识别函数(比如CDLHAMMER识别底部形态,CDLHANGINGMAN识别顶部形态),可以进一步简化极值点的判断逻辑:

import talib

# 识别底部形态(比如锤子线)
df["bottom_pattern"] = talib.CDLHAMMER(df["val"], df["val"], df["val"], df["val"])
# 识别顶部形态(比如上吊线)
df["top_pattern"] = talib.CDLHANGINGMAN(df["val"], df["val"], df["val"], df["val"])

# 提取有形态的顶底位置
ta_bottoms = df[df["bottom_pattern"] != 0]
ta_tops = df[df["top_pattern"] != 0]

方法优势

  • 无需手动迭代:所有操作都是矢量化的,效率比循环高得多,尤其是数据量大的时候
  • 逻辑清晰:把顶底识别、变化率计算、区间筛选拆分成独立步骤,更容易调试和维护
  • 可扩展性强:调整order参数或者切换TA-Lib的形态识别函数,就能适配不同的顶底判定标准

内容的提问来源于stack exchange,提问作者krakowi

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 09:17:58