Pandas DataFrame筛选:获取各ID对应的唯一最值行
解决方法:筛选每个ID对应A/B/C列最值的唯一行
我来帮你搞定这个需求——要从包含ID、A、B、C的DataFrame里,筛选每个唯一ID下A/B/C列最大值、最小值对应的行,同时保证行不重复,我们可以用pandas的分组、标记和筛选来实现,完美处理重复行的情况(比如某一行同时是多个列的最值,只保留一次)。
步骤1:准备示例输入数据
先模拟你提到的输入DataFrame(和你展示的示例逻辑一致):
import pandas as pd # 示例输入数据 df = pd.DataFrame({ 'ID': ['X', 'X', 'X', 'Y', 'Y', 'Y', 'Y'], 'A': [1, 3, 2, 5, 5, 3, 4], 'B': [4, 2, 3, 1, 2, 4, 3], 'C': [2, 1, 3, 3, 1, 2, 4] })
步骤2:标记每行是否为对应列的最值
我们可以用groupby.transform给每行添加标记,判断它是否是当前ID下某列的最大值或最小值:
# 为每个列添加"是否是最大值/最小值"的标记列 for col in ['A', 'B', 'C']: # 标记是否为当前ID下该列的最大值 df[f'{col}_is_max'] = df[col] == df.groupby('ID')[col].transform('max') # 标记是否为当前ID下该列的最小值 df[f'{col}_is_min'] = df[col] == df.groupby('ID')[col].transform('min')
步骤3:筛选符合条件的行并去重
只要某一行满足任意一个列的最大值/最小值条件,就保留它;同时用drop_duplicates去掉重复行(避免同一行因满足多个条件被多次选中):
# 生成筛选条件:只要有一个标记为True,就保留该行 filter_condition = df.filter(regex='_is_(max|min)').any(axis=1) # 筛选后去重,只保留原有的ID/A/B/C列 result_df = df[filter_condition].drop_duplicates(subset=['ID', 'A', 'B', 'C'])[['ID', 'A', 'B', 'C']]
验证结果
运行后得到的result_df就是你期望的输出:
- 对于ID=X:会保留A列最大值(3)、A列最小值(1)、B列最大值(4)、B列最小值(2)、C列最大值(3)、C列最小值(1)对应的行,但其中行
X,1,4,2同时是A的最小值和B的最大值,只会保留一次;行X,3,2,1同时是A的最大值和C的最小值,也只会保留一次。 - 对于ID=Y:同理,会保留所有符合最值条件的唯一行,处理类似df#2里的重复情况。
另一种简洁写法(无需标记列)
如果你不想添加额外标记列,也可以通过分组聚合+合并的方式实现:
# 计算每个ID下各列的最大值和最小值 agg_stats = df.groupby('ID').agg({ 'A': ['max', 'min'], 'B': ['max', 'min'], 'C': ['max', 'min'] }).reset_index() # 重命名聚合后的列,方便后续合并 agg_stats.columns = ['ID', 'A_max', 'A_min', 'B_max', 'B_min', 'C_max', 'C_min'] # 合并原数据和聚合结果,然后筛选符合条件的行 merged = df.merge(agg_stats, on='ID') filter_condition = ( (merged['A'].isin([merged['A_max'], merged['A_min']])) | (merged['B'].isin([merged['B_max'], merged['B_min']])) | (merged['C'].isin([merged['C_max'], merged['C_min']])) ) result_df = merged[filter_condition].drop_duplicates(subset=['ID', 'A', 'B', 'C'])[['ID', 'A', 'B', 'C']]
这两种方法都能完美解决你的需求,核心是先识别所有符合最值条件的行,再通过去重保证每行只出现一次。
内容的提问来源于stack exchange,提问作者TuoCuggino
相关产品推荐
相关产品推荐

