使用 groupby 获取组中具有最大值的行

Get the row(s) which have the max value in groups using groupby

提问人:jojo12 提问时间:3/29/2013 最后编辑:wjandreajojo12 更新时间:9/9/2023 访问量:636972

问:

如何找到 pandas DataFrame 中按列分组后具有最大值的所有行?count['Sp','Mt']

示例 1:以下 DataFrame:

   Sp   Mt Value   count
0  MM1  S1   a     **3**
1  MM1  S1   n       2
2  MM1  S3   cb    **5**
3  MM2  S3   mk    **8**
4  MM2  S4   bg    **10**
5  MM2  S4   dgd     1
6  MM4  S2   rd      2
7  MM4  S2   cb      2
8  MM4  S2   uyi   **7**

预期输出是获取每个组中计数为最大值的结果行,如下所示:

   Sp   Mt   Value  count
0  MM1  S1   a      **3**
2  MM1  S3   cb     **5**
3  MM2  S3   mk     **8**
4  MM2  S4   bg     **10** 
8  MM4  S2   uyi    **7**

示例 2:

   Sp   Mt   Value  count
4  MM2  S4   bg     10
5  MM2  S4   dgd    1
6  MM4  S2   rd     2
7  MM4  S2   cb     8
8  MM4  S2   uyi    8

预期输出:

   Sp   Mt   Value  count
4  MM2  S4   bg     10
7  MM4  S2   cb     8
8  MM4  S2   uyi    8
Python Pandas DataFrame 分组 BY 最大值

评论

0赞 J_Arthur 9/25/2013
stackoverflow.com/questions/18879782/......可能有用
1赞 tommy.carstensen 3/26/2017
这个答案是我能找到的最快的解决方案:stackoverflow.com/a/21007047/778533

答:

593赞 Zelazny7 3/29/2013 #1

首先,我们可以像这样获得每个组的最大计数:

In [1]: df
Out[1]:
    Sp  Mt Value  count
0  MM1  S1     a      3
1  MM1  S1     n      2
2  MM1  S3    cb      5
3  MM2  S3    mk      8
4  MM2  S4    bg     10
5  MM2  S4   dgd      1
6  MM4  S2    rd      2
7  MM4  S2    cb      2
8  MM4  S2   uyi      7

In [2]: df.groupby(['Sp', 'Mt'])['count'].max()
Out[2]:
Sp   Mt
MM1  S1     3
     S3     5
MM2  S3     8
     S4    10
MM4  S2     7
Name: count, dtype: int64

要获取原始 DF 的索引,您可以执行以下操作:

In [3]: idx = df.groupby(['Sp', 'Mt'])['count'].transform(max) == df['count']

In [4]: df[idx]
Out[4]:
    Sp  Mt Value  count
0  MM1  S1     a      3
2  MM1  S3    cb      5
3  MM2  S3    mk      8
4  MM2  S4    bg     10
8  MM4  S2   uyi      7

请注意,如果每个组有多个最大值,则将返回所有值。


更新

在 Hail Mary 上,这是 OP 要求的:

In [5]: df['count_max'] = df.groupby(['Sp', 'Mt'])['count'].transform(max)

In [6]: df
Out[6]:
    Sp  Mt Value  count  count_max
0  MM1  S1     a      3          3
1  MM1  S1     n      2          3
2  MM1  S3    cb      5          5
3  MM2  S3    mk      8          8
4  MM2  S4    bg     10         10
5  MM2  S4   dgd      1         10
6  MM4  S2    rd      2          7
7  MM4  S2    cb      2          7
8  MM4  S2   uyi      7          7

评论

5赞 3pitt 1/4/2018
@Zelazny7 我正在使用第二种方法。但是,我只能为每组提供一个最大值(并且我的数据有一些重复的最大值)。有没有办法通过您的解决方案解决这个问题?idx
1赞 Woods Chen 4/10/2019
transform方法可能具有池性能,当数据集足够大时,先获取最大值后合并数据帧会更好。
0赞 Prakash Vanapalli 5/31/2023
如前所述,@3pitt提出的原始问题是错误的。
0赞 Zelazny7 6/2/2023
@PrakashVanapalli不,不是
41赞 landewednack 2/12/2014 #2

在相对较大的 DataFrame(~400k 行)上尝试了 Zelazny 建议的解决方案后,我发现它非常慢。这是我发现的一种替代方案,可以在我的数据集上运行速度快几个数量级。

df = pd.DataFrame({
    'sp' : ['MM1', 'MM1', 'MM1', 'MM2', 'MM2', 'MM2', 'MM4', 'MM4', 'MM4'],
    'mt' : ['S1', 'S1', 'S3', 'S3', 'S4', 'S4', 'S2', 'S2', 'S2'],
    'val' : ['a', 'n', 'cb', 'mk', 'bg', 'dgb', 'rd', 'cb', 'uyi'],
    'count' : [3,2,5,8,10,1,2,2,7]
    })

df_grouped = df.groupby(['sp', 'mt']).agg({'count':'max'})

df_grouped = df_grouped.reset_index()

df_grouped = df_grouped.rename(columns={'count':'count_max'})

df = pd.merge(df, df_grouped, how='left', on=['sp', 'mt'])

df = df[df['count'] == df['count_max']]

评论

2赞 goh 7/11/2014
事实上,这要快得多。对于大型数据集,转换似乎很慢。
2赞 tommy.carstensen 3/26/2017
你能添加注释来解释每行的作用吗?
1赞 Roland 5/5/2017
fwiw:我发现 @Zelazny7 中看起来更优雅的解决方案花了很长时间才能为我的 ~100K 行集执行,但这个解决方案运行得很快。(我正在运行一个现在已经过时的 0.13.0,这可能是缓慢的原因)。
3赞 Qy Zuo 7/20/2017
但是这样做会丢失 NaN 行,以及上面的答案。df[df['count'] == df['count_max']]
0赞 Gerard 9/18/2018
我强烈建议使用这种方法,对于更大的数据帧,使用 .appy() 或 .agg() 要快得多。
14赞 PAC 7/2/2015 #3

对我来说,最简单的解决方案是在计数等于最大值时保留值。因此,以下一行命令就足够了:

df[df['count'] == df.groupby(['Mt'])['count'].transform(max)]

评论

0赞 wjandrea 2/19/2023
这与 Zelazny7 的答案相同。请不要发布重复的答案。
313赞 Rani 11/16/2016 #4

您可以按计数对 dataFrame 进行排序,然后删除重复项。我认为这更容易:

df.sort_values('count', ascending=False).drop_duplicates(['Sp','Mt'])

评论

10赞 Nolan Conaway 9/28/2017
很好!使用大型帧(25k 行)快速
3赞 Tyler 12/28/2018
对于那些不熟悉 Python 的人,您需要将其分配给一个新变量,它不会更改当前的 df 变量。
4赞 TMrtSmith 2/4/2019
@Samir或用作参数inplace = Truedrop_duplicates
13赞 Woods Chen 4/10/2019
当只需要具有相同最大值的一行时,这是一个很好的答案,但是如果我需要所有具有最大值的行,它将无法按预期工作。
3赞 Woods Chen 4/11/2019
我的意思是,如果数据帧是 pd。DataFrame({'sp':[1, 1, 2], 'mt':[1, 1, 2], 'value':[2, 2, 3]},则 sp==1 和 mt==2 的组中将有 2 行具有相同的最大值 2。 @Rani
120赞 Surya 7/7/2017 #5

简单的解决方案是应用该函数来获取具有最大值的行的索引。 这将过滤掉组中具有最大值的所有行。idxmax()

In [367]: df
Out[367]: 
    sp  mt  val  count
0  MM1  S1    a      3
1  MM1  S1    n      2
2  MM1  S3   cb      5
3  MM2  S3   mk      8
4  MM2  S4   bg     10
5  MM2  S4  dgb      1
6  MM4  S2   rd      2
7  MM4  S2   cb      2
8  MM4  S2  uyi      7


# Apply idxmax() and use .loc() on dataframe to filter the rows with max values:
In [368]: df.loc[df.groupby(["sp", "mt"])["count"].idxmax()]
Out[368]: 
    sp  mt  val  count
0  MM1  S1    a      3
2  MM1  S3   cb      5
3  MM2  S3   mk      8
4  MM2  S4   bg     10
8  MM4  S2  uyi      7


# Just to show what values are returned by .idxmax() above:
In [369]: df.groupby(["sp", "mt"])["count"].idxmax().values
Out[369]: array([0, 2, 3, 4, 8])

评论

18赞 Max Power 12/19/2017
这里的提问者指定了,而根据文档(0.21)。"I want to get ALL the rows where count equals max in each group"idxmaxReturn[s] index of first occurrence of maximum over requested axis"
11赞 Carlos Souza 10/28/2019
这是一个很好的解决方案,但针对不同的问题
17赞 blueear 7/24/2018 #6

用途及方法:groupbyidxmax

  1. 将 col 转移到 :datedatetime

    df['date'] = pd.to_datetime(df['date'])
    
  2. 获取 的 列 的索引 ,之后:maxdategroupyby ad_id

    idx = df.groupby(by='ad_id')['date'].idxmax()
    
  3. 获取所需数据:

    df_max = df.loc[idx,]
    
   ad_id  price       date
7     22      2 2018-06-11
6     23      2 2018-06-22
2     24      2 2018-06-30
3     28      5 2018-06-22

评论

1赞 wjandrea 2/19/2023
date列???这似乎是另一个问题的答案。否则,它与 Surya 的答案重复,并且存在同样的问题:如果出现平局,则只保留第一次出现。
2赞 George Liu 8/9/2018 #7
df = pd.DataFrame({
'sp' : ['MM1', 'MM1', 'MM1', 'MM2', 'MM2', 'MM2', 'MM4', 'MM4','MM4'],
'mt' : ['S1', 'S1', 'S3', 'S3', 'S4', 'S4', 'S2', 'S2', 'S2'],
'val' : ['a', 'n', 'cb', 'mk', 'bg', 'dgb', 'rd', 'cb', 'uyi'],
'count' : [3,2,5,8,10,1,2,2,7]
})

df.groupby(['sp', 'mt']).apply(lambda grp: grp.nlargest(1, 'count'))
77赞 BENY 1/4/2019 #8

您可能不需要这样做,但两者都使用groupby()sort_values + drop_duplicates

df.sort_values('count').drop_duplicates(['Sp', 'Mt'], keep='last')
Out[190]: 
    Sp  Mt Value  count
0  MM1  S1     a      3
2  MM1  S3    cb      5
8  MM4  S2   uyi      7
3  MM2  S3    mk      8
4  MM2  S4    bg     10

也几乎相同的逻辑通过使用tail

df.sort_values('count').groupby(['Sp', 'Mt']).tail(1)
Out[52]: 
    Sp  Mt Value  count
0  MM1  S1     a      3
2  MM1  S3    cb      5
8  MM4  S2   uyi      7
3  MM2  S3    mk      8
4  MM2  S4    bg     10

评论

2赞 Clay 8/9/2019
这不仅比其他解决方案快一个数量级(至少对于我的用例而言),而且它还有一个额外的好处,即简单地将链接作为原始数据帧构建的一部分。
1赞 Hunaphu 2/25/2021
当你看到这个答案时,你就会意识到其他人都是错的。这显然是做到这一点的方法。谢谢。
0赞 Antoine 8/20/2021
为了忽略 s,应该添加 to。na_position="first"sort_valuesNaN
0赞 John Stud 2/10/2022
我发现这对于我的数百万行 DF 来说很快。
2赞 Benjamin Ziepert 12/26/2022
这似乎不适用于领带。
4赞 joh-mue 1/14/2019 #9

我一直在将这种函数样式用于许多组操作:

df = pd.DataFrame({
    'Sp': ['MM1', 'MM1', 'MM1', 'MM2', 'MM2', 'MM2', 'MM4', 'MM4', 'MM4'],
    'Mt': ['S1', 'S1', 'S3', 'S3', 'S4', 'S4', 'S2', 'S2', 'S2'],
    'Val': ['a', 'n', 'cb', 'mk', 'bg', 'dgb', 'rd', 'cb', 'uyi'],
    'Count': [3, 2, 5, 8, 10, 1, 2, 2, 7]
})

(df.groupby(['Sp', 'Mt'])
   .apply(lambda group: group[group['Count'] == group['Count'].max()])
   .reset_index(drop=True))

    Sp  Mt  Val  Count
0  MM1  S1    a      3
1  MM1  S3   cb      5
2  MM2  S3   mk      8
3  MM2  S4   bg     10
4  MM4  S2  uyi      7

.reset_index(drop=True)通过删除 group-index 返回到原始索引。

评论

0赞 wjandrea 2/19/2023
而不是 ,您可以考虑 ,用reset_index.droplevel([0]).groupby(..., as_index=False)
6赞 Surya 4/10/2019 #10

意识到将“”nlargest“”应用于 groupby 对象同样有效:

其他优势 - 如果需要,还可以获取前 n 个值

In [85]: import pandas as pd

In [86]: df = pd.DataFrame({
    ...: 'sp' : ['MM1', 'MM1', 'MM1', 'MM2', 'MM2', 'MM2', 'MM4', 'MM4','MM4'],
    ...: 'mt' : ['S1', 'S1', 'S3', 'S3', 'S4', 'S4', 'S2', 'S2', 'S2'],
    ...: 'val' : ['a', 'n', 'cb', 'mk', 'bg', 'dgb', 'rd', 'cb', 'uyi'],
    ...: 'count' : [3,2,5,8,10,1,2,2,7]
    ...: })

## Apply nlargest(1) to find the max val df, and nlargest(n) gives top n values for df:
In [87]: df.groupby(["sp", "mt"]).apply(lambda x: x.nlargest(1, "count")).reset_index(drop=True)
Out[87]:
   count  mt   sp  val
0      3  S1  MM1    a
1      5  S3  MM1   cb
2      8  S3  MM2   mk
3     10  S4  MM2   bg
4      7  S2  MM4  uyi

评论

0赞 Benjamin Ziepert 12/26/2022
这似乎不适用于领带。
9赞 Kweweli 7/9/2019 #11

尝试在 groupby 对象上使用 nlargest 。优点是它返回从中获取“nlargest 项”的行,我们可以获取它们的索引。

在本例中,我们想要最大值并包含重复的最大值。n=1keep='all'

注意:我们对索引的最后一个 (-1) 元素进行切片,因为在这种情况下,我们的索引由元组组成(例如 )。('MM1', 'S1', 0)

df = pd.DataFrame({
    'Sp': ['MM1', 'MM1', 'MM1', 'MM2', 'MM2', 'MM2', 'MM4', 'MM4','MM4'],
    'Mt': ['S1', 'S1', 'S3', 'S3', 'S4', 'S4', 'S2', 'S2', 'S2'],
    'Val': ['a', 'n', 'cb', 'mk', 'bg', 'dgb', 'rd', 'cb', 'uyi'],
    'count': [3, 2, 5, 8, 10, 1, 2, 2, 7]
})

d = df.groupby(['Sp', 'Mt'])['count'].nlargest(1, keep='all')

df.loc[[i[-1] for i in d.index]]
    Sp  Mt  Val  count
0  MM1  S1    a      3
2  MM1  S3   cb      5
3  MM2  S3   mk      8
4  MM2  S4   bg     10
8  MM4  S2  uyi      7

评论

0赞 wjandrea 2/19/2023
如果输入具有 MultiIndex,则最好执行更类似于 .我不确定。df.loc[d.droplevel(['Sp', 'Mt']).index]
0赞 wjandrea 4/3/2023
你可以用更惯用的方式做到这一点。df.loc[d.index.get_level_values(-1)]
0赞 Prakash Vanapalli 5/31/2023
这是正确的,但在具有 ~100k 行的大型数据集上非常非常慢。
9赞 Mauro Mascia 3/2/2021 #12

综上所述,有很多方法,但哪一种更快?

import pandas as pd
import numpy as np
import time

df = pd.DataFrame(np.random.randint(1,10,size=(1000000, 2)), columns=list('AB'))

start_time = time.time()
df1idx = df.groupby(['A'])['B'].transform(max) == df['B']
df1 = df[df1idx]
print("---1 ) %s seconds ---" % (time.time() - start_time))

start_time = time.time()
df2 = df.sort_values('B').groupby(['A']).tail(1)
print("---2 ) %s seconds ---" % (time.time() - start_time))

start_time = time.time()
df3 = df.sort_values('B').drop_duplicates(['A'],keep='last')
print("---3 ) %s seconds ---" % (time.time() - start_time))

start_time = time.time()
df3b = df.sort_values('B', ascending=False).drop_duplicates(['A'])
print("---3b) %s seconds ---" % (time.time() - start_time))

start_time = time.time()
df4 = df[df['B'] == df.groupby(['A'])['B'].transform(max)]
print("---4 ) %s seconds ---" % (time.time() - start_time))

start_time = time.time()
d = df.groupby('A')['B'].nlargest(1)
df5 = df.iloc[[i[1] for i in d.index], :]
print("---5 ) %s seconds ---" % (time.time() - start_time))

获胜者是......

  • --1 ) 0.03337574005126953 秒 ---
  • --2 ) 0.1346898078918457 秒 ---
  • --3 ) 0.10243558883666992 秒 ---
  • --3b) 0.1004343032836914 秒 ---
  • --4 ) 0.028397560119628906 秒 ---
  • --5 ) 0.07552886009216309 秒 ---

评论

1赞 Jon 8/2/2022
很棒的工作,包括所有这些建议中缺少的计时器。还有一些,重要的是,将其添加到更大的数据集上也很好。使用 280 万行和不同数量的重复项显示出一些惊人的数字。特别是在大数据上使用 nlargest 会失败(比如慢 100 倍以上)。对于我的数据来说,最快的是排序,然后删除重复项(删除除最后一项之外的所有内容比排序降序并删除除第一名外的所有内容略快)
4赞 nbertagnolli 4/21/2021 #13

如果对 DataFrame 进行排序,则该排序将保留在 groupby 中。然后,您可以只抓取第一个或最后一个元素并重置索引。

df = pd.DataFrame({
    'sp' : ['MM1', 'MM1', 'MM1', 'MM2', 'MM2', 'MM2', 'MM4', 'MM4','MM4'],
    'mt' : ['S1', 'S1', 'S3', 'S3', 'S4', 'S4', 'S2', 'S2', 'S2'],
    'val' : ['a', 'n', 'cb', 'mk', 'bg', 'dgb', 'rd', 'cb', 'uyi'],
    'count' : [3,2,5,8,10,1,2,2,7]
})

df.sort_values("count", ascending=False).groupby(["sp", "mt"]).first().reset_index()

评论

0赞 wjandrea 9/9/2023
这实际上与 Rani 的答案BENY 的答案相同,只是使用的方法略有不同。
-1赞 upuil 7/7/2022 #14
df.loc[df.groupby('mt')['count'].idxmax()]

如果索引不是唯一的,则可能需要先执行此步骤。dfdf.reset_index(inplace=True)

评论

0赞 wjandrea 9/9/2023
这与 Surya 的答案重复,除了关于非唯一索引的观点。
3赞 Jon 8/2/2022 #15

其中许多都是很好的答案,但为了帮助显示可伸缩性,在具有不同数量重复的 280 万行上显示出一些惊人的差异。对于我的数据来说,最快的是排序,然后删除重复项(删除除最后一项之外的所有内容比排序降序并删除除第一名外的所有内容略快)

  1. 升序排序,删除重复保留最后(2.22 秒)
  2. 降序排序,先删除复制保留(2.32 秒)
  3. 在 loc 函数中变换最大值(3.73 秒)
  4. 转换最大存储 IDX,然后使用 loc select 作为第二步(3.84 秒)
  5. 分组使用尾部 (8.98 s)
  6. IDMax 与 groupby,然后使用 loc select 作为第二步 (95.39 s)
  7. IDMax 在 loc select 内带有 groupby (95.74 s)
  8. NLargest(1) 然后使用 iloc select 作为第二步 (> 35000 秒) - 运行了一夜后没有完成
  9. NLargest(1) within iloc select (> 35000 s ) - 运行过夜后未完成

正如你所看到的,排序比转换快 1/3,比 groupby 快 75%。其他一切都慢了 40 倍。在小型数据集中,这可能无关紧要,但正如您所看到的,这可能会对大型数据集产生重大影响。

评论

0赞 8/29/2022
对于那些使用这些方法之一的人来说,这是很好的性能指南!