Отримайте рядки, які мають максимальну кількість у групах, що використовують groupby


244

Як знайти групи рядків у фреймі даних панди, які мають максимальне значення для 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**
1 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, у кожній групі, наприклад:

MM2  S4   bg     10
MM4  S2   cb     8
MM4  S2   uyi    8

У якому форматі знаходиться ваш кадр даних?
— Девід Робінсон

2
Я не розумію. Що саме являє собою група? Чому починається другий рядок в результаті 1 3?
— Jo So

stackoverflow.com/questions/18879782/… Може бути корисним
— J_Arthur

1
Ця відповідь є найшвидшим рішенням я міг би знайти: stackoverflow.com/a/21007047/778533
— tommy.carstensen

Подібно до цього питання, чи не може хто-небудь відповісти на це: stackoverflow.com/questions/62069465/… Дякую.
— ds_Abc

Відповіді:


325
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

Щоб отримати індекси початкового коду, можна зробити:

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

Зауважте, що якщо у вас кілька максимальних значень на групу, всі будуть повернуті.

Оновлення

З великим шансом на те, що ОП вимагає:

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

@ Zelazny7, чи є спосіб прийняти цю відповідь, щоб застосувати до групування по стовпчику, а потім переглянувши 2 стовпчики і зробивши максимум з них, щоб отримати більше двох? Я не можу так працювати. На даний момент у мене є: def Greater (Злиття, максимальнийA, максимальнийB): a = Злиття [максимальнийA] b = Об’єднання [максимальнийB] повернення макс. (A, b) Merger.groupby ("Search_Term"). Застосувати (Greater, "Ratio_x "," Ratio_y ")
— матхловер

3
@ Zelazny7 Я використовую другий idxпідхід. Але я можу дозволити собі лише один максимум для кожної групи (і мої дані мають кілька дублікатів-максимумів). чи є спосіб подолати це за допомогою свого рішення?
— 3pitt

насправді це не працює для мене. Я не можу відслідкувати проблему, тому що фреймворк, якщо вийти з великих, але рішення від @Rani працює добре
— Ладенков Владислав

Привіт Zealzny! Якщо я хочу взяти верхній 3 максимальний рядок замість одного максимуму, як я можу налаштувати ваш код?
— Зефір

transformметод може мати продуктивність пулу, коли набір даних досить великий, спочатку отримайте максимальне значення, тоді об'єднайте кадри даних буде краще.
— Вудс Чен

170

Ви можете сортувати DataFrame за кількістю, а потім видаляти дублікати. Я думаю, що це простіше:

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

4
Дуже хороша! Швидкий з великокаліберними кадрами (25 к. Рядів)
— Нолан Конавей

2
Для тих, хто з Python дещо новий, вам потрібно буде призначити цю нову змінну, вона не змінює поточну змінну df.
— Тайлер

1
@Samir або використовувати inplace = Trueяк аргумент дляdrop_duplicates
— TMrtSmith

5
Це чудова відповідь, коли потрібен лише один із рядків з однаковими максимальними значеннями, однак він не буде працювати, як очікувалося, якщо мені потрібні всі рядки з максимальними значеннями.
— Вудс Чен

1
@WoodsChen, він скидає дублікати [sp, mt], тому у вашому прикладі вихід повинен бути лише одним рядком.
— Рані

54

Простим рішенням було б застосувати: 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])

4
Тут опитували запитувача "I want to get ALL the rows where count equals max in each group", тоді як idxmax Return[s] index of first occurrence of maximum over requested axis"згідно з документами (0,21).
— Макс. Потужність

1
Це чудове рішення, але для іншої проблеми
— Карлос Суза

33

Спробувавши рішення, запропоноване Зелазним, на відносно великій 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']]

1
дійсно це набагато швидше. Трансформація здається повільною для великих наборів даних.
— goh

1
Чи можете ви додати коментарі, щоб пояснити, що робить кожен рядок?
— tommy.carstensen

fwiw: Я знайшов більш елегантне на вигляд рішення від @ Zelazny7, який знадобився тривалий час, щоб виконати мій набір ~ 100 К рядків, але цей пробіг досить швидко. (Я працюю тепер застарілим способом 0.13.0, який може пояснювати повільність).
— Роланд

2
Але при цьому df[df['count'] == df['count_max']]ви втратите рядки NaN, а також відповіді вище.
— Qy Zuo

Я настійно пропоную використовувати цей підхід, для великих кадрів даних набагато швидше використовувати .appy () або .agg ().
— Туя Д. Сердан

18

Можливо, вам не потрібно буде робити з групою, використовуючи 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

Мало того, що це на порядок швидше, ніж інші рішення (принаймні, для мого випадку використання), він має додаткову перевагу просто ланцюжком у складі оригінальної структури даних.
— Глина

Я чухав голову, думаючи, що це просто, дякую за вашу блискучу відповідь, як завжди містер Вень.
— Datanovice

7

Для мене найпростішим рішенням буде зберігати значення, коли кількість дорівнює максимальній. Тому достатньо наступної однієї рядкової команди:

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

4

Використання groupbyта idxmaxметоди:

  1. передачі сідловини dateв datetime:

    df['date']=pd.to_datetime(df['date'])
  2. отримати індекс maxстовпця dateпісля groupyby ad_id:

    idx=df.groupby(by='ad_id')['date'].idxmax()
  3. отримати потрібні дані:

    df_max=df.loc[idx,]

Вихід [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

2
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'))

2

Зрозумівши, що "застосувати" "найвеличніший" до об'єкта групи працює так само добре:

Додаткова перевага - також можна отримати найвищі 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

2

Спробуйте використовувати "nlargest" на об'єкті groupby. Перевага використання nlargest полягає в тому, що він повертає індекс рядків, з яких отримано "найменший елемент (и)". Примітка: ми нарізаємо другий (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

введіть тут опис зображення


1

Я використовую цей функціональний стиль для багатьох групових операцій:

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) повертає вас до початкового індексу, скидаючи груповий індекс.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.