pandas python: supprimez les doublons par colonnes A, en conservant la ligne avec la valeur la plus élevée dans la colonne B

162

J'ai un dataframe avec des valeurs de répétition dans la colonne A. Je veux supprimer les doublons, en conservant la ligne avec la valeur la plus élevée dans la colonne B.

Donc ça:

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

Devrait se transformer en ceci:

A B
1 20
2 40
3 10

Wes a ajouté quelques fonctionnalités intéressantes pour supprimer les doublons: http://wesmckinney.com/blog/?p=340 . Mais AFAICT, il est conçu pour les doublons exacts, il n'y a donc aucune mention de critères pour sélectionner les lignes à conserver.

Je suppose qu'il y a probablement un moyen facile de le faire - peut-être aussi simple que de trier le dataframe avant de supprimer les doublons - mais je ne connais pas assez bien la logique interne de groupby pour le comprendre. Aucune suggestion?

Abe
la source
1
Notez que l'URL de la question apparaît EOL.
DaveL17
Pour une manière idiomatique et performante, voyez cette solution ci-dessous .
Ted Petrou

Réponses:

195

Cela prend le dernier. Pas le maximum cependant:

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

Vous pouvez également faire quelque chose comme:

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
la source
12
Petite remarque: les paramètres colset take_lastsont amortis et ont été remplacés par les paramètres subsetet keep. pandas.pydata.org/pandas-docs/version/0.17.1/generated/…
Jezzamon
comme le dit @Jezzamon,FutureWarning: the take_last=True keyword is deprecated, use keep='last' instead
tumultous_rooster
1
Y a-t-il une raison de ne pas utiliser df.sort_values(by=['B']).drop_duplicates(subset=['A'], keep='last')? Je veux dire que ce sort_values ​​me semble sûr mais je n'ai aucune idée si c'est réellement le cas.
Little Bobby Tables
4
Cette réponse est désormais obsolète. Voir la réponse de @Ted Petrou ci-dessous.
cxrodgers
Si vous voulez utiliser ce code mais avec le cas de plus d'une colonne dans le group_by, vous pouvez ajouter .reset_index(drop=True) df.groupby(['A','C'], group_keys=False).apply(lambda x: x.ix[x.B.idxmax()]).reset_index(drop=True)Cela réinitialisera l'index car sa valeur par défaut serait un Multindex compsed de 'A'et'C'
Hamri Said
79

La réponse principale est de faire trop de travail et semble être très lent pour des ensembles de données plus volumineux. applyest lent et doit être évité si possible. ixest obsolète et doit également être évité.

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

   A   B
1  1  20
3  2  40
4  3  10

Ou regroupez simplement toutes les autres colonnes et prenez le maximum de la colonne dont vous avez besoin. df.groupby('A', as_index=False).max()

Ted Petrou
la source
1
Il s'agit en fait d'une approche de couperet. Je me demandais si cela pouvait être généralisé en utilisant une lambafonction lors de la suppression. Par exemple, comment puis-je supprimer uniquement les valeurs inférieures à la moyenne de ces valeurs en double.
Dexter
16

Solution la plus simple:

Pour supprimer les doublons basés sur une colonne:

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

Pour supprimer les doublons basés sur plusieurs colonnes:

df = df.drop_duplicates(['col_name1','col_name2','col_name3'], keep='last')
Gil Baggio
la source
1
Meilleure solution. Merci.
Flavio
Heureux de vous aider. @Flavio
Gil Baggio
Mon bloc de données comporte 10 colonnes et j'ai utilisé ce code pour supprimer les doublons de trois colonnes. Cependant, il a supprimé les lignes du reste des colonnes. Existe-t-il un moyen de supprimer les doublons uniquement pour les 4 dernières colonnes?
Sofia
2
Mais OP souhaite conserver la valeur la plus élevée dans la colonne B. Cela peut fonctionner si vous effectuez un tri en premier. Mais ensuite, c'est essentiellement la réponse de Ted Petrou.
Teepeemm le
7

Essaye ça:

df.groupby(['A']).max()
eumiro
la source
1
Connaissez-vous le meilleur idiome pour réindexer ceci afin qu'il ressemble au DataFrame original? J'essayais de comprendre ça quand tu m'as ninja. : ^)
DSM
4
Soigné. Que faire si le dataframe contient plus de colonnes (par exemple C, D, E)? Max ne semble pas fonctionner dans ce cas, car nous devons spécifier que B est la seule colonne qui doit être maximisée.
Abe du
1
@DSM Vérifiez le lien dans la question d'origine. Il y a du code pour réindexer le dataframe groupé.
Abe du
5

Je trierais d'abord le dataframe avec la colonne B décroissante, puis supprimerais les doublons pour la colonne A et garderais en premier

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

sans aucun groupby

Nobel
la source
1

Je pense que dans votre cas, vous n'avez pas vraiment besoin d'un groupby. Je trierais par ordre décroissant votre colonne B, puis déposerais les doublons dans la colonne A et si vous le souhaitez, vous pouvez également avoir un nouvel index agréable et propre comme celui-ci:

df.sort_values('B', ascending=False).drop_duplicates('A').sort_index().reset_index(drop=True)
quoi que ce soit
la source
en quoi est-ce différent des autres articles?
DJK
1

Voici une variante que j'ai dû résoudre et qui mérite d'être partagée: pour chaque chaîne unique dans, columnAje voulais trouver la chaîne associée la plus courante dans columnB.

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

Il en .any()choisit un s'il y a égalité pour le mode. (Notez que l'utilisation .any()sur une série deint s renvoie un booléen plutôt que d'en choisir un.)

Pour la question d'origine, l'approche correspondante se simplifie en

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

Mistaben
la source
0

Lorsque des messages déjà donnés répondent à la question, j'ai fait un petit changement en ajoutant le nom de la colonne sur laquelle la fonction max () est appliquée pour une meilleure lisibilité du code.

df.groupby('A', as_index=False)['B'].max()
Bhagabat Behera
la source
Veuillez donner un peu plus de contexte à vos réponses, en expliquant comment elles fonctionnent et pourquoi elles sont supérieures ou complémentaires aux réponses déjà disponibles pour une question. S'ils n'apportent pas de valeur ajoutée, veuillez vous abstenir de publier des réponses supplémentaires sur d'anciennes questions. Enfin, veuillez formater votre code sous forme de bloc de code en l'indentant.
WhoIsJack
0

Façon la plus simple de le faire:

# 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
la source
-1

cela fonctionne également:

a=pd.DataFrame({'A':a.groupby('A')['B'].max().index,'B':a.groupby('A')       ['B'].max().values})
Mahesh
la source
Bien que cet extrait de code puisse résoudre la question, inclure une explication aide vraiment à améliorer la qualité de votre message. N'oubliez pas que vous répondez à la question aux lecteurs à l'avenir, et que ces personnes pourraient ne pas connaître les raisons de votre suggestion de code. Essayez également de ne pas surcharger votre code avec des commentaires explicatifs, cela réduit la lisibilité du code et des explications!
Martin Tournoij
-8

Je ne vais pas vous donner la réponse complète (je ne pense pas que vous cherchiez de toute façon l'analyse et l'écriture dans le fichier), mais un indice pivot devrait suffire: utilisez la set()fonction de python , puis sorted()ou .sort()couplé avec .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
la source
8
Peut-être que je me trompe sur ce point, mais refondre un DataFrame pandas en tant qu'ensemble, puis le reconvertir semble être un moyen très inefficace de résoudre ce problème. Je fais une analyse des journaux, donc je vais l'appliquer à de très gros ensembles de données.
Abe
Désolé, je ne sais pas trop sur ce scénario particulier, il se peut donc que ma réponse générique ne se révèle pas trop efficace pour votre problème.
Abhranil Das