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

我如何找到熊猫DataFrame中有count列的最大值的所有行,由['Sp','Mt']列分组后?

示例1:下面的数据帧,我用['Sp','Mt']分组:

   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**

预期输出是得到每组中数量最大的结果行,如下所示:

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']来分组:

   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

预期输出是获得每组中count等于max的所有行,如下所示:

   Sp   Mt   Value  count
4  MM2  S4   bg     10
7  MM4  S2   cb     8
8  MM4  S2   uyi    8
541438 次浏览
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

要获得原始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

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

更新

碰碰运气,这就是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

在尝试了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']]

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

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

您可以根据计数对dataFrame进行排序,然后删除重复项。我认为这样更简单:

df.sort_values('count', ascending=False).drop_duplicates(['Sp','Mt'])
简单的解决方案是应用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])

使用groupbyidxmax方法:

  1. transfer col date to datetime:

    df['date']=pd.to_datetime(df['date'])
    
  2. get the index of max of column date, after groupyby ad_id:

    idx=df.groupby(by='ad_id')['date'].idxmax()
    
  3. get the wanted data:

    df_max=df.loc[idx,]
    

Out[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
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'))

你可能不需要执行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

我在很多组操作中都使用了这种函数风格:

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('Mt')\
.apply(lambda group: group[group.Count == group.Count.max()])\
.reset_index(drop=True)


sp  mt  val  count
0  MM1  S1    a      3
1  MM4  S2  uyi      7
2  MM2  S3   mk      8
3  MM2  S4   bg     10

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

意识到“应用”“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

试着用" ngest"在groupby对象上。使用nbiggest的优点是它返回“第n个最大项”所在行的索引。从。 注意:我们对索引的第二个(1)元素进行切片,因为在这种情况下,我们的索引由元组(例如。(s1 0)) . < / p >

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

enter image description here

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

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秒——

如果你对你的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()

df.loc[df.groupby('mt')['count'].idxmax()]

如果df索引不是唯一的,你可能首先需要这个步骤df.reset_index(inplace=True)

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

  1. 升序排序,删除副本保持最后(2.22秒)
  2. 排序降序,先删除重复保持(2.32秒)
  3. 在loc函数内转换Max (3.73 s)
  4. 转换最大存储IDX然后使用loc选择作为第二步(3.84 s)
  5. 使用Tail分组(8.98 s)
  6. IDMax, groupby,然后使用loc select作为第二步(95.39秒)
  7. 在loc select中包含groupby的IDMax (95.74 s)
  8. nmaximum(1)然后使用iloc select作为第二步(>35000 s) -在跑了一夜之后没有完成
  9. 在iloc select (>35000 s) -在跑了一夜之后没有完成

如你所见,Sort比transform快1/3,比groupby快75%。其他的都要慢40倍。在小型数据集中,这可能无关紧要,但如您所见,这可能会对大型数据集产生重大影响。