Python-Pandas: Entfernen Sie Duplikate in Spalte A, wobei die Zeile mit dem höchsten Wert in Spalte B beibehalten wird

159

Ich habe einen Datenrahmen mit Wiederholungswerten in Spalte A. Ich möchte Duplikate löschen und die Zeile mit dem höchsten Wert in Spalte B beibehalten.

Also das:

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

Sollte sich in Folgendes verwandeln:

A B
1 20
2 40
3 10

Wes hat einige nette Funktionen hinzugefügt, um Duplikate zu löschen: http://wesmckinney.com/blog/?p=340 . AFAICT wurde jedoch für exakte Duplikate entwickelt, sodass Kriterien für die Auswahl der beibehaltenen Zeilen nicht erwähnt werden.

Ich vermute, es gibt wahrscheinlich einen einfachen Weg, dies zu tun - vielleicht so einfach wie das Sortieren des Datenrahmens vor dem Löschen von Duplikaten -, aber ich kenne die interne Logik von groupby nicht gut genug, um es herauszufinden. Irgendwelche Vorschläge?

Abe
quelle
1
Beachten Sie, dass die URL in der Frage EOL erscheint.
DaveL17
Eine idiomatische und performante Methode finden Sie in der folgenden Lösung .
Ted Petrou

Antworten:

192

Dies dauert den letzten. Nicht das Maximum:

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

Sie können auch so etwas tun:

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
quelle
12
Kleiner Hinweis: Die Parameter colsund take_lastwerden abgeschrieben und durch die Parameter subsetund ersetzt keep. pandas.pydata.org/pandas-docs/version/0.17.1/generated/…
Jezzamon
wie @Jezzamon sagt,FutureWarning: the take_last=True keyword is deprecated, use keep='last' instead
tumultous_rooster
1
Gibt es einen Grund, nicht zu verwenden df.sort_values(by=['B']).drop_duplicates(subset=['A'], keep='last')? Ich meine, diese sort_values ​​scheinen mir sicher zu sein, aber ich habe keine Ahnung, ob es tatsächlich so ist.
Little Bobby Tables
4
Diese Antwort ist jetzt veraltet. Siehe die Antwort von @Ted Petrou unten.
Cxrodgers
Wenn Sie diesen Code verwenden möchten, aber mit mehr als einer Spalte in der group_by, können Sie hinzufügen. .reset_index(drop=True) df.groupby(['A','C'], group_keys=False).apply(lambda x: x.ix[x.B.idxmax()]).reset_index(drop=True)Dadurch wird der Index zurückgesetzt, da sein Standardwert ein Multindex ist, der aus 'A'und'C'
Hamri Said
79

Die beste Antwort ist zu viel Arbeit und scheint für größere Datenmengen sehr langsam zu sein. applyist langsam und sollte nach Möglichkeit vermieden werden. ixist veraltet und sollte ebenfalls vermieden werden.

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

   A   B
1  1  20
3  2  40
4  3  10

Oder gruppieren Sie einfach nach allen anderen Spalten und nehmen Sie das Maximum der benötigten Spalte. df.groupby('A', as_index=False).max()

Ted Petrou
quelle
1
Dies ist eigentlich ein Hackbeil-Ansatz. Ich habe mich gefragt, ob es durch die Verwendung einer lambaFunktion beim Ablegen verallgemeinert werden kann . Wie kann ich zum Beispiel nur Werte löschen, die kleiner sind als der Durchschnitt dieser doppelten Werte?
Dexter
15

Einfachste Lösung:

So löschen Sie Duplikate basierend auf einer Spalte:

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

So löschen Sie Duplikate basierend auf mehreren Spalten:

df = df.drop_duplicates(['col_name1','col_name2','col_name3'], keep='last')
Gil Baggio
quelle
1
Beste Lösung. Vielen Dank.
Flavio
Froh, dass ich Helfen kann. @Flavio
Gil Baggio
Mein Datenrahmen hat 10 Spalten, und ich habe diesen Code verwendet, um Duplikate aus drei Spalten zu löschen. Die Zeilen wurden jedoch aus den übrigen Spalten gelöscht. Gibt es eine Möglichkeit, die Duplikate nur für die 4 letzten Spalten zu löschen?
Sofia
2
OP möchte jedoch den höchsten Wert in Spalte B beibehalten. Dies funktioniert möglicherweise, wenn Sie zuerst sortieren. Aber dann ist es im Grunde Ted Petrous Antwort.
Teepeemm
7

Versuche dies:

df.groupby(['A']).max()
Eumiro
quelle
1
Kennen Sie die beste Redewendung, um dies neu zu indizieren, damit es wie der ursprüngliche DataFrame aussieht? Ich habe versucht, das herauszufinden, als du mich ninja hast. : ^)
DSM
4
Ordentlich. Was ist, wenn der Datenrahmen mehr Spalten enthält (z. B. C, D, E)? Max scheint in diesem Fall nicht zu funktionieren, da wir angeben müssen, dass B die einzige Spalte ist, die maximiert werden muss.
Abe
1
@DSM Überprüfen Sie den Link in der ursprünglichen Frage. Es gibt Code, um den gruppierten Datenrahmen neu zu indizieren.
Abe
5

Ich würde den Datenrahmen zuerst mit absteigender Spalte B sortieren, dann Duplikate für Spalte A löschen und zuerst behalten

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

ohne groupby

Nobel
quelle
1

Ich denke in deinem Fall brauchst du nicht wirklich einen Groupby. Ich würde Ihre B-Spalte in absteigender Reihenfolge sortieren und dann Duplikate in Spalte A ablegen. Wenn Sie möchten, können Sie auch einen neuen schönen und sauberen Index wie diesen haben:

df.sort_values('B', ascending=False).drop_duplicates('A').sort_index().reset_index(drop=True)
was auch immer
quelle
Wie unterscheidet sich das von anderen Posts?
DJK
1

Hier ist eine Variation, die ich lösen musste und die es wert ist, geteilt zu werden: Für jede eindeutige Zeichenfolge in columnAwollte ich die häufigste zugehörige Zeichenfolge in finden columnB.

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

Die .any()Auswahl eins, wenn es ein Unentschieden für den Modus gibt. (Beachten Sie, dass die Verwendung .any()einer Reihe von ints einen Booleschen Wert zurückgibt, anstatt einen davon auszuwählen.)

Für die ursprüngliche Frage vereinfacht sich der entsprechende Ansatz zu

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

Mistaben
quelle
0

Wenn bereits gegebene Beiträge die Frage beantworten, habe ich eine kleine Änderung vorgenommen, indem ich den Spaltennamen hinzugefügt habe, auf den die Funktion max () angewendet wird, um die Lesbarkeit des Codes zu verbessern.

df.groupby('A', as_index=False)['B'].max()
Bhagabat Behera
quelle
Bitte geben Sie Ihren Antworten etwas mehr Kontext und erklären Sie, wie sie funktionieren und warum sie den bereits für eine Frage verfügbaren Antworten überlegen oder komplementär sind. Wenn sie keinen Mehrwert bieten, veröffentlichen Sie bitte keine zusätzlichen Antworten auf alte Fragen. Zum Schluss formatieren Sie Ihren Code als Codeblock, indem Sie ihn einrücken.
WhoIsJack
0

Der einfachste Weg, dies zu tun:

# 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
quelle
-1

das funktioniert auch:

a=pd.DataFrame({'A':a.groupby('A')['B'].max().index,'B':a.groupby('A')       ['B'].max().values})
Mahesh
quelle
Während dieses Code-Snippet die Frage lösen kann, hilft eine Erklärung wirklich, die Qualität Ihres Beitrags zu verbessern. Denken Sie daran, dass Sie die Frage für Leser in Zukunft beantworten und diese Personen möglicherweise die Gründe für Ihren Codevorschlag nicht kennen. Bitte versuchen Sie auch, Ihren Code nicht mit erklärenden Kommentaren zu überfüllen. Dies verringert die Lesbarkeit sowohl des Codes als auch der Erklärungen!
Martin Tournoij
-8

Ich werde Ihnen nicht die ganze Antwort geben (ich glaube, Sie suchen sowieso nicht nach dem Parsen und Schreiben in einen Dateiteil), aber ein entscheidender Hinweis sollte ausreichen: Verwenden Sie die Python- set()Funktion und dann sorted()oder .sort()gekoppelt mit .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
quelle
8
Vielleicht irre ich mich darin, aber die Neufassung eines Pandas DataFrame als Set und die anschließende Rückkonvertierung scheinen ein sehr ineffizienter Weg zu sein, um dieses Problem zu lösen. Ich mache eine Protokollanalyse, daher werde ich diese auf einige sehr große Datenmengen anwenden.
Abe
Entschuldigung, ich weiß nicht viel über dieses spezielle Szenario, daher kann es sein, dass sich meine generische Antwort für Ihr Problem nicht als zu effizient herausstellt.
Abhranil Das