如何在Pandas中获取分组最小值及对应行的列值、分组最小日期及对应列值
我来帮你搞定这两个Pandas分组相关的问题,先从第一个问题说起,再重点解决你遇到的第二个问题的代码bug~
问题1:如何在Pandas中获取分组的最小值,以及该最小值所在行对应某一列的数值?
假设你有一个DataFrame,需要按某列(比如group_col)分组,找到另一列(比如value_col)的最小值,同时获取该最小值所在行的第三列(比如target_col)的数值,这里有两种常用的实现方法:
方法1:用idxmin()定位行索引后提取
idxmin()会返回每个分组中value_col最小值对应的行索引,我们可以通过这个索引直接提取目标列的值,再合并回原DataFrame:
import pandas as pd # 示例数据 data = {'group_col': ['A', 'A', 'B', 'B'], 'value_col': [3, 1, 5, 2], 'target_col': ['x', 'y', 'm', 'n']} df = pd.DataFrame(data) # 获取每个分组中value_col最小的行索引 min_idx = df.groupby('group_col')['value_col'].idxmin() # 提取对应的最小值和目标列值 result = df.loc[min_idx, ['group_col', 'value_col', 'target_col']].rename(columns={'value_col': 'min_value'}) # 合并回原数据(如果需要保留所有行) df = df.merge(result, on='group_col', suffixes=('', '_min'))
方法2:结合transform()和布尔索引
先用transform('min')得到每个分组的最小值,再筛选出对应行的目标列值,最后将结果广播到整个分组:
df['min_value'] = df.groupby('group_col')['value_col'].transform('min') # 筛选出最小值所在行的target_col,再按分组广播到所有行 df['target_min'] = df.loc[df['value_col'] == df['min_value'], 'target_col'].groupby(df['group_col']).transform('first')
问题2:按分组获取最小日期,并获取该最小日期所在行对应另一列的数值(解决你的代码错误)
先分析你现有代码的问题:你使用df.groupby('type')['date_value','sum_col'].transform('min')时,Pandas会分别对date_value和sum_col两列取分组内的最小值,所以new_sum_col得到的是每个分组sum_col的最小值(比如分组B的sum_col最小值是2),而不是你期望的「最小日期对应行的sum_col值」。
下面是三种正确的解决方案:
方法1:通过idxmin()定位最小日期的行,再合并回原数据
这是最直观的方法,先找到每个分组中最小日期的行索引,提取对应的日期和sum_col,再和原DataFrame合并:
import pandas as pd data = {'date_value': ['2014-06-22', '2014-06-24', '2014-06-25', '2014-06-25', '2014-07-02', '2014-07-06', '2014-07-05', '2014-07-27'], 'type': ['A', 'A', 'A', 'B', 'B', 'B', 'B', 'C'], 'sum_col': [1, 2, 3, 5, 4, 3, 2, 1]} df = pd.DataFrame(data, columns=['date_value', 'type', 'sum_col']) df['date_value'] = pd.to_datetime(df['date_value']) # 获取每个分组中date_value最小的行索引 min_date_idx = df.groupby('type')['date_value'].idxmin() # 提取对应的min_date和new_sum_col min_info = df.loc[min_date_idx, ['type', 'date_value', 'sum_col']].rename(columns={'date_value': 'min_date', 'sum_col': 'new_sum_col'}) # 合并回原DataFrame df = df.merge(min_info, on='type')
运行后会得到你期望的结果:
date_value type sum_col min_date new_sum_col 0 2014-06-22 A 1 2014-06-22 1 1 2014-06-24 A 2 2014-06-22 1 2 2014-06-25 A 3 2014-06-22 1 3 2014-06-25 B 5 2014-06-25 5 4 2014-07-02 B 4 2014-06-25 5 5 2014-07-06 B 3 2014-06-25 5 6 2014-07-05 B 2 2014-06-25 5 7 2014-07-27 C 1 2014-07-27 1
方法2:用transform()结合索引匹配
如果你不想用merge,可以先计算每个分组的最小日期,再通过索引匹配找到对应行的sum_col:
# 先添加min_date列 df['min_date'] = df.groupby('type')['date_value'].transform('min') # 获取每个分组最小日期对应的sum_col,再通过type映射到每一行 min_sum = df.loc[df['date_value'] == df['min_date'], ['type', 'sum_col']].set_index('type')['sum_col'] df['new_sum_col'] = df['type'].map(min_sum)
方法3:自定义聚合函数
通过groupby.agg()自定义聚合逻辑,直接计算每个分组的最小日期和对应sum_col,再合并:
agg_result = df.groupby('type').agg( min_date=('date_value', 'min'), new_sum_col=('sum_col', lambda x: df.loc[x.index[df.loc[x.index, 'date_value'] == df.loc[x.index, 'date_value'].min()], 'sum_col'].iloc[0]) ) df = df.merge(agg_result, on='type')
内容的提问来源于stack exchange,提问作者Sam

