如何按ID分组筛选DataFrame行至指定列值≤25?
问题与解决方案
原始数据
import pandas as pd df = pd.DataFrame({ 'id': [1, 1, 1, 1, 1, 2, 2, 2, 2, 2], 'value2': [30, 28, 27, 24, 25, 29, 28, 28, 26, 25], 'value3': [29, 29, 27, 25, 24, 27, 27, 26, 26, 26], 'value4': [30, 29, 25, 25, 25, 29, 28, 28, 25, 26] })
对应表格:
| id | value2 | value3 | value4 |
|---|---|---|---|
| 1 | 30 | 29 | 30 |
| 1 | 28 | 29 | 29 |
| 1 | 27 | 27 | 25 |
| 1 | 24 | 25 | 25 |
| 1 | 25 | 24 | 25 |
| 2 | 29 | 27 | 29 |
| 2 | 28 | 27 | 28 |
| 2 | 28 | 26 | 28 |
| 2 | 26 | 26 | 25 |
| 2 | 25 | 26 | 26 |
需求
按id分组,每组筛选所有行直到value2、value3或value4任意一列的值≤25(包含该行),后续行全部剔除。
之前尝试的问题
之前的代码df[df['value2'].eq(25).cummin() | df['value3'].eq(25).cummin() | df['value4'].eq(25).cummin()]存在两个问题:
- 仅匹配等于25的值,未覆盖≤25的情况
- 没有按
id分组处理,会跨id生效
最优解决方案
通过分组后计算触发条件+累积掩码实现,步骤如下:
完整代码
import pandas as pd df = pd.DataFrame({ 'id': [1, 1, 1, 1, 1, 2, 2, 2, 2, 2], 'value2': [30, 28, 27, 24, 25, 29, 28, 28, 26, 25], 'value3': [29, 29, 27, 25, 24, 27, 27, 26, 26, 26], 'value4': [30, 29, 25, 25, 25, 29, 28, 28, 25, 26] }) # 1. 标记每行是否满足任意列≤25的触发条件 df['trigger'] = df[['value2', 'value3', 'value4']].le(25).any(axis=1) # 2. 按id分组生成筛选掩码:保留从开头到第一次触发的所有行 mask = df.groupby('id')['trigger'].transform(lambda x: ~x.cummax().shift(1, fill_value=False)) # 3. 筛选数据并删除临时列 result = df[mask].drop('trigger', axis=1) print(result)
代码解释
df[['value2', 'value3', 'value4']].le(25).any(axis=1):逐行判断是否有任意一列≤25,生成布尔触发列x.cummax():分组内第一次触发后,后续所有行标记为Trueshift(1, fill_value=False):将触发标记后移一行,确保触发行本身被保留- 取反后得到的掩码,会保留每组从开头到第一次触发的所有行
输出结果
| id | value2 | value3 | value4 |
|---|---|---|---|
| 1 | 30 | 29 | 30 |
| 1 | 28 | 29 | 29 |
| 1 | 27 | 27 | 25 |
| 2 | 29 | 27 | 29 |
| 2 | 28 | 27 | 28 |
| 2 | 28 | 26 | 28 |
| 2 | 26 | 26 | 25 |
内容的提问来源于stack exchange,提问作者Roman Lents
相关产品推荐
相关产品推荐

