python panda: rimuove i duplicati dalle colonne A, mantenendo la riga con il valore più alto nella colonna B


162

Ho un frame di dati con valori di ripetizione nella colonna A. Voglio eliminare i duplicati, mantenendo la riga con il valore più alto nella colonna B.

Così questo:

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

Dovrebbe trasformarsi in questo:

A B
1 20
2 40
3 10

Wes ha aggiunto alcune belle funzionalità per eliminare i duplicati: http://wesmckinney.com/blog/?p=340 . Ma AFAICT, è progettato per duplicati esatti, quindi non si fa menzione di criteri per la selezione delle righe da conservare.

Immagino che ci sia probabilmente un modo semplice per farlo --- forse facile come ordinare il frame di dati prima di eliminare i duplicati --- ma non conosco la logica interna di groupby abbastanza bene da capirlo. Eventuali suggerimenti?


1
Si noti che l'URL nella domanda appare EOL.
DaveL17

Per un modo idiomatico e performante, vedi questa soluzione di seguito .
Ted Petrou,

Risposte:


195

Questo richiede l'ultimo. Non il massimo però:

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

Puoi anche fare qualcosa del tipo:

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
Piccola nota: i parametri colse take_lastsono ammortizzati e sono stati sostituiti dai parametri subsete keep. pandas.pydata.org/pandas-docs/version/0.17.1/generated/…
Jezzamon

come dice @Jezzamon,FutureWarning: the take_last=True keyword is deprecated, use keep='last' instead
tumultous_rooster il

1
C'è un motivo per non usare df.sort_values(by=['B']).drop_duplicates(subset=['A'], keep='last')? Voglio dire, questo sort_values ​​mi sembra sicuro, ma non ho idea se lo sia davvero.
Tavolini Bobby,

4
Questa risposta è ora obsoleta. Vedi la risposta di @Ted Petrou di seguito.
Cxrodgers,

Se si desidera utilizzare questo codice, ma con il caso di più di una colonna in group_by, è possibile aggiungere .reset_index(drop=True) df.groupby(['A','C'], group_keys=False).apply(lambda x: x.ix[x.B.idxmax()]).reset_index(drop=True)Ciò reimpostare l'indice come il suo valore di default sarebbe un Multindex compsed da 'A'e'C'
Hamri detto

79

La risposta migliore è fare troppo lavoro e sembra essere molto lenta per set di dati più grandi. applyè lento e dovrebbe essere evitato se possibile. ixè deprecato e dovrebbe essere evitato anche.

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

   A   B
1  1  20
3  2  40
4  3  10

O semplicemente raggruppa per tutte le altre colonne e prendi il massimo della colonna di cui hai bisogno. df.groupby('A', as_index=False).max()


1
Questo è in realtà un approccio di mannaia. Mi chiedevo se si potesse generalizzare usando alcune lambafunzioni mentre si lasciava cadere. Ad esempio, come posso eliminare solo valori inferiori alla media di quei valori duplicati.
Dexter,

16

La soluzione più semplice:

Per eliminare i duplicati in base a una colonna:

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

Per eliminare i duplicati in base a più colonne:

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

1
Soluzione migliore. Grazie.
Flavio,

Felice di aiutare. @Flavio
Gil Baggio,

Il mio frame di dati ha 10 colonne e ho usato questo codice per eliminare i duplicati da tre colonne. Tuttavia, ha eliminato le righe dal resto delle colonne. Esiste un modo per eliminare i duplicati solo per le ultime 4 colonne?
Sofia,

2
Ma OP vuole mantenere il valore più alto nella colonna B. Questo potrebbe funzionare se si ordina prima. Ma in fondo è la risposta di Ted Petrou.
Teepeemm,

7

Prova questo:

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

1
Sai qual è il miglior idioma per reindicizzare questo per assomigliare al DataFrame originale? Stavo cercando di capirlo quando mi hai ucciso. : ^)
DSM

4
Neat. Cosa succede se il frame di dati contiene più colonne (ad es. C, D, E)? Max non sembra funzionare in quel caso, perché dobbiamo specificare che B è l'unica colonna che deve essere massimizzata.
Abe,

1
@DSM Controlla il link nella domanda originale. C'è del codice per reindicizzare il dataframe raggruppato.
Abe,

5

Vorrei prima ordinare il frame di dati con la colonna B decrescente, quindi rilasciare i duplicati per la colonna A e mantenerlo per primo

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

senza alcun groupby



1

Penso che nel tuo caso non ti serva davvero un groupby. Ordinerei per ordine decrescente la tua colonna B, quindi lascerei cadere i duplicati nella colonna A e se vuoi puoi anche avere un nuovo indice bello e pulito come quello:

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

come è diverso da altri post?
DJK,

1

Ecco una variante che ho dovuto risolvere che vale la pena condividere: per ogni stringa univoca in columnAcui volevo trovare la stringa associata più comune columnB.

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

L' .any()raccoglie uno se c'è un legame per la modalità. (Nota che l'uso di .any()una serie di ints restituisce un valore booleano anziché sceglierne uno.)

Per la domanda originale, l'approccio corrispondente semplifica

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


0

Quando i post già indicati rispondono alla domanda, ho apportato una piccola modifica aggiungendo il nome della colonna su cui viene applicata la funzione max () per una migliore leggibilità del codice.

df.groupby('A', as_index=False)['B'].max()

Per favore, dai un po 'più di contesto alle tue risposte, spiegando come funzionano e perché sono superiori o complementari alle risposte già disponibili per una domanda. Se non forniscono valore aggiunto, si prega di astenersi dal pubblicare risposte aggiuntive su vecchie domande. Infine, si prega di formattare il codice come un blocco di codice rientrandolo.
WhoIsJack,

0

Il modo più semplice per farlo:

# 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

questo funziona anche:

a=pd.DataFrame({'A':a.groupby('A')['B'].max().index,'B':a.groupby('A')       ['B'].max().values})

Mentre questo frammento di codice può risolvere la domanda, inclusa una spiegazione aiuta davvero a migliorare la qualità del tuo post. Ricorda che stai rispondendo alla domanda per i lettori in futuro e che queste persone potrebbero non conoscere i motivi del tuo suggerimento sul codice. Si prega inoltre di cercare di non riempire il codice con commenti esplicativi, questo riduce la leggibilità sia del codice che delle spiegazioni!
Martin Tournoij,

-8

Non ho intenzione di darti la risposta completa (non credo che tu stia cercando l'analisi e la scrittura della parte del file comunque), ma un suggerimento fondamentale dovrebbe essere sufficiente: usa la set()funzione di Python , e quindi sorted()o .sort()accoppiato con .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
Forse mi sbaglio su questo, ma rifondere un DataFrame panda come set, quindi riconvertirlo sembra un modo molto inefficiente per risolvere questo problema. Sto eseguendo l'analisi dei registri, quindi applicherò questo ad alcuni insiemi di dati molto grandi.
Abe,

Spiacenti, non so molto di questo particolare scenario, quindi potrebbe essere che la mia risposta generica non risulti troppo efficiente per il tuo problema.
Abhranil Das,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.