pandy pythonowe: Usuń duplikaty według kolumn A, zachowując wiersz o najwyższej wartości w kolumnie B.

162

Mam ramkę danych z powtarzającymi się wartościami w kolumnie A. Chcę usunąć duplikaty, zachowując wiersz z najwyższą wartością w kolumnie B.

Więc to:

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

Powinien zmienić się w to:

A B
1 20
2 40
3 10

Wes dodał kilka fajnych funkcji do usuwania duplikatów: http://wesmckinney.com/blog/?p=340 . Ale AFAICT, jest przeznaczony do dokładnych duplikatów, więc nie ma wzmianki o kryteriach wyboru, które wiersze mają zostać zachowane.

Domyślam się, że prawdopodobnie istnieje łatwy sposób na zrobienie tego - może tak prosty jak posortowanie ramki danych przed usunięciem duplikatów - ale nie znam wewnętrznej logiki Groupby na tyle dobrze, aby to rozgryźć. Jakieś sugestie?

Abe
źródło
1
Zwróć uwagę, że adres URL w pytaniu pojawia się jako EOL.
DaveL17
Aby zapoznać się z idiomatycznym i wydajnym sposobem, zobacz poniższe rozwiązanie .
Ted Petrou

Odpowiedzi:

194

To trwa ostatnie. Jednak nie maksimum:

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

Możesz też zrobić coś takiego:

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
Wes McKinney
źródło
12
Mała uwaga: parametry colsi take_lastsą amortyzowane i zostały zastąpione parametrami subseti keep. pandas.pydata.org/pandas-docs/version/0.17.1/generated/…
Jezzamon
jak mówi @Jezzamon,FutureWarning: the take_last=True keyword is deprecated, use keep='last' instead
tumultous_rooster
1
Czy jest powód, aby nie używać df.sort_values(by=['B']).drop_duplicates(subset=['A'], keep='last')? Chodzi mi o to, że te wartości sort_values ​​wydają mi się bezpieczne, ale nie mam pojęcia, czy tak jest.
Little Bobby Tables
4
Ta odpowiedź jest już nieaktualna. Zobacz odpowiedź @Ted Petrou poniżej.
cxrodgers
Jeśli chcesz użyć tego kodu, ale z przypadkiem więcej niż jednej kolumny w group_by, możesz dodać .reset_index(drop=True) df.groupby(['A','C'], group_keys=False).apply(lambda x: x.ix[x.B.idxmax()]).reset_index(drop=True)Spowoduje to zresetowanie indeksu, ponieważ jego domyślną wartością będzie Multindex compsed from 'A'i'C'
Hamri Said
79

Najlepsza odpowiedź to za dużo pracy i wygląda na bardzo powolną w przypadku większych zbiorów danych. applyjest powolny i należy go unikać, jeśli to możliwe. ixjest przestarzały i należy go również unikać.

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

   A   B
1  1  20
3  2  40
4  3  10

Lub po prostu pogrupuj według wszystkich innych kolumn i wybierz maksimum z kolumny, której potrzebujesz. df.groupby('A', as_index=False).max()

Ted Petrou
źródło
1
W rzeczywistości jest to mądre podejście. Zastanawiałem się, czy można to uogólnić za pomocą jakiejś lambafunkcji podczas upuszczania. Na przykład, jak mogę upuścić tylko wartości mniejsze niż powiedzmy średnia tych zduplikowanych wartości.
Dexter,
15

Najprostsze rozwiązanie:

Aby usunąć duplikaty na podstawie jednej kolumny:

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

Aby usunąć duplikaty na podstawie wielu kolumn:

df = df.drop_duplicates(['col_name1','col_name2','col_name3'], keep='last')
Gil Baggio
źródło
1
Najlepsze rozwiązanie. Dzięki.
Flavio,
Miło, że mogłem pomóc. @Flavio
Gil Baggio
Moja ramka danych ma 10 kolumn i użyłem tego kodu do usunięcia duplikatów z trzech kolumn. Jednak usunął wiersze z pozostałych kolumn. Czy istnieje sposób na usunięcie duplikatów tylko z 4 ostatnich kolumn?
Sofia
2
Ale OP chce zachować najwyższą wartość w kolumnie B. Może to zadziałać, jeśli najpierw posortujesz. Ale to w zasadzie odpowiedź Teda Petrou.
Teepeemm
7

Spróbuj tego:

df.groupby(['A']).max()
eumiro
źródło
1
Czy znasz najlepszy idiom do ponownego zindeksowania tego, aby wyglądał jak oryginalny DataFrame? Próbowałem to rozgryźć, kiedy ty mnie ninja. : ^)
DSM
4
Schludny. Co jeśli ramka danych zawiera więcej kolumn (np. C, D, E)? Wydaje się, że Max nie działa w tym przypadku, ponieważ musimy określić, że B jest jedyną kolumną, którą należy zmaksymalizować.
Abe
1
@DSM Sprawdź link w pierwotnym pytaniu. Istnieje kod do ponownego zindeksowania zgrupowanej ramki danych.
Abe
5

Najpierw posortowałbym ramkę danych w kolejności malejącej z kolumny B, a następnie upuścił duplikaty dla kolumny A i zostawiłbym jako pierwszy

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

bez żadnego groupby

Nobel
źródło
1

Myślę, że w twoim przypadku tak naprawdę nie potrzebujesz grupy. Sortuję według malejącej kolejności kolumny B, a następnie upuszczam duplikaty w kolumnie A, a jeśli chcesz, możesz również mieć nowy ładny i czysty indeks, taki jak ten:

df.sort_values('B', ascending=False).drop_duplicates('A').sort_index().reset_index(drop=True)
cokolwiek
źródło
czym różni się to od innych postów?
DJK
1

Oto wariacja, którą musiałem rozwiązać, którą warto udostępnić: dla każdego unikalnego ciągu columnAchciałem znaleźć najpopularniejszy powiązany ciąg columnB.

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

.any()Wybiera jedną czy jest remis dla trybu. (Zwróć uwagę, że użycie .any()serii ints zwraca wartość logiczną, a nie wybranie jednej z nich).

W przypadku pierwotnego pytania odpowiednie podejście upraszcza się do

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

mistaben
źródło
0

Gdy już podane posty odpowiadają na to pytanie, dokonałem małej zmiany dodając nazwę kolumny, na której zastosowano funkcję max () dla lepszej czytelności kodu.

df.groupby('A', as_index=False)['B'].max()
Bhagabat Behera
źródło
Proszę podać trochę więcej kontekstu swoim odpowiedziom, wyjaśniając, jak działają i dlaczego są lepsze lub uzupełniające w stosunku do odpowiedzi już dostępnych na pytanie. Jeśli nie stanowią one wartości dodanej, prosimy o powstrzymanie się od zamieszczania dodatkowych odpowiedzi na stare pytania. Na koniec sformatuj swój kod jako blok kodu, wciskając go.
WhoIsJack
0

Najłatwiej to zrobić:

# 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
rra
źródło
-1

to też działa:

a=pd.DataFrame({'A':a.groupby('A')['B'].max().index,'B':a.groupby('A')       ['B'].max().values})
Mahesh
źródło
Chociaż ten fragment kodu może rozwiązać problem, dołączenie wyjaśnienia naprawdę pomaga poprawić jakość Twojego posta. Pamiętaj, że odpowiadasz na pytanie do czytelników w przyszłości, a osoby te mogą nie znać powodów, dla których zaproponowałeś kod. Prosimy również starać się nie zatłaczać kodu komentarzami wyjaśniającymi, ponieważ zmniejsza to czytelność zarówno kodu, jak i wyjaśnień!
Martin Tournoij
-8

Nie mam zamiaru udzielić Ci całej odpowiedzi (i tak nie sądzę, że szukasz parsowania i zapisywania do pliku), ale powinna wystarczyć kluczowa wskazówka: użyj set()funkcji Pythona , a następnie sorted()lub w .sort()połączeniu z .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]
Abhranil Das
źródło
8
Może się mylę, ale przekształcenie pandy DataFrame jako zestawu, a następnie konwersja z powrotem wydaje się bardzo nieefektywnym sposobem rozwiązania tego problemu. Robię analizę logów, więc zastosuję to do bardzo dużych zbiorów danych.
Abe
Przepraszam, nie wiem zbyt wiele o tym konkretnym scenariuszu, więc może się zdarzyć, że moja ogólna odpowiedź nie okaże się zbyt skuteczna dla twojego problemu.
Abhranil Das