提问人:Wendy 提问时间:2/1/2019 最后编辑:Mykola ZotkoWendy 更新时间:10/16/2022 访问量:53972
Pandas GroupBy 并选择特定列中具有最小值的行
Pandas GroupBy and select rows with the minimum value in a specific column
问:
我有一个包含 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 的答案,我宁愿避免这样做。
谢谢你的帮助。
答:
我觉得你想多了。只需使用和:groupby
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
评论
Passing list-likes to .loc or [] with any missing labels is no longer supported
有类似的情况,但列标题更复杂(例如“B val”),在这种情况下需要这样做:
df.loc[df.groupby('A')['B val'].idxmin()]
我发现了一个更冗长的答案,但效率要高得多:
以下是示例数据集:
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
我已经在非常大的数据集上测试了它,这是我在合理的时间内让它工作的唯一方法。
评论
接受的答案(建议)不能与管道模式一起使用。管道友好的替代方法是首先对值进行排序,然后与idxmin
groupby
DataFrame.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)
评论
data.sort_values('B').groupby('A').head(1)
如前所述,解决方案是;
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()]
您还可以对 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
评论
您可以sort_values
并drop_duplicates
:
df.sort_values('B').drop_duplicates('A')
输出:
A B C
2 1 2 10
4 2 4 4
评论