如何筛选GroupBy分组中从起始到首个局部最大值的行?
解决分组后保留每组从起始到首个局部最大值行的问题
首先得说,你选filter方法其实不太适配你的需求——filter是用来决定整个组要不要保留的,而你要的是留每个组里从开头到首个局部最大值的部分行。换用groupby.apply或者结合transform的思路会更顺畅,我给你整理两种实用方法:
方法一:用groupby.apply逐个处理分组
这个方法逻辑直观,对每个分组单独计算并截取目标行:
def keep_until_first_peak(group): # 计算B列的差值,第一行默认算上升趋势(填充1保证>=0成立) diff_positive = group['B'].diff().fillna(1) >= 0 # 累积乘积:只要出现一次下降,后面所有行都会变成0,True只保留到首次下降前 is_ascending = diff_positive.cumprod() == 1 # 找到最后一个处于上升阶段的行(也就是首个局部最大值)的索引 first_peak_idx = is_ascending[is_ascending].index[-1] # 返回从组开头到这个索引的所有行 return group.loc[:first_peak_idx] # 应用到分组上,最后重置索引去掉层级 result = df.groupby('Flag').apply(keep_until_first_peak).reset_index(drop=True)
方法二:用transform标记目标行(更高效)
如果你的数据量较大,apply的效率可能稍弱,用transform给每行生成保留标记,直接筛选更快捷:
# 对每个分组,给每行标记是否在首个峰值之前的区间内 df['keep'] = df.groupby('Flag')['B'].transform( lambda x: (x.diff().fillna(1) >= 0).cumprod() == 1 ) # 筛选出需要保留的行,最后删掉辅助列 result = df[df['keep']].drop('keep', axis=1)
对你原有思路的补充
你原来想用filter做组内索引比较的思路,其实filter做不到——它只能返回True/False决定整个组的去留,没法精细到组内的行。而上面的第二种方法,其实和你原本判断上升趋势的逻辑完全一致,只是用transform把判断结果映射到每一行,直接筛选就行,比找索引再逐个对比简洁多了。
另外,如果你的“局部最大值”是严格意义上的「该行大于前后行」(不是连续上升后的首次下降前一行),可以调整判断逻辑:
def find_first_strict_peak(group): # 标记局部最大值:中间行要大于前后,首尾行只需要大于相邻行 is_peak = (group['B'] > group['B'].shift(1)) & (group['B'] > group['B'].shift(-1)) is_peak.iloc[0] = group['B'].iloc[0] > group['B'].iloc[1] is_peak.iloc[-1] = group['B'].iloc[-1] > group['B'].iloc[-2] # 找到第一个峰值的索引,没有峰值就保留整组 first_peak_idx = is_peak[is_peak].index[0] if any(is_peak) else group.index[-1] return group.loc[:first_peak_idx] result = df.groupby('Flag').apply(find_first_strict_peak).reset_index(drop=True)
你可以根据自己对“局部最大值”的实际定义来选对应的逻辑~
内容的提问来源于stack exchange,提问作者John
相关产品推荐
相关产品推荐

