如何从时间序列Pandas DataFrame中识别事件并计算曲线下面积?
时间序列事件识别与面积计算解决方案
一、先明确事件定义(核心前提)
你得先把「高于零的上升段」的规则量化,不然代码没法落地,给你两种常用定义参考:
- 基础版:连续高于零的区间
- 事件起始:数据点从≤0变为>0的时刻
- 事件结束:数据点从>0变为≤0的时刻
- 中间所有连续>0的点都归为同一个事件
- 进阶版:连续高于零且趋势上升的区间
- 在基础版规则上,额外要求事件区间内整体趋势向上(比如用相邻点差值为正的占比、或线性拟合斜率为正来判断)
二、用Pandas识别事件区间
假设你的DataFrame是df,包含timestamp(已排序的时间戳列)和value(数值列)
步骤1:标记事件状态
给每个数据点标记是否处于事件中:
# 基础版:标记>0的点为事件内 df['in_event'] = (df['value'] > 0).astype(int) # 进阶版:额外加入上升趋势判断(以相邻差值≥0为例) # df['diff'] = df['value'].diff().fillna(0) # df['in_event'] = ((df['value'] > 0) & (df['diff'] >= 0)).astype(int)
步骤2:给每个事件分配唯一ID
通过事件状态的变化,把连续的事件点归为同一个ID:
# 标记新事件的起始点:从非事件(0)转为事件(1)的位置 df['event_start'] = (df['in_event'] == 1) & (df['in_event'].shift(1) == 0) # 生成事件ID:累计起始标记的次数 df['event_id'] = df['event_start'].cumsum() # 非事件的ID设为0,方便后续过滤 df['event_id'] = df['event_id'].where(df['in_event'] == 1, 0)
步骤3:提取事件起止时间
按事件ID分组,提取每个事件的最早和最晚时间:
event_info = df[df['event_id'] != 0].groupby('event_id').agg( start_time=('timestamp', 'min'), end_time=('timestamp', 'max') ).reset_index()
三、计算每个事件的曲线下面积(AUC)
用梯形法计算最贴合时间序列的面积,借助scipy的trapezoid函数:
from scipy.integrate import trapezoid # 定义单个事件的AUC计算函数 def calculate_auc(group): # 确保数据按时间排序 group_sorted = group.sort_values('timestamp') # 把时间转为数值(比如秒)作为x轴,数值作为y轴 x = (group_sorted['timestamp'] - group_sorted['timestamp'].iloc[0]).dt.total_seconds() y = group_sorted['value'] return trapezoid(y, x) # 分组计算每个事件的AUC event_auc = df[df['event_id'] != 0].groupby('event_id').apply(calculate_auc).reset_index(name='auc') # 合并事件起止时间和AUC结果 final_result = event_info.merge(event_auc, on='event_id')
四、结果存储
把最终结果导出成常用格式即可:
# 保存为CSV final_result.to_csv('event_auc_results.csv', index=False) # 或者保存为Excel # final_result.to_excel('event_auc_results.xlsx', index=False)
补充提示
- 如果你的时间戳不是datetime类型,先转换:
df['timestamp'] = pd.to_datetime(df['timestamp']) - 进阶版的趋势判断可以灵活调整,比如用滑动窗口或线性回归:
from scipy.stats import linregress def is_rising(group): x = range(len(group)) y = group['value'].values slope, _, _, _, _ = linregress(x, y) return slope > 0 # 过滤出真正上升的事件 rising_events = df[df['event_id'] != 0].groupby('event_id').filter(is_rising) # 再对过滤后的事件重新计算信息和AUC即可
内容的提问来源于stack exchange,提问作者abhi_kr
相关产品推荐
相关产品推荐

