如何筛选Pandas DataFrame以保留分组内最早和最晚年份的所有行?
问题:保留每个ID分组内最早和最晚年份的所有行
给定如下DataFrame:
import pandas as pd data = [ [123456, "2017", 150.235], [123456, "2017", 160], [123456, "2017", 135], [123456, "2017", 135], [123456, "2017", 135], [123456, "2018", 202.5], [123456, "2019", 168.526], [123456, "2020", 175.559], [123456, "2020", 176], [123456, "2021", 206.667], [789101, "2017", 228.9], [789101, "2018", 208], [789101, "2018", 208], [789101, "2018", 208], ] df = pd.DataFrame( data, columns=["ID", "year", "value"], )
需求:筛选该DataFrame,保留每个ID分组内最早年份的所有行(含重复value)以及最晚年份的所有行(含重复value)。
之前尝试的代码仅保留了最早年份的第一行和最晚年份的最后一行,无法满足需求:
g = df.groupby("ID") (pd.concat([g.head(1), g.tail(1)]) .drop_duplicates() .sort_values('ID') .reset_index(drop=True))
解决方案
方法1:使用transform标记分组内的最小/最大年份
# 先将year转为数值类型,确保年份比较准确 df['year'] = df['year'].astype(int) # 按ID分组,获取每个分组的最小和最大年份,映射到每一行 grouped_year = df.groupby('ID')['year'] df['is_min_year'] = grouped_year.transform(lambda x: x == x.min()) df['is_max_year'] = grouped_year.transform(lambda x: x == x.max()) # 筛选出属于最早或最晚年份的行 result = df[df['is_min_year'] | df['is_max_year']] # 清理临时列,按ID和年份排序并重置索引 result = result.drop(['is_min_year', 'is_max_year'], axis=1) result = result.sort_values(['ID', 'year']).reset_index(drop=True) print(result)
方法2:更简洁的掩码写法
df['year'] = df['year'].astype(int) # 生成布尔掩码,判断每行年份是否为分组内的最小或最大值 mask = df.groupby('ID')['year'].transform(lambda x: x == x.min() | x == x.max()) # 筛选并整理结果 result = df[mask].sort_values(['ID', 'year']).reset_index(drop=True) print(result)
说明
- 先将
year转为数值类型是为了避免字符串排序的潜在问题(比如"2019"和"202"的错误比较)。 transform方法会将分组计算的结果映射到原DataFrame的每一行,这样就能精准筛选出每个ID分组内所有属于最早或最晚年份的行,包括重复的value。
内容的提问来源于stack exchange,提问作者user
相关产品推荐
相关产品推荐

