Получить ряд (ы), которые имеют максимальное количество в группах, используя групповой


245

Как найти все строки в кадре данных pandas, которые имеют максимальное значение для countстолбца, после группировки по ['Sp','Mt']столбцам?

Пример 1: следующий dataFrame, который я группирую по ['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?
Джо Со

stackoverflow.com/questions/18879782/… Может быть полезно
J_Arthur

1
Этот ответ является самым быстрым решением, которое я смог найти: stackoverflow.com/a/21007047/778533
tommy.carstensen

Как и на этот вопрос, кто-нибудь может ответить на это: stackoverflow.com/questions/62069465/… Спасибо.
ds_Abc

Ответы:


326
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

Обратите внимание, что если у вас есть несколько максимальных значений на группу, все будут возвращены.

Обновить

С большой вероятностью, что ОП запрашивает следующее:

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 (Merge, MaximumA, MaximumB): a = Merge [MaximumA] b = Merge [MaximumB] return max (a, b) Merger.groupby ("Search_Term"). Apply (Greater, "Ratio_x "," Ratio_y ")
mathlover

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

Попробовав решение, предложенное Zelazny, на относительно большом DataFrame (~ 400 тыс. Строк) я обнаружил, что оно очень медленное. Вот альтернатива, которую я нашел для ускорения выполнения моих данных на несколько порядков.

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
на самом деле это намного быстрее. преобразование кажется медленным для большого набора данных.
Го

1
Можете ли вы добавить комментарии, чтобы объяснить, что делает каждая строка?
tommy.carstensen

fwiw: я обнаружил, что более элегантно выглядящее решение от @ Zelazny7 заняло много времени для моего набора ~ 100K строк, но это работало довольно быстро. (Я использую устаревшую версию 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

Мало того, что это на порядок быстрее, чем другие решения (по крайней мере, для моего случая использования), оно имеет дополнительное преимущество, заключающееся в простом объединении в цепочку как часть построения исходного кадра данных.
Глина

Я почесал голову, думая, конечно, что это просто, спасибо за ваш блестящий ответ, как всегда, мистер Вэнь.
Датановице

7

Для меня самым простым решением было бы сохранить значение, когда число равно максимуму. Следовательно, достаточно следующей однострочной команды:

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

4

Использование groupbyи idxmaxметоды:

  1. передать col 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.