python panda: Hapus duplikat dengan kolom A, pertahankan baris dengan nilai tertinggi di kolom B

161

Saya memiliki kerangka data dengan nilai berulang di kolom A. Saya ingin menjatuhkan duplikat, menjaga baris dengan nilai tertinggi di kolom B.

Jadi ini:

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

Harus berubah menjadi ini:

A B
1 20
2 40
3 10

Wes telah menambahkan beberapa fungsionalitas yang bagus untuk menjatuhkan duplikat: http://wesmckinney.com/blog/?p=340 . Tapi AFAICT, ini dirancang untuk duplikat yang tepat, jadi tidak disebutkan kriteria untuk memilih baris mana yang disimpan.

Saya menduga mungkin ada cara mudah untuk melakukan ini --- mungkin semudah mengurutkan dataframe sebelum menjatuhkan duplikat --- tapi saya tidak tahu logika internal groupby cukup baik untuk mengetahuinya. Ada saran?

Abe
sumber
1
Perhatikan bahwa URL dalam pertanyaan muncul EOL.
DaveL17
Untuk cara idiomatis dan performan, lihat solusi ini di bawah .
Ted Petrou

Jawaban:

194

Ini yang terakhir. Tidak maksimal:

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

Anda juga dapat melakukan sesuatu seperti:

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
sumber
12
Catatan kecil: colsdan take_lastparameter disusutkan dan telah digantikan oleh subsetdan keepparameter. pandas.pydata.org/pandas-docs/version/0.17.1/generated/…
Jezzamon
seperti yang dikatakan @Jezzamon,FutureWarning: the take_last=True keyword is deprecated, use keep='last' instead
tumultous_rooster
1
Apakah ada alasan untuk tidak menggunakan df.sort_values(by=['B']).drop_duplicates(subset=['A'], keep='last')? Maksud saya sort_values ​​ini tampaknya aman bagi saya, tetapi saya tidak tahu apakah itu benar-benar aman.
Little Bobby Tables
4
Jawaban ini sekarang sudah usang. Lihat jawaban @Ted Petrou di bawah ini.
cxrodgers
Jika Anda ingin menggunakan kode ini tetapi dengan huruf lebih dari satu kolom di group_by, Anda dapat menambahkan .reset_index(drop=True) df.groupby(['A','C'], group_keys=False).apply(lambda x: x.ix[x.B.idxmax()]).reset_index(drop=True)Ini akan mengatur ulang indeks karena nilai defaultnya adalah Multindex yang berasal dari 'A'dan'C'
Hamri Said
79

Jawaban teratas adalah melakukan terlalu banyak pekerjaan dan terlihat sangat lambat untuk set data yang lebih besar. applylambat dan harus dihindari jika memungkinkan. ixsudah usang dan harus dihindari juga.

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

   A   B
1  1  20
3  2  40
4  3  10

Atau cukup kelompokkan dengan semua kolom lain dan ambil maks kolom yang Anda butuhkan. df.groupby('A', as_index=False).max()

Ted Petrou
sumber
1
Ini sebenarnya adalah pendekatan golok. Saya bertanya-tanya apakah itu dapat digeneralisasi dengan menggunakan beberapa lambafungsi saat menjatuhkan. Misalnya bagaimana saya bisa menjatuhkan hanya nilai yang lebih rendah dari rata-rata nilai duplikat tersebut
Dexter
15

Solusi paling sederhana:

Untuk menjatuhkan duplikat berdasarkan satu kolom:

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

Untuk menjatuhkan duplikat berdasarkan beberapa kolom:

df = df.drop_duplicates(['col_name1','col_name2','col_name3'], keep='last')
Gil Baggio
sumber
1
Solusi terbaik. Terima kasih.
Flavio
Senang untuk membantu. @Flavio
Gil Baggio
Kerangka data saya memiliki 10 kolom, dan saya menggunakan kode ini untuk menghapus duplikat dari tiga kolom. Namun, itu menghapus baris dari sisa kolom. Apakah ada cara untuk menghapus duplikat hanya untuk 4 kolom terakhir?
Sofia
2
Tetapi OP ingin mempertahankan nilai tertinggi di kolom B. Ini mungkin berhasil jika Anda mengurutkannya terlebih dahulu. Tapi pada dasarnya itulah jawaban Ted Petrou.
Teepeemm
7

Coba ini:

df.groupby(['A']).max()
eumiro
sumber
1
D'Anda tahu idiom terbaik untuk mengindeks ulang ini agar terlihat seperti DataFrame asli? Saya sedang berusaha mencari tahu ketika Anda ninja saya. : ^)
DSM
4
Rapi. Bagaimana jika kerangka data berisi lebih banyak kolom (misalnya C, D, E)? Max tampaknya tidak berfungsi dalam kasus itu, karena kita perlu menentukan bahwa B adalah satu-satunya kolom yang perlu dimaksimalkan.
Abe
1
@ DSM Periksa tautan di pertanyaan awal. Ada beberapa kode untuk mengindeks ulang kerangka data yang dikelompokkan.
Abe
5

Saya akan mengurutkan dataframe pertama dengan Kolom B turun, kemudian drop duplikat untuk Kolom A dan pertahankan terlebih dahulu

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

tanpa groupby

Nobel
sumber
1

Saya pikir dalam kasus Anda, Anda tidak benar-benar membutuhkan grup. Saya akan menyortir dengan urutan menurun kolom B Anda, kemudian drop duplikat di kolom A dan jika Anda mau, Anda juga dapat memiliki indeks bagus dan bersih baru seperti itu:

df.sort_values('B', ascending=False).drop_duplicates('A').sort_index().reset_index(drop=True)
terserahlah
sumber
bagaimana ini berbeda dari posting lain?
DJK
1

Berikut variasi yang harus saya pecahkan yang layak dibagikan: untuk setiap string unik di columnAsaya ingin menemukan string terkait yang paling umum columnB.

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

The .any()mengambil satu jika ada dasi untuk mode. (Perhatikan bahwa menggunakan .any()pada Seri ints mengembalikan boolean daripada memilih salah satunya.)

Untuk pertanyaan awal, pendekatan yang sesuai disederhanakan

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

mistaben
sumber
0

Ketika sudah diberikan tulisan, jawab pertanyaan itu, saya membuat perubahan kecil dengan menambahkan nama kolom tempat fungsi max () diterapkan untuk keterbacaan kode yang lebih baik.

df.groupby('A', as_index=False)['B'].max()
Bhagabat Behera
sumber
Tolong beri sedikit lebih banyak konteks untuk jawaban Anda, menjelaskan bagaimana mereka bekerja dan mengapa mereka lebih unggul atau melengkapi jawaban yang sudah tersedia untuk pertanyaan. Jika mereka tidak memberikan nilai tambah, harap jangan mengirim jawaban tambahan pada pertanyaan lama. Terakhir, harap format kode Anda sebagai blok kode dengan memberi indentasi.
WhoIsJack
0

Cara termudah untuk melakukan ini:

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

ini juga berfungsi:

a=pd.DataFrame({'A':a.groupby('A')['B'].max().index,'B':a.groupby('A')       ['B'].max().values})
Mahesh
sumber
Sementara potongan kode ini dapat menyelesaikan pertanyaan, termasuk penjelasan sangat membantu untuk meningkatkan kualitas posting Anda. Ingatlah bahwa Anda menjawab pertanyaan untuk pembaca di masa depan, dan orang-orang itu mungkin tidak tahu alasan untuk saran kode Anda. Tolong juga cobalah untuk tidak membuat kerumunan kode Anda dengan komentar penjelasan, ini mengurangi keterbacaan kode dan penjelasan!
Martin Tournoij
-8

Saya tidak akan memberikan Anda seluruh jawaban (saya tidak berpikir Anda sedang mencari bagian parsing dan menulis untuk mengajukan), tetapi petunjuk penting harus mencukupi: gunakan set()fungsi python , lalu sorted()atau .sort()ditambah dengan .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
sumber
8
Mungkin saya salah dalam hal ini, tetapi menyusun kembali panda DataFrame sebagai satu set, lalu mengubahnya kembali sepertinya cara yang sangat tidak efisien untuk menyelesaikan masalah ini. Saya sedang melakukan analisis log, jadi saya akan menerapkan ini pada beberapa set data yang sangat besar.
Abe
Maaf, saya tidak tahu terlalu banyak tentang skenario khusus ini, jadi mungkin jawaban generik saya tidak akan menjadi terlalu efisien untuk masalah Anda.
Abhranil Das