python python: ลบรายการที่ซ้ำกันโดยคอลัมน์ A, ทำให้แถวมีค่าสูงสุดในคอลัมน์ B


162

ฉันมี dataframe ที่มีค่าซ้ำในคอลัมน์ A ฉันต้องการปล่อยรายการที่ซ้ำกันรักษาแถวด้วยค่าสูงสุดในคอลัมน์ B

ดังนั้นนี่คือ:

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

ควรเปลี่ยนเป็น:

A B
1 20
2 40
3 10

เวสสตรีทได้เพิ่มบางฟังก์ชันการทำงานที่ดีที่จะซ้ำกันวาง: http://wesmckinney.com/blog/?p=340 แต่ AFAICT มันถูกออกแบบมาสำหรับการทำซ้ำที่แน่นอนดังนั้นจึงไม่มีการกล่าวถึงเกณฑ์สำหรับการเลือกแถวที่จะเก็บไว้

ฉันเดาว่าอาจเป็นวิธีที่ง่ายในการทำ --- อาจจะง่ายเหมือนกับการคัดแยกดาต้าเฟรมก่อนที่จะทิ้งข้อมูลที่ซ้ำกัน --- แต่ฉันไม่รู้ว่าตรรกะภายในของกรุ๊ปบายเพียงพอที่จะเข้าใจได้ ข้อเสนอแนะใด ๆ


1
โปรดทราบว่า URL ในคำถามจะปรากฏขึ้น EOL
DaveL17

หาวิธีการที่สำนวนและ performant, ดูวิธีการแก้ปัญหาด้านล่างนี้
Ted Petrou

คำตอบ:


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และ pandas.pydata.org/pandas-docs/version/0.17.1/generated/ …keep
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 ​​นี้ดูเหมือนว่าปลอดภัยสำหรับฉัน แต่ฉันไม่รู้ว่ามันจริงหรือไม่
ตาราง Bobby น้อย

4
คำตอบนี้ล้าสมัยไปแล้ว ดูคำตอบของ @Ted Petrou ด้านล่าง
cxrodgers

หากคุณต้องการใช้รหัสนี้ แต่ในกรณีที่มีมากกว่าหนึ่งคอลัมน์ใน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 Said

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ฟังก์ชั่นบางอย่างในขณะที่ดรอป ตัวอย่างเช่นฉันจะวางเฉพาะค่าที่น้อยกว่าค่าเฉลี่ยของค่าซ้ำเหล่านั้นได้อย่างไร
เด็กซ์เตอร์

16

วิธีที่ง่ายที่สุด:

หากต้องการวางรายการซ้ำตามคอลัมน์เดียว:

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

หากต้องการวางรายการซ้ำตามคอลัมน์หลายคอลัมน์:

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

1
ทางออกที่ดีที่สุด ขอบคุณ
Flavio

ดีใจที่ได้ช่วยเหลือ @Flavio
Gil Baggio

กรอบข้อมูลของฉันมี 10 คอลัมน์และฉันใช้รหัสนี้เพื่อลบรายการที่ซ้ำจากสามคอลัมน์ อย่างไรก็ตามมันลบแถวออกจากคอลัมน์ที่เหลือ มีวิธีใดที่จะลบรายการที่ซ้ำกันสำหรับ 4 คอลัมน์สุดท้ายเท่านั้น?
โซเฟีย

2
แต่ OP ต้องการเก็บค่าสูงสุดในคอลัมน์ B สิ่งนี้อาจใช้ได้ถ้าคุณเรียงลำดับก่อน แต่โดยทั่วไปแล้วคำตอบของ Ted Petrou
Teepeemm

7

ลองสิ่งนี้:

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

1
คุณรู้จักสำนวนที่ดีที่สุดในการทำดัชนีนี้ใหม่ให้เหมือน DataFrame ดั้งเดิมหรือไม่? ฉันพยายามที่จะคิดออกว่าเมื่อคุณนินจาฉัน : ^)
DSM

4
เรียบร้อย เกิดอะไรขึ้นถ้าไฟล์ข้อมูลมีคอลัมน์มากขึ้น (เช่น C, D, E) Max ดูเหมือนจะไม่ทำงานในกรณีนั้นเนื่องจากเราต้องระบุว่า B เป็นคอลัมน์เดียวที่ต้องขยายให้ใหญ่สุด
Abe

1
@DSM ตรวจสอบลิงก์ในคำถามเดิม มีบางรหัสที่จะจัดทำดัชนีข้อมูลที่จัดกลุ่มใหม่
Abe

5

ฉันจะเรียงลำดับไฟล์ข้อมูลก่อนโดยให้คอลัมน์ B ลงมาจากนั้นให้วางรายการที่ซ้ำกันสำหรับคอลัมน์ A และเก็บไว้ก่อน

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

ไม่มีกลุ่มใด



1

ฉันคิดว่าในกรณีของคุณคุณไม่จำเป็นต้องมีกลุ่มด้วย ฉันจะเรียงลำดับจากมากไปหาน้อยคอลัมน์ B ของคุณจากนั้นปล่อยรายการซ้ำที่คอลัมน์ A และหากคุณต้องการคุณสามารถมีดัชนีที่ดีและสะอาดใหม่เช่นนั้น:

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

สิ่งนี้แตกต่างจากโพสต์อื่นอย่างไร
DJK

1

นี่คือรูปแบบที่ผมต้องแก้ปัญหาที่มีมูลค่าการใช้งานร่วมกันสำหรับแต่ละสายที่ไม่ซ้ำกันในที่ฉันต้องการที่จะหาสตริงทั่วไปที่เกี่ยวข้องมากที่สุดในcolumnAcolumnB

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

.any()หยิบหนึ่งถ้ามีการผูกสำหรับโหมดที่ (โปรดทราบว่าการใช้.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

-8

ฉันจะไม่ให้คำตอบทั้งหมดกับคุณ (ฉันไม่คิดว่าคุณกำลังมองหาการแยกวิเคราะห์และเขียนลงในส่วนของไฟล์ต่อไป) แต่คำใบ้สำคัญควรจะพอเพียง: ใช้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 ของแพนด้าเป็นชุดจากนั้นการแปลงกลับมาดูเหมือนจะเป็นวิธีที่ไม่มีประสิทธิภาพในการแก้ปัญหานี้ ฉันกำลังทำการวิเคราะห์ล็อกดังนั้นฉันจะใช้มันกับชุดข้อมูลขนาดใหญ่มาก
Abe

ขออภัยฉันไม่รู้มากเกี่ยวกับสถานการณ์เฉพาะนี้ดังนั้นอาจเป็นไปได้ว่าคำตอบทั่วไปของฉันจะไม่กลายเป็นปัญหาที่มีประสิทธิภาพมากเกินไปสำหรับปัญหาของคุณ
Abhranil Das
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.