Панди python: Видаліть дублікати стовпцями A, зберігаючи рядок з найвищим значенням у стовпці B


162

У мене є фрейм даних з повторюваними значеннями у стовпці А. Я хочу скинути дублікати, зберігаючи рядок з найвищим значенням у стовпці 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 досить добре, щоб це зрозуміти. Будь-які пропозиції?


1
Зауважте, що URL-адреса у запитанні відображається EOL.
DaveL17

Про ідіоматичний та виконавський спосіб див. Це рішення нижче .
Тед Петру

Відповіді:


194

Це займає останнє. Не максимум, хоча:

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 ​​здається мені безпечним, але я не маю уявлення, чи є він насправді.
Little Bobby Tables

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 compsed від '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функцію під час випадання. Наприклад, як я можу скидати лише значення, менші, ніж середнє значення цих повторюваних значень.
Декстер

15

Найпростіше рішення:

Щоб скинути дублікати на основі одного стовпця:

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

Щоб скинути дублікати на основі кількох стовпців:

df = df.drop_duplicates(['col_name1','col_name2','col_name3'], keep='last')

1
Найкраще рішення. Дякую.
Флавіо

Радий допомогти. @Flavio
Gil Baggio

Мій кадр даних містить 10 стовпців, і я використовував цей код, щоб видалити дублікати з трьох стовпців. Однак він видалив рядки з решти стовпців. Чи є можливість видалити дублікати лише для 4 останніх стовпців?
Софія

2
Але ОП хоче зберегти найвище значення у стовпці В. Це може спрацювати, якщо ви відсортуєте перше. Але тоді це в основному відповідь Теда Петру.
Teepeemm

7

Спробуйте це:

df.groupby(['A']).max()

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)

чим це відрізняється від інших постів?
DJK

1

Ось варіант, який мені довелося вирішити, про який варто поділитися: для кожного унікального рядка в columnAмені хотілося знайти найпоширеніший асоційований рядок у columnB.

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

.any()Вибирає один , якщо є зв'язок для режиму. (Зверніть увагу, що використання .any()на Series ints повертає булевий, а не вибір одного з них.)

Для оригінального питання відповідний підхід спрощується

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


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})

Хоча цей фрагмент коду може вирішити питання, зокрема пояснення дійсно допомагає покращити якість вашої публікації. Пам'ятайте, що ви відповідаєте на запитання читачів у майбутньому, і ці люди можуть не знати причини вашої пропозиції щодо коду. Будь ласка, намагайтеся не переповнювати свій код пояснювальними коментарями, це зменшує читабельність і коду, і пояснень!
Martin Tournoij

-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 як безліч, а потім перетворювати її назад здається дуже неефективним способом вирішення цієї проблеми. Я роблю аналіз журналів, тому буду застосовувати це до деяких дуже великих наборів даних.
Абе

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