รับแถวที่มีจำนวนสูงสุดในกลุ่มโดยใช้ groupby


244

ฉันจะค้นหาแถวทั้งหมดใน dataframe แพนด้าซึ่งมีค่าสูงสุดได้อย่างไร countคอลัมน์หลังจากจัดกลุ่มตาม['Sp','Mt']คอลัมน์ได้อย่างไร

ตัวอย่างที่ 1: dataFrame ต่อไปนี้ซึ่งฉันจัดกลุ่มตาม['Sp','Mt']:

   Sp   Mt Value   count
0  MM1  S1   a      **3**
1  MM1  S1   n      2
2  MM1  S3   cb     5
3  MM2  S3   mk      **8**
4  MM2  S4   bg     **10**
5  MM2  S4   dgd      1
6  MM4  S2  rd     2
7  MM4  S2   cb      2
8  MM4  S2   uyi      **7**

ผลลัพธ์ที่ควรได้รับ: รับแถวผลลัพธ์ที่มีจำนวนสูงสุดระหว่างกลุ่มเช่น:

0  MM1  S1   a      **3**
1 3  MM2  S3   mk      **8**
4  MM2  S4   bg     **10** 
8  MM4  S2   uyi      **7**

ตัวอย่างที่ 2: ไฟล์ข้อมูลนี้ซึ่งฉันจัดกลุ่มตาม['Sp','Mt']:

   Sp   Mt   Value  count
4  MM2  S4   bg     10
5  MM2  S4   dgd    1
6  MM4  S2   rd     2
7  MM4  S2   cb     8
8  MM4  S2   uyi    8

สำหรับตัวอย่างข้างต้นฉันต้องการแถวทั้งหมดที่มีcountค่าสูงสุดในแต่ละกลุ่มเช่น:

MM2  S4   bg     10
MM4  S2   cb     8
MM4  S2   uyi    8

กรอบข้อมูลของคุณอยู่ในรูปแบบใด
David Robinson

2
ฉันไม่เข้าใจ กลุ่มคืออะไร ทำไมบรรทัดที่สองในผลที่ได้เริ่มต้นด้วย1 3?
Jo So

stackoverflow.com/questions/18879782/… อาจเป็นประโยชน์
J_Arthur

1
คำตอบนี้เป็นคำตอบที่เร็วที่สุดที่ฉันพบได้: stackoverflow.com/a/21007047/778533
tommy.carstensen

คล้ายกับคำถามนี้ใครก็ได้โปรดตอบคำถามนี้: stackoverflow.com/questions/62069465/… ขอบคุณ
ds_Abc

คำตอบ:


325
In [1]: df
Out[1]:
    Sp  Mt Value  count
0  MM1  S1     a      3
1  MM1  S1     n      2
2  MM1  S3    cb      5
3  MM2  S3    mk      8
4  MM2  S4    bg     10
5  MM2  S4   dgd      1
6  MM4  S2    rd      2
7  MM4  S2    cb      2
8  MM4  S2   uyi      7

In [2]: df.groupby(['Mt'], sort=False)['count'].max()
Out[2]:
Mt
S1     3
S3     8
S4    10
S2     7
Name: count

ในการรับดัชนีของ DF ดั้งเดิมคุณสามารถทำได้:

In [3]: idx = df.groupby(['Mt'])['count'].transform(max) == df['count']

In [4]: df[idx]
Out[4]:
    Sp  Mt Value  count
0  MM1  S1     a      3
3  MM2  S3    mk      8
4  MM2  S4    bg     10
8  MM4  S2   uyi      7

โปรดทราบว่าถ้าคุณมีค่าสูงสุดหลายค่าต่อกลุ่มทั้งหมดจะถูกส่งกลับ

ปรับปรุง

มีโอกาสทักทายลูกเห็บว่านี่คือสิ่งที่ OP ร้องขอ

In [5]: df['count_max'] = df.groupby(['Mt'])['count'].transform(max)

In [6]: df
Out[6]:
    Sp  Mt Value  count  count_max
0  MM1  S1     a      3          3
1  MM1  S1     n      2          3
2  MM1  S3    cb      5          8
3  MM2  S3    mk      8          8
4  MM2  S4    bg     10         10
5  MM2  S4   dgd      1         10
6  MM4  S2    rd      2          7
7  MM4  S2    cb      2          7
8  MM4  S2   uyi      7          7

@ Zelazny7 มีวิธีที่จะนำคำตอบนี้ไปใช้กับการจัดกลุ่มตามคอลัมน์แล้วดูที่ 2 คอลัมน์และทำจำนวนสูงสุดเพื่อให้ได้มากกว่าสองหรือไม่ ฉันไม่สามารถทำงานได้ สิ่งที่ฉันมีอยู่ในปัจจุบันคือ: def Greater (Merge, MaximumA, maximumB): a = Merge [maxA] b = Merge [maximumB] return max (a, b) Merger.groupby ("Search_Term") ใช้ (Greater, "Ratio_x "," Ratio_y ")
mathlover

3
@ Zelazny7 ฉันใช้สองidxวิธี แต่ฉันสามารถจ่ายได้สูงสุดเพียงครั้งเดียวสำหรับแต่ละกลุ่ม (และข้อมูลของฉันมีค่าสูงสุดซ้ำกันเล็กน้อย) มีวิธีแก้ไขปัญหานี้ด้วยวิธีการของคุณหรือไม่
3pitt

จริงๆแล้วมันใช้ไม่ได้สำหรับฉัน ฉันไม่สามารถติดตามปัญหาเพราะ dataframe ถ้าเลิกใหญ่ แต่การแก้ปัญหาโดย @Rani ทำงานดี
Ladenkov วลา

สวัสดี Zealzny, ถ้าฉันต้องการที่จะรับสูงสุด 3 แถวสูงสุดแทนค่าสูงสุดหนึ่งฉันจะปรับแต่งรหัสของคุณได้อย่างไร?
Zephyr

transformเมธอดอาจมีประสิทธิภาพการทำงานของพูลเมื่อชุดข้อมูลมีขนาดใหญ่พอรับค่าสูงสุดก่อนจากนั้นผสานดาต้าเฟรมจะดีกว่า
วูดเฉิน

170

คุณสามารถเรียงลำดับ dataFrame ตามจำนวนแล้วลบรายการที่ซ้ำกัน ฉันคิดว่ามันง่ายกว่า:

df.sort_values('count', ascending=False).drop_duplicates(['Sp','Mt'])

4
ดีมาก! เร็วกับเฟรมมากมาย (แถว 25k)
โนแลนโคนาเวย์

2
สำหรับผู้ที่ค่อนข้างใหม่กับ Python คุณจะต้องกำหนดสิ่งนี้ให้กับตัวแปรใหม่มันไม่เปลี่ยนตัวแปร df ปัจจุบัน
Tyler

1
@ ซามีร์หรือใช้inplace = Trueเป็นอาร์กิวเมนต์เพื่อdrop_duplicates
TMrtSmith

5
นี่เป็นคำตอบที่ยอดเยี่ยมเมื่อต้องการเพียงหนึ่งแถวที่มีค่าสูงสุดเท่ากัน แต่มันจะไม่ทำงานอย่างที่คาดไว้ถ้าฉันต้องการแถวทั้งหมดที่มีค่าสูงสุด
วูดส์เฉิน

1
@WoodsChen มันลดลงซ้ำของ [sp, mt] ดังนั้นในตัวอย่างของคุณผลลัพธ์ควรมีเพียงหนึ่งแถว
Rani

54

การแก้ปัญหาที่ง่ายจะนำไปใช้: ฟังก์ชัน idxmax () เพื่อรับดัชนีแถวที่มีค่าสูงสุด สิ่งนี้จะกรองแถวทั้งหมดที่มีค่าสูงสุดในกลุ่ม

In [365]: import pandas as pd

In [366]: df = pd.DataFrame({
'sp' : ['MM1', 'MM1', 'MM1', 'MM2', 'MM2', 'MM2', 'MM4', 'MM4','MM4'],
'mt' : ['S1', 'S1', 'S3', 'S3', 'S4', 'S4', 'S2', 'S2', 'S2'],
'val' : ['a', 'n', 'cb', 'mk', 'bg', 'dgb', 'rd', 'cb', 'uyi'],
'count' : [3,2,5,8,10,1,2,2,7]
})

In [367]: df                                                                                                       
Out[367]: 
   count  mt   sp  val
0      3  S1  MM1    a
1      2  S1  MM1    n
2      5  S3  MM1   cb
3      8  S3  MM2   mk
4     10  S4  MM2   bg
5      1  S4  MM2  dgb
6      2  S2  MM4   rd
7      2  S2  MM4   cb
8      7  S2  MM4  uyi


### Apply idxmax() and use .loc() on dataframe to filter the rows with max values:
In [368]: df.loc[df.groupby(["sp", "mt"])["count"].idxmax()]                                                       
Out[368]: 
   count  mt   sp  val
0      3  S1  MM1    a
2      5  S3  MM1   cb
3      8  S3  MM2   mk
4     10  S4  MM2   bg
8      7  S2  MM4  uyi

### Just to show what values are returned by .idxmax() above:
In [369]: df.groupby(["sp", "mt"])["count"].idxmax().values                                                        
Out[369]: array([0, 2, 3, 4, 8])

4
ผู้ถามระบุไว้ที่นี่"I want to get ALL the rows where count equals max in each group"ในขณะที่idxmax Return[s] index of first occurrence of maximum over requested axis"เป็นไปตามเอกสาร (0.21)
พลังสูงสุด

1
นี่เป็นทางออกที่ดี แต่สำหรับปัญหาที่แตกต่าง
Carlos Souza

33

หลังจากได้ลองใช้วิธีแก้ปัญหาที่แนะนำโดย Zelazny ใน DataFrame ที่ค่อนข้างใหญ่ (~ 400k แถว) ฉันพบว่ามันช้ามาก นี่เป็นอีกทางเลือกหนึ่งที่ฉันพบว่าสามารถรันคำสั่งขนาดได้เร็วขึ้นในชุดข้อมูลของฉัน

df = pd.DataFrame({
    'sp' : ['MM1', 'MM1', 'MM1', 'MM2', 'MM2', 'MM2', 'MM4', 'MM4', 'MM4'],
    'mt' : ['S1', 'S1', 'S3', 'S3', 'S4', 'S4', 'S2', 'S2', 'S2'],
    'val' : ['a', 'n', 'cb', 'mk', 'bg', 'dgb', 'rd', 'cb', 'uyi'],
    'count' : [3,2,5,8,10,1,2,2,7]
    })

df_grouped = df.groupby(['sp', 'mt']).agg({'count':'max'})

df_grouped = df_grouped.reset_index()

df_grouped = df_grouped.rename(columns={'count':'count_max'})

df = pd.merge(df, df_grouped, how='left', on=['sp', 'mt'])

df = df[df['count'] == df['count_max']]

1
แน่นอนนี้เร็วกว่ามาก การแปลงดูเหมือนจะช้าสำหรับชุดข้อมูลขนาดใหญ่
goh

1
คุณสามารถเพิ่มความคิดเห็นเพื่ออธิบายว่าแต่ละบรรทัดทำอะไรได้บ้าง
tommy.carstensen

fwiw: ฉันพบวิธีแก้ปัญหาที่ดูสวยงามมากขึ้นจาก @ Zelazny7 ใช้เวลานานในการประมวลผลสำหรับชุดแถว ~ 100K ของฉัน แต่อันนี้วิ่งเร็วมาก (ฉันใช้ 0.13.0 ล้าสมัยไปแล้วซึ่งอาจเป็นสาเหตุของความล่าช้า)
Roland

2
แต่การทำเช่นนี้df[df['count'] == df['count_max']]จะทำให้แถว NaN สูญเสียเช่นเดียวกับคำตอบข้างต้น
Qy Zuo

ฉันขอแนะนำให้ใช้วิธีนี้สำหรับเฟรมข้อมูลที่ใหญ่กว่ามันเร็วกว่าการใช้ .appy () หรือ .agg ()
Touya D. Serdan

18

คุณอาจไม่จำเป็นต้องทำกับกลุ่มโดยใช้sort_values+drop_duplicates

df.sort_values('count').drop_duplicates(['Sp','Mt'],keep='last')
Out[190]: 
    Sp  Mt Value  count
0  MM1  S1     a      3
2  MM1  S3    cb      5
8  MM4  S2   uyi      7
3  MM2  S3    mk      8
4  MM2  S4    bg     10

ตรรกะเดียวกันเกือบโดยใช้ tail

df.sort_values('count').groupby(['Sp', 'Mt']).tail(1)
Out[52]: 
    Sp  Mt Value  count
0  MM1  S1     a      3
2  MM1  S3    cb      5
8  MM4  S2   uyi      7
3  MM2  S3    mk      8
4  MM2  S4    bg     10

ไม่เพียง แต่คำสั่งนี้จะมีขนาดที่เร็วกว่าโซลูชันอื่น ๆ (อย่างน้อยสำหรับกรณีการใช้งานของฉัน) แต่ก็มีประโยชน์เพิ่มเติมจากการผูกมัดเพียงเป็นส่วนหนึ่งของการสร้างดาต้าเฟรมเดิม
Clay

ฉันเกาหัวของฉันคิดว่านี่เป็นเรื่องง่ายขอบคุณสำหรับคำตอบที่ยอดเยี่ยมของคุณเช่นเดียวกับนายเหวิน
Datanovice

7

สำหรับฉันทางออกที่ง่ายที่สุดคือรักษามูลค่าเมื่อจำนวนเท่ากับค่าสูงสุด ดังนั้นคำสั่งหนึ่งบรรทัดต่อไปนี้จึงเพียงพอ:

df[df['count'] == df.groupby(['Mt'])['count'].transform(max)]

4

การใช้งานgroupbyและidxmaxวิธีการ:

  1. โอนเทือกเขาdateไปที่datetime:

    df['date']=pd.to_datetime(df['date'])
  2. รับดัชนีmaxของคอลัมน์dateหลังจากgroupyby ad_id:

    idx=df.groupby(by='ad_id')['date'].idxmax()
  3. รับข้อมูลที่ต้องการ:

    df_max=df.loc[idx,]

ออก [54]:

ad_id  price       date
7     22      2 2018-06-11
6     23      2 2018-06-22
2     24      2 2018-06-30
3     28      5 2018-06-22

2
df = pd.DataFrame({
'sp' : ['MM1', 'MM1', 'MM1', 'MM2', 'MM2', 'MM2', 'MM4', 'MM4','MM4'],
'mt' : ['S1', 'S1', 'S3', 'S3', 'S4', 'S4', 'S2', 'S2', 'S2'],
'val' : ['a', 'n', 'cb', 'mk', 'bg', 'dgb', 'rd', 'cb', 'uyi'],
'count' : [3,2,5,8,10,1,2,2,7]
})

df.groupby(['sp', 'mt']).apply(lambda grp: grp.nlargest(1, 'count'))

2

ตระหนักว่า"ใช้" "nlargest"เพื่อวัตถุ groupbyทำงานได้ดี:

ข้อได้เปรียบเพิ่มเติม - นอกจากนี้ยังสามารถดึง ค่า n อันดับสูงสุดหากต้องการ:

In [85]: import pandas as pd

In [86]: df = pd.DataFrame({
    ...: 'sp' : ['MM1', 'MM1', 'MM1', 'MM2', 'MM2', 'MM2', 'MM4', 'MM4','MM4'],
    ...: 'mt' : ['S1', 'S1', 'S3', 'S3', 'S4', 'S4', 'S2', 'S2', 'S2'],
    ...: 'val' : ['a', 'n', 'cb', 'mk', 'bg', 'dgb', 'rd', 'cb', 'uyi'],
    ...: 'count' : [3,2,5,8,10,1,2,2,7]
    ...: })

## Apply nlargest(1) to find the max val df, and nlargest(n) gives top n values for df:
In [87]: df.groupby(["sp", "mt"]).apply(lambda x: x.nlargest(1, "count")).reset_index(drop=True)
Out[87]:
   count  mt   sp  val
0      3  S1  MM1    a
1      5  S3  MM1   cb
2      8  S3  MM2   mk
3     10  S4  MM2   bg
4      7  S2  MM4  uyi

2

ลองใช้ "nlargest" บนวัตถุ groupby ข้อได้เปรียบของการใช้ nlargest ก็คือมันจะส่งคืนดัชนีของแถวที่เรียก "ไอเท็ม nlargest" (s) หมายเหตุ: เราแบ่งองค์ประกอบที่สอง (1) ของดัชนีของเราเนื่องจากดัชนีของเราในกรณีนี้ประกอบด้วย tuples (เช่น. (s1, 0))

df = pd.DataFrame({
'sp' : ['MM1', 'MM1', 'MM1', 'MM2', 'MM2', 'MM2', 'MM4', 'MM4','MM4'],
'mt' : ['S1', 'S1', 'S3', 'S3', 'S4', 'S4', 'S2', 'S2', 'S2'],
'val' : ['a', 'n', 'cb', 'mk', 'bg', 'dgb', 'rd', 'cb', 'uyi'],
'count' : [3,2,5,8,10,1,2,2,7]
})

d = df.groupby('mt')['count'].nlargest(1) # pass 1 since we want the max

df.iloc[[i[1] for i in d.index], :] # pass the index of d as list comprehension

ป้อนคำอธิบายรูปภาพที่นี่


1

ฉันใช้สไตล์การทำงานนี้สำหรับการทำงานกลุ่มหลายอย่าง:

df = pd.DataFrame({
   'Sp' : ['MM1', 'MM1', 'MM1', 'MM2', 'MM2', 'MM2', 'MM4', 'MM4', 'MM4'],
   'Mt' : ['S1', 'S1', 'S3', 'S3', 'S4', 'S4', 'S2', 'S2', 'S2'],
   'Val' : ['a', 'n', 'cb', 'mk', 'bg', 'dgb', 'rd', 'cb', 'uyi'],
   'Count' : [3,2,5,8,10,1,2,2,7]
})

df.groupby('Mt')\
  .apply(lambda group: group[group.Count == group.Count.max()])\
  .reset_index(drop=True)

    sp  mt  val  count
0  MM1  S1    a      3
1  MM4  S2  uyi      7
2  MM2  S3   mk      8
3  MM2  S4   bg     10

.reset_index(drop=True) นำคุณกลับไปที่ดัชนีเดิมโดยวางดัชนีกลุ่ม

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