python pandas:列Aの重複を削除し、列Bの最大値を持つ行を維持します


161

列Aに繰り返し値があるデータフレームがあります。重複を削除して、列Bの値が最も高い行を維持します。

したがって、この:

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

これに変わるはずです:

A B
1 20
2 40
3 10

Wesは、重複を削除するためのいくつかの優れた機能を追加しました:http : //wesmckinney.com/blog/?p=340。ただし、AFAICT、完全に重複するように設計されているため、保持する行を選択する基準についての言及はありません。

おそらくこれを行う簡単な方法があると思います-重複を削除する前にデータフレームをソートするのと同じくらい簡単かもしれません-しかし、それを理解するのに十分なgroupbyの内部ロジックを知りません。助言がありますか?


1
質問のURLはEOLと表示されます。
DaveL17 2017年

慣用的で高性能な方法については、以下のこのソリューションを参照してください
Ted Petrou 2017

回答:


194

これが最後です。ただし、最大ではありません:

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

次のようなこともできます:

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
小さな注意:colsおよびtake_lastパラメータは廃止され、subsetおよびkeepパラメータに置き換えられました。pandas.pydata.org/pandas-docs/version/0.17.1/generated/...
Jezzamon

@Jezzamonが言うようにFutureWarning: the take_last=True keyword is deprecated, use keep='last' instead
tumultous_rooster

1
使用しない理由はありますdf.sort_values(by=['B']).drop_duplicates(subset=['A'], keep='last')か?つまり、このsort_valuesは安全に思えますが、実際に安全かどうかはわかりません。
リトルボビーテーブル

4
この回答は廃止されました。以下の@Ted Petrouの回答を参照してください。
cxrodgers 2017

あなたが複数の列の場合と、このコードが、を使用したい場合group_by、あなたは追加することができます.reset_index(drop=True) df.groupby(['A','C'], group_keys=False).apply(lambda x: x.ix[x.B.idxmax()]).reset_index(drop=True)。これは、Multindexになり、デフォルト値としてインデックスをリセットしますからcompsed 'A''C'
Hamriサイード

79

一番の答えは、あまりにも多くの作業を行っており、より大きなデータセットの場合は非常に遅いようです。applyは遅く、可能であれば回避する必要があります。ixは非推奨であり、同様に回避する必要があります。

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

   A   B
1  1  20
3  2  40
4  3  10

または、他のすべての列でグループ化し、必要な列の最大値を取得します。 df.groupby('A', as_index=False).max()


1
これは実際には包丁のアプローチです。lambaドロップ中になんらかの機能を使って一般化できるのかと思っていました。たとえば、これらの重複する値の平均よりも小さい値のみを削除するにはどうすればよいですか。
デクスター

15

最も簡単なソリューション:

1つの列に基づいて重複を削除するには:

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

複数の列に基づいて重複を削除するには:

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

1
最良のソリューション。ありがとう。
フラビオ

お役に立ててうれしいです。@Flavio
ギル・バッジョ

私のデータフレームには10列あり、このコードを使用して3列から重複を削除しました。ただし、残りの列から行が削除されました。最後の4つの列のみの重複を削除する方法はありますか?
ソフィア

2
しかし、OPは列Bの最高値を保持する必要があります。これは、最初にソートした場合に機能する可能性があります。しかし、それは基本的にTed Petrouの答えです。
Teepeemm

7

これを試して:

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

1
これを元のDataFrameのように再インデックスするのに最適なイディオムを知っていますか?あなたが忍者を私にしたとき、私はそれを理解しようとしていました。:^)
DSM

4
きちんと。データフレームにさらに列が含まれている場合はどうなりますか(C、D、Eなど)?最大化が必要な列はBだけであることを指定する必要があるため、その場合、Maxは機能しないようです。
阿部

1
@DSM元の質問のリンクを確認してください。グループ化されたデータフレームのインデックスを再作成するコードがいくつかあります。
阿部

5

最初に列Bの降順でデータフレームを並べ替え、次に列Aの複製をドロップして最初に保持します

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

groupbyなし



1

あなたの場合、あなたは本当にgroupbyを必要としないと思います。私はB列を降順で並べ替え、列Aに重複をドロップします。必要に応じて、次のような新しいきれいなインデックスを作成することもできます。

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

これは他の投稿とどう違うのですか?
DJK

1

これは、共有する価値がある解決する必要があったバリエーションです。の各一意の文字列についてcolumnA、で最も一般的な関連文字列を見つけたかったのcolumnBです。

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

.any()モードにタイがある場合、ピックは1つを選択します。(.any()一連のints でを使用すると、いずれかを選択するのではなくブール値が返されることに注意してください。)

元の質問では、対応するアプローチにより

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


0

すでに投稿された質問に答えるとき、コードを読みやすくするためにmax()関数が適用される列名を追加することで、小さな変更を加えました。

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

それらがどのように機能するか、なぜそれらが質問ですでに利用可能な回答に対して優れているか、または補完的であるかを説明して、回答にもう少しコンテキストを与えてください。それらが付加価値を提供しない場合は、古い質問に追加の回答を投稿しないでください。最後に、コードをインデントして、コードブロックとしてフォーマットしてください。
WhoIsJack

0

これを行う最も簡単な方法:

# 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

これも機能します:

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

このコードスニペットは問題を解決する可能性がありますが、説明を含めると、投稿の品質を向上させるのに役立ちます。あなたは将来の読者のための質問に答えていることを覚えておいてください、そしてそれらの人々はあなたのコード提案の理由を知らないかもしれません。また、コードと説明コメントを混同しないようにしてください。これにより、コードと説明の両方が読みにくくなります。
Martin Tournoij 2017年

-8

私はあなたに完全な答えを与えるつもりはありませんが(とにかくファイル部分の解析と書き込みを探しているとは思わない)、重要なヒントで十分です:pythonのset()関数を使用してから、sorted()または.sort()と組み合わせて.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
多分これは間違っているかもしれませんが、パンダのDataFrameをセットとして再キャストし、それを元に戻すことは、この問題を解決するための非常に非効率的な方法のようです。私はログ分析を行っているので、これをいくつかの非常に大きなデータセットに適用します。
安倍

申し訳ありませんが、この特定のシナリオについてはあまり知りません。そのため、私の一般的な答えが問題に対して効率的であるとは言えないかもしれません。
Abhranil Das 2012
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.