非迭代式识别趋势斜率偏离:时间序列阈值分析需求
问题描述
我拥有如下时间序列数据(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
相关产品推荐
相关产品推荐

