Отримайте рядки, які мають максимальну кількість у групах, що використовують 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.