Pandas GroupBy 并选择特定列中具有最小值的行
- 2025-02-08 08:52:00
- admin 原创
- 72
问题描述:
我有一个包含 A、B 和 C 列的 DataFrame。对于 A 的每个值,我想选择 B 列中值最小的行。
也就是说:
df = pd.DataFrame({'A': [1, 1, 1, 2, 2, 2],
'B': [4, 5, 2, 7, 4, 6],
'C': [3, 4, 10, 2, 4, 6]})
A B C
0 1 4 3
1 1 5 4
2 1 2 10
3 2 7 2
4 2 4 4
5 2 6 6
我想要得到:
A B C
0 1 2 10
1 2 4 4
目前,我按 A 列分组,然后创建一个值来指示我将保留的行:
a = data.groupby('A').min()
a['A'] = a.index
to_keep = [str(x[0]) + str(x[1]) for x in a[['A', 'B']].values]
data['id'] = data['A'].astype(str) + data['B'].astype('str')
data[data['id'].isin(to_keep)]
我确信有更直接的方法可以做到这一点。我在这里看到很多使用 MultiIndex 的答案,我宁愿避免这样做。
感谢您的帮助。
解决方案 1:
我觉得你想太多了。只需使用groupby
and idxmin
:
df.loc[df.groupby('A').B.idxmin()]
A B C
2 1 2 10
4 2 4 4
df.loc[df.groupby('A').B.idxmin()].reset_index(drop=True)
A B C
0 1 2 10
1 2 4 4
解决方案 2:
有过类似的情况,但列标题更复杂(例如“B val”),在这种情况下需要这样做:
df.loc[df.groupby('A')['B val'].idxmin()]
解决方案 3:
您sort_values
可以drop_duplicates
:
df.sort_values('B').drop_duplicates('A')
输出:
A B C
2 1 2 10
4 2 4 4
解决方案 4:
接受的答案(建议idxmin
)不能与管道模式一起使用。管道友好的替代方法是先对值进行排序,然后groupby
使用DataFrame.head
:
data.sort_values('B').groupby('A').apply(DataFrame.head, n=1)
这是可能的,因为默认情况下groupby
保留每个组内的行顺序,这是稳定且有记录的行为(参见pandas.DataFrame.groupby
)。
这种方法还有其他好处:
可以轻松扩展以选择特定列中具有最小值的n行
它可以通过提供另一列(作为列表)来打破平局
.sort_values()
,例如:
data.sort_values(['final_score', 'midterm_score']).groupby('year').apply(DataFrame.head, n=1)
与其他答案一样,需要完全匹配问题所需的结果.reset_index(drop=True)
,从而制作最后的片段:
df.sort_values('B').groupby('A').apply(DataFrame.head, n=1).reset_index(drop=True)
解决方案 5:
我发现答案有点冗长,但效率更高:
这是示例数据集:
data = pd.DataFrame({'A': [1,1,1,2,2,2], 'B':[4,5,2,7,4,6], 'C':[3,4,10,2,4,6]})
data
Out:
A B C
0 1 4 3
1 1 5 4
2 1 2 10
3 2 7 2
4 2 4 4
5 2 6 6
首先,我们将通过 groupby 操作获取 Series 上的最小值:
min_value = data.groupby('A').B.min()
min_value
Out:
A
1 2
2 4
Name: B, dtype: int64
然后,我们将该系列结果合并到原始数据框中
data = data.merge(min_value, on='A',suffixes=('', '_min'))
data
Out:
A B C B_min
0 1 4 3 2
1 1 5 4 2
2 1 2 10 2
3 2 7 2 4
4 2 4 4 4
5 2 6 6 4
最后,我们只得到 B 等于 B_min 的行并删除 B_min,因为我们不再需要它了。
data = data[data.B==data.B_min].drop('B_min', axis=1)
data
Out:
A B C
2 1 2 10
4 2 4 4
我已经在非常大的数据集上对其进行了测试,这是我能使其在合理时间内完成工作的唯一方法。
解决方案 6:
解决方案是,如前所述;
df.loc[df.groupby('A')['B'].idxmin()]
如果解决方案但是如果您收到错误;
"Passing list-likes to .loc or [] with any missing labels is no longer supported.
The following labels were missing: Float64Index([nan], dtype='float64').
See https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#deprecate-loc-reindex-listlike"
就我而言,B 列中有“NaN”值。因此,我使用了“dropna()”,然后它就起作用了。
df.loc[df.groupby('A')['B'].idxmin().dropna()]
解决方案 7:
您还可以对列为B
最小值的行进行布尔索引
out = df[df['B'] == df.groupby('A')['B'].transform('min')]
print(out)
A B C
2 1 2 10
4 2 4 4
扫码咨询,免费领取项目管理大礼包!