Python:使用groupby获取具有最大值的行

joj*_*o12 194 python max pandas pandas-groupby

我希望我能为我的问题找到帮助.我正在寻找以下问题的解决方案:

我有一个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**
Run Code Online (Sandbox Code Playgroud)

我的目标是获取组之间计数最大的结果行,例如:

0  MM1  S1   a      **3**
1 3  MM2  S3   mk      **8**
4  MM2  S4   bg     **10** 
8  MM4  S2   uyi      **7**
Run Code Online (Sandbox Code Playgroud)

有人知道我怎么能在熊猫或python中做到这一点?

UPDATE

我没有提供更多关于我的问题的细节.对于我的问题,我想按['Sp','Mt'分组.让我们举个第二个例子:

   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
Run Code Online (Sandbox Code Playgroud)

对于上面的例子,我想得到每个组中count等于max的所有行,例如:

MM2  S4   bg     10
MM4  S2   cb     8
MM4  S2   uyi    8
Run Code Online (Sandbox Code Playgroud)

Zel*_*ny7 262

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(['Mt'], sort=False)['count'].max()
Out[2]:
Mt
S1     3
S3     8
S4    10
S2     7
Name: count
Run Code Online (Sandbox Code Playgroud)

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

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

In [4]: df[idx]
Out[4]:
    Sp  Mt Value  count
0  MM1  S1     a      3
3  MM2  S3    mk      8
4  MM2  S4    bg     10
8  MM4  S2   uyi      7
Run Code Online (Sandbox Code Playgroud)

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

更新

在冰雹玛丽的机会,这是OP要求的:

In [5]: df['count_max'] = df.groupby(['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          8
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
Run Code Online (Sandbox Code Playgroud)

  • @ Zelazny7我正在使用第二种"idx"方法.但是,我只能为每个组提供一个最大值(而且我的数据有一些duplicate-max).有没有办法解决这个问题? (3认同)

Ran*_*ani 142

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

df.sort_values('count', ascending=False).drop_duplicates(['Sp','Mt'])
Run Code Online (Sandbox Code Playgroud)

  • 对于那些对 Python 有点陌生的人,您需要将其分配给一个新变量,它不会更改当前的 df 变量。 (3认同)
  • 当只需要一个具有相同最大值的行时,这是一个很好的答案,但是如果我需要所有具有最大值的行,它将无法按预期工作。 (3认同)
  • 非常好!快速大框架(25k行) (2认同)
  • @Samir 或使用 `inplace = True` 作为 `drop_duplicates` 的参数 (2认同)
  • 我的意思是,如果数据框是 pd.DataFrame({'sp':[1, 1, 2], 'mt':[1, 1, 2], 'value':[2, 2, 3]}, 那么将是 sp==1 和 mt==2 组中具有相同最大值 2 的 2 行。@Rani (2认同)

Sur*_*rya 44

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

In [365]: import pandas as pd

In [366]: 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]
})

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


### 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]: 
   count  mt   sp  val
0      3  S1  MM1    a
2      5  S3  MM1   cb
3      8  S3  MM2   mk
4     10  S4  MM2   bg
8      7  S2  MM4  uyi

### 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])
Run Code Online (Sandbox Code Playgroud)

  • 这是一个很好的解决方案,但是针对不同的问题 (4认同)
  • 这里的提问者指定"我希望得到所有行中每个组中的count等于max",而`idxmax``返回[s]索引的第一次出现的最大请求轴"`根据文档(0.21) . (2认同)

lan*_*ack 28

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

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']]
Run Code Online (Sandbox Code Playgroud)

  • 事实上这要快得多。对于大型数据集来说,转换似乎很慢。 (2认同)
  • 您可以添加注释来解释每一行的作用吗? (2认同)
  • 但是这样做`df [df ['count'] == df ['count_max']]`将丢失NaN行以及上面的答案。 (2认同)

WeN*_*Ben 10

您可能不需要使用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
Run Code Online (Sandbox Code Playgroud)

通过使用也几乎相同的逻辑 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
Run Code Online (Sandbox Code Playgroud)

  • 这不仅比其他解决方案快一个数量级(至少对于我的用例而言),而且它还具有简单链接作为原始数据帧构建的一部分的额外好处。 (2认同)
  • 这似乎不适用于领带。 (2认同)

小智 9

用途groupbyidxmax方法:

  1. 将 col 转移datedatetime

    df['date']=pd.to_datetime(df['date'])
    
    Run Code Online (Sandbox Code Playgroud)
  2. 获取max列的索引date,之后groupyby ad_id

    idx=df.groupby(by='ad_id')['date'].idxmax()
    
    Run Code Online (Sandbox Code Playgroud)
  3. 获取想要的数据:

    df_max=df.loc[idx,]
    
    Run Code Online (Sandbox Code Playgroud)

出[54]:

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
Run Code Online (Sandbox Code Playgroud)


Mau*_*cia 9

总结起来,方法有很多种,但是哪一种更快呢?

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))
Run Code Online (Sandbox Code Playgroud)

最终获胜者是...

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


PAC*_*PAC 7

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

df[df['count'] == df.groupby(['Mt'])['count'].transform(max)]
Run Code Online (Sandbox Code Playgroud)


esc*_*cha 6

尝试在 groupby 对象上使用“nlargest”。使用 nlargest 的优点是它返回从中获取“nlargest item(s)”的行的索引。注意:我们对索引的第二个(1)元素进行切片,因为在这种情况下我们的索引由元组(例如(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('mt')['count'].nlargest(1) # pass 1 since we want the max

df.iloc[[i[1] for i in d.index], :] # pass the index of d as list comprehension
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明


joh*_*mue 5

我一直在许多组操作中使用这种函数式风格:

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
Run Code Online (Sandbox Code Playgroud)

.reset_index(drop=True)通过删除组索引让您回到原始索引。


Sur*_*rya 5

意识到“应用”“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
Run Code Online (Sandbox Code Playgroud)