python pandas: удаляйте дубликаты по столбцам A, сохраняя строку с наибольшим значением в столбце B

162

У меня есть фрейм данных с повторяющимися значениями в столбце A. Я хочу удалить дубликаты, сохранив строку с самым высоким значением в столбце B.

Итак, это:

A B
1 10
1 20
2 30
2 40
3 10

Должен превратиться в это:

A B
1 20
2 40
3 10

Уэс добавил несколько приятных функций для удаления дубликатов: http://wesmckinney.com/blog/?p=340 . Но AFAICT, он предназначен для точных дубликатов, поэтому нет упоминания о критериях выбора, какие строки будут сохранены.

Я предполагаю, что, вероятно, есть простой способ сделать это - возможно, такой же простой, как сортировка кадра данных перед удалением дубликатов - но я недостаточно хорошо знаю внутреннюю логику groupby, чтобы понять это. Какие-либо предложения?

Abe
источник
1
Обратите внимание, что URL в вопросе появляется EOL.
DaveL17
Для идиоматического и производительного способа, см. Это решение ниже .
Тед Петру

Ответы:

195

Это занимает последнее. Не максимум, хотя:

In [10]: df.drop_duplicates(subset='A', keep="last")
Out[10]: 
   A   B
1  1  20
3  2  40
4  3  10

Вы также можете сделать что-то вроде:

In [12]: df.groupby('A', group_keys=False).apply(lambda x: x.loc[x.B.idxmax()])
Out[12]: 
   A   B
A       
1  1  20
2  2  40
3  3  10
Уэс МакКинни
источник
12
Небольшая справка: colsи take_lastпараметры амортизации и были заменены subsetи keepпараметрами. pandas.pydata.org/pandas-docs/version/0.17.1/generated/…
Jezzamon
как говорит @Jezzamon,FutureWarning: the take_last=True keyword is deprecated, use keep='last' instead
tumultous_rooster
1
Есть ли причина не использовать df.sort_values(by=['B']).drop_duplicates(subset=['A'], keep='last')? Я имею в виду, что sort_values ​​мне кажется безопасным, но я понятия не имею, так ли это на самом деле.
Маленькие столики Бобби
4
Этот ответ сейчас устарел. Смотрите ответ @Ted Petrou ниже.
cxrodgers
Если вы хотите использовать этот код, но в случае более чем одного столбца в group_by, вы можете добавить .reset_index(drop=True) df.groupby(['A','C'], group_keys=False).apply(lambda x: x.ix[x.B.idxmax()]).reset_index(drop=True)Это сбросит индекс, поскольку его значением по умолчанию будет Multindex, составленный из 'A'и'C'
Hamri Said
79

Лучший ответ - это слишком много работы, и он выглядит очень медленным для больших наборов данных. applyмедленный и его следует избегать, если это возможно. ixустарела и ее следует избегать.

df.sort_values('B', ascending=False).drop_duplicates('A').sort_index()

   A   B
1  1  20
3  2  40
4  3  10

Или просто сгруппируйте по всем другим столбцам и возьмите максимум нужного столбца. df.groupby('A', as_index=False).max()

Тед Петру
источник
1
Это на самом деле умный подход. Мне было интересно, если это можно обобщить с помощью какой-то lambaфункции при отбрасывании. Например, как я могу отбросить только значения, меньшие, чем, скажем, среднее из этих повторяющихся значений.
Декстер
16

Самое простое решение:

Чтобы удалить дубликаты на основе одного столбца:

df = df.drop_duplicates('column_name', keep='last')

Чтобы удалить дубликаты на основе нескольких столбцов:

df = df.drop_duplicates(['col_name1','col_name2','col_name3'], keep='last')
Джил Баджо
источник
1
Лучшее решение. Спасибо.
Флавио
Рад помочь. @Flavio
Джил Баджо
Мой фрейм данных имеет 10 столбцов, и я использовал этот код для удаления дубликатов из трех столбцов. Однако он удалил строки из остальных столбцов. Есть ли способ удалить дубликаты только для 4 последних столбцов?
София
2
Но OP хочет сохранить максимальное значение в столбце B. Это может сработать, если вы отсортировали в первую очередь. Но тогда это в основном ответ Теда Петру.
Teepeemm
7

Попробуй это:

df.groupby(['A']).max()
eumiro
источник
1
Вы знаете лучшую идиому для переиндексации, чтобы она выглядела как оригинальный DataFrame? Я пытался понять это, когда ты меня ниндзя. : ^)
DSM
4
Ухоженная. Что если информационный фрейм содержит больше столбцов (например, C, D, E)? Макс, кажется, не работает в этом случае, потому что мы должны указать, что B - единственный столбец, который должен быть развернут.
Абэ
1
@DSM Проверьте ссылку в оригинальном вопросе. Есть некоторый код для переиндексации сгруппированного фрейма данных.
Абэ
5

Я бы отсортировал данные сначала по убыванию столбца B, затем удалил дубликаты для столбца A и оставил первым

df = df.sort_values(by='B', ascending=False)
df = df.drop_duplicates(subset='A', keep="first")

без какой-либо групповой

Нобель
источник
1

Я думаю, что в вашем случае вам не нужна групповая игра. Я бы отсортировал в порядке убывания ваш столбец B, затем удалил дубликаты в столбец A, и если вы хотите, у вас также может быть новый красивый и чистый индекс:

df.sort_values('B', ascending=False).drop_duplicates('A').sort_index().reset_index(drop=True)
whateveros
источник
чем это отличается от других постов?
DJK
1

Вот вариант, который мне нужно было решить, который стоит поделиться: для каждой уникальной строки в columnAя хотел найти наиболее распространенную связанную строку columnB.

df.groupby('columnA').agg({'columnB': lambda x: x.mode().any()}).reset_index()

.any()Выбирает один , если есть связь для режима. (Обратите внимание, что использование .any()для Series intвозвращает логическое значение вместо выбора одного из них.)

Для первоначального вопроса соответствующий подход упрощает

df.groupby('columnA').columnB.agg('max').reset_index(),

mistaben
источник
0

Когда уже заданные сообщения отвечают на вопрос, я внес небольшое изменение, добавив имя столбца, к которому применяется функция max () для лучшей читаемости кода.

df.groupby('A', as_index=False)['B'].max()
Бхагабад Бехера
источник
Пожалуйста, дайте немного больше контекста вашим ответам, объяснив, как они работают и почему они превосходят или дополняют ответы, уже имеющиеся на вопрос. Если они не обеспечивают дополнительную ценность, пожалуйста, воздержитесь от публикации дополнительных ответов на старые вопросы. Наконец, отформатируйте код как блок кода, сделав отступ.
WhoIsJack
0

Самый простой способ сделать это:

# First you need to sort this DF as Column A as ascending and column B as descending 
# Then you can drop the duplicate values in A column 
# Optional - you can reset the index and get the nice data frame again
# I'm going to show you all in one step. 

d = {'A': [1,1,2,3,1,2,3,1], 'B': [30, 40,50,42,38,30,25,32]}
df = pd.DataFrame(data=d)
df

    A   B
0   1   30
1   1   40
2   2   50
3   3   42
4   1   38
5   2   30
6   3   25
7   1   32


df = df.sort_values(['A','B'], ascending =[True,False]).drop_duplicates(['A']).reset_index(drop=True)

df

    A   B
0   1   40
1   2   50
2   3   42
АСР
источник
-1

это также работает:

a=pd.DataFrame({'A':a.groupby('A')['B'].max().index,'B':a.groupby('A')       ['B'].max().values})
Махеш
источник
Хотя этот фрагмент кода может решить вопрос, в том числе объяснение действительно помогает улучшить качество вашего сообщения. Помните, что вы отвечаете на вопрос читателей в будущем, и эти люди могут не знать причин, по которым вы предлагаете код. Также постарайтесь не переполнять ваш код пояснительными комментариями, это снижает удобочитаемость кода и пояснений!
Мартин Турной
-8

Я не собираюсь давать вам полный ответ (я не думаю, что вы все равно ищете синтаксический анализ и запись в файловую часть), но ключевой подсказки должно быть достаточно: используйте set()функцию python , а затем sorted()или в .sort()сочетании с .reverse():

>>> a=sorted(set([10,60,30,10,50,20,60,50,60,10,30]))
>>> a
[10, 20, 30, 50, 60]
>>> a.reverse()
>>> a
[60, 50, 30, 20, 10]
Абхранил Дас
источник
8
Может быть, я ошибаюсь в этом, но преобразование DataFrame pandas в набор, а затем преобразование его обратно представляется очень неэффективным способом решения этой проблемы. Я делаю анализ логов, поэтому я буду применять это к некоторым очень большим наборам данных.
Абэ
Извините, я не слишком много знаю об этом конкретном сценарии, поэтому, возможно, мой общий ответ не окажется слишком эффективным для вашей проблемы.
Абхранил Дас