分组后动态移除首尾满足条件的行(基于非零值判断)
按组移除首尾连续全零行,保留中间零行
需求说明
按year列对DataFrame分组,完成以下操作:
- 移除每组开头连续的val1/val2/val3全为0的行,直到遇到第一个非全零行
- 移除每组末尾连续的全零行
- 组内中间的零行必须保留
原始数据
创建DataFrame的代码:
import pandas as pd data = {'year': [2019,2019,2019,2019,2019,2019,2019,2019,2020,2020,2020,2020,2020,2020], 'month': [1,2,3,4,5,6,7,8,5,6,7,8,9,10], 'val1': [0,0,0,0,1,0,0,0,0,0,1,0,0,0], 'val2': [0,0,1,0,1,0,0,0,1,0,1,0,0,0], 'val3': [0,0,0,0,1,1,0,0,0,0,1,1,0,0]} df = pd.DataFrame(data)
原始数据打印输出:
+------+-------+------+------+------+ | year | month | val1 | val2 | val3 | +------+-------+------+------+------+ | 2019 | 1 | 0 | 0 | 0 | +------+-------+------+------+------+ | 2019 | 2 | 0 | 0 | 0 | +------+-------+------+------+------+ | 2019 | 3 | 0 | 1 | 0 | +------+-------+------+------+------+ | 2019 | 4 | 0 | 0 | 0 | +------+-------+------+------+------+ | 2019 | 5 | 1 | 1 | 1 | +------+-------+------+------+------+ | 2019 | 6 | 0 | 0 | 1 | +------+-------+------+------+------+ | 2019 | 7 | 0 | 0 | 0 | +------+-------+------+------+------+ | 2019 | 8 | 0 | 0 | 0 | +------+-------+------+------+------+ | 2020 | 5 | 0 | 1 | 0 | +------+-------+------+------+------+ | 2020 | 6 | 0 | 0 | 0 | +------+-------+------+------+------+ | 2020 | 7 | 1 | 1 | 1 | +------+-------+------+------+------+ | 2020 | 8 | 0 | 0 | 1 | +------+-------+------+------+------+ | 2020 | 9 | 0 | 0 | 0 | +------+-------+------+------+------+ | 2020 | 10 | 0 | 0 | 0 | +------+-------+------+------+------+
期望输出
+------+-------+------+------+------+ | year | month | val1 | val2 | val3 | +------+-------+------+------+------+ | 2019 | 3 | 0 | 1 | 0 | +------+-------+------+------+------+ | 2019 | 4 | 0 | 0 | 0 | +------+-------+------+------+------+ | 2019 | 5 | 1 | 1 | 1 | +------+-------+------+------+------+ | 2019 | 6 | 0 | 0 | 1 | +------+-------+------+------+------+ | 2020 | 5 | 0 | 1 | 0 | +------+-------+------+------+------+ | 2020 | 6 | 0 | 0 | 0 | +------+-------+------+------+------+ | 2020 | 7 | 1 | 1 | 1 | +------+-------+------+------+------+ | 2020 | 8 | 0 | 0 | 1 | +------+-------+------+------+------+
实现代码
import pandas as pd # 原始数据创建 data = {'year': [2019,2019,2019,2019,2019,2019,2019,2019,2020,2020,2020,2020,2020,2020], 'month': [1,2,3,4,5,6,7,8,5,6,7,8,9,10], 'val1': [0,0,0,0,1,0,0,0,0,0,1,0,0,0], 'val2': [0,0,1,0,1,0,0,0,1,0,1,0,0,0], 'val3': [0,0,0,0,1,1,0,0,0,0,1,1,0,0]} df = pd.DataFrame(data) # 标记每行是否为val1/val2/val3全零行 df['is_all_zero'] = (df[['val1', 'val2', 'val3']] == 0).all(axis=1) # 定义分组处理函数:裁剪首尾连续全零行 def trim_group(group): # 找到组内第一个非全零行的索引 first_valid = (~group['is_all_zero']).idxmax() # 找到组内最后一个非全零行的索引 last_valid = (~group['is_all_zero'])[::-1].idxmax() # 截取有效区间内的行,去掉标记列 return group.loc[first_valid:last_valid].drop('is_all_zero', axis=1) # 分组应用处理函数,合并结果 result_df = df.groupby('year', group_keys=False).apply(trim_group) # 打印验证结果 print(result_df)
逻辑说明
- 标记全零行:用
(df[cols] ==0).all(axis=1)快速判断每行是否满足val1、val2、val3全为0的条件 - 找第一个有效行:
(~group['is_all_zero']).idxmax()利用布尔值的数值特性(True=1,False=0),直接取第一个True的索引,也就是第一个非全零行的位置 - 找最后一个有效行:把布尔序列反转后再取idxmax,就能得到最后一个非全零行的索引
- 切片保留有效区间:通过
loc[first:last]直接截取中间部分,自然保留了组内中间的零行
内容的提问来源于stack exchange,提问作者adama
相关产品推荐
相关产品推荐

