Pandas GroupBy 并选择特定列中具有最小值的行

Pandas GroupBy and select rows with the minimum value in a specific column

提问人:Wendy 提问时间:2/1/2019 最后编辑:Mykola ZotkoWendy 更新时间:10/16/2022 访问量:53972

问:

我有一个包含 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 的答案,我宁愿避免这样做。

谢谢你的帮助。

Python pandas group-by

评论


答:

84赞 cs95 2/1/2019 #1

我觉得你想多了。只需使用和:groupbyidxmin

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

评论

0赞 Eve Edomenko 8/18/2020
我正在尝试此解决方案,但在 pandas 1.0.0 中出现错误:.@cs95你有什么建议如何解决这个问题吗?Passing list-likes to .loc or [] with any missing labels is no longer supported
4赞 A-dude 8/5/2021
@cs95这导致每个 A 有一行,如果 A 中每个值都有多行的最小值,就像所有在科学中得分最低的学生一样。
14赞 Juho 12/15/2019 #2

有类似的情况,但列标题更复杂(例如“B val”),在这种情况下需要这样做:

df.loc[df.groupby('A')['B val'].idxmin()]
3赞 Sergio Polimante 9/24/2021 #3

我发现了一个更冗长的答案,但效率要高得多

以下是示例数据集:

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

我已经在非常大的数据集上测试了它,这是我在合理的时间内让它工作的唯一方法。

评论

0赞 Niccola Tartaglia 5/20/2022
非常好的解决方案,易于遵循。
5赞 krassowski 2/16/2022 #4

接受的答案(建议)不能与管道模式一起使用。管道友好的替代方法是首先对值进行排序,然后与idxmingroupbyDataFrame.head

data.sort_values('B').groupby('A').apply(DataFrame.head, n=1)

这是可能的,因为默认情况下会保留每个组中的行顺序,这是稳定且有记录的行为(参见 pandas。DataFrame.groupby)。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)

评论

2赞 igorkf 5/6/2022
不错的答案。我会补充说我以这种方式做到了,并且似乎以同样的方式工作:data.sort_values('B').groupby('A').head(1)
1赞 yalin 8/15/2022 #5

如前所述,解决方案是;

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()]
1赞 Ynjxsjmh 8/17/2022 #6

您还可以对 column 为最小值的行进行布尔索引B

out = df[df['B'] == df.groupby('A')['B'].transform('min')]
print(out)

   A  B   C
2  1  2  10
4  2  4   4

评论

0赞 Mikhail Genkin 8/24/2023
谢谢,这就是我一直在寻找的
6赞 Mykola Zotko 10/4/2022 #7

您可以sort_valuesdrop_duplicates

df.sort_values('B').drop_duplicates('A')

输出:

   A  B   C
2  1  2  10
4  2  4   4