寻求Pandas式无循环方法移除DataFrame中连续升降序列
解决Pandas移除连续升降序列中间点的问题
嘿,这个需求用Pandas的矢量化操作完全可以搞定,不用写循环~ 我给你两种简洁的实现方式,核心思路都是找到连续升降的区间,然后只保留每个区间的关键转折点。
方法一:分组法(简洁易读)
这是最推荐的方式,利用groupby对连续同方向的区间分组,直接提取每组的终点:
import pandas as pd import numpy as np # 你的原始DataFrame df = pd.DataFrame([1,2,3,5,4,3,2,4,5,6,7,8,9]) s = df[0] # 提取单列作为Series方便操作 # 计算相邻元素变化的符号(从第二个元素开始,第一个元素无前置值) signs = s.diff().apply(np.sign).iloc[1:] # 为连续相同变化方向的区间生成唯一分组ID group_key = (signs != signs.shift()).cumsum() # 组装结果:第一个元素 + 每个连续区间的最后一个元素 result = [s.iloc[0]] result.extend(s.iloc[1:].groupby(group_key).last().tolist()) print(result) # 输出: [1,5,2,9]
逻辑解释:
s.diff()计算相邻元素的差值,np.sign()将差值转换为符号(1=上升,-1=下降,0=不变),我们从第二个元素开始取结果,因为第一个元素的差值为NaN,无实际意义。(signs != signs.shift()).cumsum()会给每一段连续相同符号的区间分配唯一ID:每当符号变化时,分组ID自动+1,这样所有连续上升/下降的元素会被分到同一组。- 最后我们保留第一个元素,再加上每个分组的最后一个元素(即该连续区间的终点),就得到了去掉中间点的目标结果。
方法二:掩码筛选法
通过生成变化位置的掩码,直接筛选需要保留的元素索引:
import pandas as pd import numpy as np df = pd.DataFrame([1,2,3,5,4,3,2,4,5,6,7,8,9]) s = df[0] # 计算所有元素的变化符号,第一个元素补0(无前置值) diff_sign = s.diff().apply(np.sign).fillna(0) # 找到符号变化的位置:当前符号与前一个符号不同的位置 change_mask = diff_sign.ne(diff_sign.shift()) # 提取变化位置的索引,调整得到需要保留的元素索引 change_indices = change_mask[change_mask].index.tolist() result_indices = [change_indices[0]] + [idx-1 for idx in change_indices[1:]] + [s.index[-1]] # 去重避免重复保留第一个元素 result_indices = list(dict.fromkeys(result_indices)) # 获取最终结果 result = s.loc[result_indices].tolist() print(result) # 输出: [1,5,2,9]
逻辑解释:
- 先为每个元素计算变化符号,第一个元素因无前置值,用
fillna(0)填充。 diff_sign.ne(diff_sign.shift())生成布尔掩码,标记所有方向切换的位置。- 我们取这些切换位置的前一个索引(即上一个连续区间的终点),再加上第一个和最后一个元素,去重后就是需要保留的元素索引,最后提取对应元素即可。
两种方法都是纯矢量化操作,效率远高于循环,处理大型数据集时优势明显。分组法更简洁易读,优先推荐~
内容的提问来源于stack exchange,提问作者smallcat31
相关产品推荐
相关产品推荐

