pandas python: remova duplicatas pelas colunas A, mantendo a linha com o valor mais alto na coluna B

161

Eu tenho um quadro de dados com valores de repetição na coluna A. Quero eliminar duplicatas, mantendo a linha com o valor mais alto na coluna B.

Então, é isso:

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

Deve se transformar nisso:

A B
1 20
2 40
3 10

Wes adicionou uma funcionalidade interessante para eliminar duplicatas: http://wesmckinney.com/blog/?p=340 . Mas o AFAICT foi desenvolvido para duplicatas exatas, portanto não há menção de critérios para selecionar quais linhas serão mantidas.

Suponho que provavelmente haja uma maneira fácil de fazer isso - talvez tão fácil quanto classificar o quadro de dados antes de eliminar duplicatas -, mas não conheço a lógica interna do groupby o suficiente para descobrir isso. Alguma sugestão?

Abe
fonte
1
Observe que o URL na pergunta aparece EOL.
precisa saber é o seguinte
Para uma maneira idiomática e com desempenho, consulte esta solução abaixo .
Ted Petrou

Respostas:

194

Isso leva o último. Não é o máximo:

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

Você também pode fazer algo como:

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
fonte
12
Nota pequena: Os parâmetros colse take_lastsão depreciados e foram substituídos pelos parâmetros subsete keep. pandas.pydata.org/pandas-docs/version/0.17.1/generated/…
Jezzamon
como diz @Jezzamon,FutureWarning: the take_last=True keyword is deprecated, use keep='last' instead
tumultous_rooster
1
Existe uma razão para não usar df.sort_values(by=['B']).drop_duplicates(subset=['A'], keep='last')? Quero dizer que esses valores_de_parte parecem seguros para mim, mas não tenho idéia se realmente é.
Little Bobby Tables
4
Esta resposta está agora obsoleta. Veja a resposta de @Ted Petrou abaixo.
precisa saber é o seguinte
Se você quiser usar esse código, mas com o caso de mais de uma coluna group_by, poderá adicionar. .reset_index(drop=True) df.groupby(['A','C'], group_keys=False).apply(lambda x: x.ix[x.B.idxmax()]).reset_index(drop=True)Isso redefinirá o índice, pois seu valor padrão seria um índice múltiplo composto por 'A'e'C'
Hamri Disse
79

A resposta principal é fazer muito trabalho e parece muito lenta para conjuntos de dados maiores. applyé lento e deve ser evitado, se possível. ixestá obsoleto e deve ser evitado também.

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

   A   B
1  1  20
3  2  40
4  3  10

Ou simplesmente agrupe por todas as outras colunas e obtenha o máximo da coluna necessária. df.groupby('A', as_index=False).max()

Ted Petrou
fonte
1
Esta é realmente uma abordagem de cutelo. Eu queria saber se ele pode ser generalizado usando alguma lambafunção ao soltar. Por exemplo, como posso soltar apenas valores menores que a média desses valores duplicados?
Dexter
15

Solução mais simples:

Para eliminar duplicatas com base em uma coluna:

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

Para eliminar duplicatas com base em várias colunas:

df = df.drop_duplicates(['col_name1','col_name2','col_name3'], keep='last')
Gil Baggio
fonte
1
Melhor solução. Obrigado.
Flavio
Feliz em ajudar. @Flavio
Gil Baggio
Meu quadro de dados possui 10 colunas e usei esse código para excluir duplicatas de três colunas. No entanto, ele excluiu as linhas do restante das colunas. Existe alguma maneira de excluir as duplicatas apenas para as 4 últimas colunas?
Sofia
2
Mas o OP deseja manter o valor mais alto na coluna B. Isso pode funcionar se você classificar primeiro. Mas então é basicamente a resposta de Ted Petrou.
Teepeemm 24/01
7

Tente o seguinte:

df.groupby(['A']).max()
eumiro
fonte
1
Você conhece o melhor idioma para reindexar isso para se parecer com o DataFrame original? Eu estava tentando descobrir isso quando você me ninja. : ^)
DSM
4
Arrumado. E se o quadro de dados contiver mais colunas (por exemplo, C, D, E)? Max parece não funcionar nesse caso, porque precisamos especificar que B é a única coluna que precisa ser maximizada.
Abe
1
@DSM Verifique o link na pergunta original. Há algum código para reindexar o quadro de dados agrupado.
Abe
5

Eu classificaria o quadro de dados primeiro com a Coluna B descendente, depois soltaria duplicatas da Coluna A e manteria

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

sem nenhum groupby

Nobel
fonte
1

Eu acho que no seu caso você realmente não precisa de um grupo. Gostaria de classificar por ordem decrescente sua coluna B, em seguida, largar duplicatas na coluna A e, se você quiser, também pode ter um novo índice limpo e agradável assim:

df.sort_values('B', ascending=False).drop_duplicates('A').sort_index().reset_index(drop=True)
tanto faz
fonte
como isso é diferente de outras postagens?
DJK 27/10
1

Aqui está uma variação que eu tive que resolver e que vale a pena compartilhar: para cada string exclusiva, columnAeu queria encontrar a string associada mais comum columnB.

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

O .any()escolhe se houver um empate para o modo. (Observe que o uso .any()de uma série de ints retorna um booleano em vez de escolher um deles.)

Para a pergunta original, a abordagem correspondente simplifica a

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

mistaben
fonte
0

Quando as postagens já respondidas respondem à pergunta, fiz uma pequena alteração adicionando o nome da coluna na qual a função max () é aplicada para melhor legibilidade do código.

df.groupby('A', as_index=False)['B'].max()
Bhagabat Behera
fonte
Dê um pouco mais de contexto às suas respostas, explicando como elas funcionam e por que são superiores ou complementares às respostas já disponíveis para uma pergunta. Se eles não fornecerem valor agregado, evite postar respostas adicionais sobre perguntas antigas. Por fim, formate seu código como um bloco de código recuando-o.
WhoIsJack
0

A maneira mais fácil de fazer isso:

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

isso também funciona:

a=pd.DataFrame({'A':a.groupby('A')['B'].max().index,'B':a.groupby('A')       ['B'].max().values})
Mahesh
fonte
Embora esse trecho de código possa resolver a questão, incluir uma explicação realmente ajuda a melhorar a qualidade da sua postagem. Lembre-se de que você está respondendo à pergunta dos leitores no futuro e essas pessoas podem não saber os motivos da sua sugestão de código. Tente também não sobrecarregar seu código com comentários explicativos, pois isso reduz a legibilidade do código e das explicações!
Martin Tournoij 14/01
-8

Não vou lhe dar a resposta completa (acho que você não está procurando a análise e a gravação da parte do arquivo), mas uma dica essencial deve ser suficiente: use a set()função python e, em seguida, sorted()ou em .sort()conjunto com .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
fonte
8
Talvez eu esteja errado nisso, mas reformular um DataFrame do pandas como um conjunto e convertê-lo novamente parece uma maneira muito ineficiente de resolver esse problema. Estou fazendo análise de log, portanto aplicarei isso a alguns conjuntos de dados muito grandes.
Abe
Desculpe, não sei muito sobre esse cenário em particular, por isso pode ser que minha resposta genérica não seja muito eficiente para o seu problema.
Abhranil Das