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

Posted

技术标签:

【中文标题】Pandas GroupBy 并选择特定列中具有最小值的行【英文标题】:Pandas GroupBy and select rows with the minimum value in a specific column 【发布时间】:2019-06-25 12:34:45 【问题描述】:

我按 A 列对我的数据集进行分组,然后想取 B 列中的最小值和 C 列中的相应值。

data = pd.DataFrame('A': [1, 2], 'B':[ 2, 4], 'C':[10, 4])
data  
    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)]

我确信有更直接的方法可以做到这一点。 我在这里看到了许多使用多索引的答案,但我想在不向我的数据框添加多索引的情况下这样做。 感谢您的帮助。

【问题讨论】:

【参考方案1】:

接受的答案(建议idxmin)不能与管道模式一起使用。管道友好的替代方法是首先对值进行排序,然后将 groupbyDataFrame.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) 中所需的结果完全匹配,制作最终的sn-p:

df.sort_values('B').groupby('A').apply(DataFrame.head, n=1).reset_index(drop=True)

【讨论】:

【参考方案2】:

我找到了一个有点啰嗦的答案,但效率更高

这是示例数据集:

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

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

【讨论】:

【参考方案3】:

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

df.loc[df.groupby('A')['B val'].idxmin()]

【讨论】:

【参考方案4】:

我觉得你想多了。只需使用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

【讨论】:

谢谢!这适用于一条简单的线。确实,我不知道 idxmin() 方法存在。 我正在尝试此解决方案,但使用 pandas 1.0.0 时出现错误:Passing list-likes to .loc or [] with any missing labels is no longer supported 。 @cs95 你有什么建议可以解决这个问题吗? @cs95 这会导致每个 A 有一行,如果 A 中的每个值的最小值都有多行怎么办。就像所有科学分数最低的学生一样。

以上是关于Pandas GroupBy 并选择特定列中具有最小值的行的主要内容,如果未能解决你的问题,请参考以下文章

Pandas:按行从 DataFrame 的特定列中选择值

如何在 pandas 中使用过滤条件和 groupby

Python pandas:替换 groupby 对象中的选择值

pandas groupby 按总和聚合特定列,按最常见值聚合其他列

Pandas:仅当特定列中的值以开头时才选择数据框行

(pandas) 根据 groupby 和 column 条件填充 NaN