Dapatkan Baris yang memiliki jumlah maksimum dalam grup menggunakan groupby


244

Bagaimana cara menemukan semua baris dalam kerangka data panda yang memiliki nilai maksimum untuk countkolom, setelah dikelompokkan berdasarkan ['Sp','Mt']kolom?

Contoh 1: dataFrame berikut, yang saya kelompokkan ['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**

Output yang diharapkan: dapatkan baris hasil yang hitungnya maksimum di antara grup, seperti:

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

Contoh 2: bingkai data ini, yang saya kelompokkan dengan ['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

Untuk contoh di atas, saya ingin mendapatkan semua baris di mana countsama dengan maks, di setiap kelompok mis:

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

Apa format frame data Anda?
David Robinson

2
Saya tidak mengerti. Apa sebenarnya grup itu? Mengapa baris kedua pada hasil dimulai dengan 1 3?
Jo So


1
Jawaban ini adalah solusi tercepat yang bisa saya temukan: stackoverflow.com/a/21007047/778533
tommy.carstensen

Mirip dengan pertanyaan ini, dapatkah seseorang menjawab ini: stackoverflow.com/questions/62069465/… Terima kasih.
ds_Abc

Jawaban:


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

Untuk mendapatkan indeks DF asli yang dapat Anda lakukan:

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

Perhatikan bahwa jika Anda memiliki beberapa nilai maks per grup, semua akan dikembalikan.

Memperbarui

Pada kesempatan hujan es bahwa inilah yang diminta 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, adakah cara untuk mengadopsi jawaban ini untuk diterapkan pada pengelompokan oleh sebuah kolom dan kemudian melihat 2 kolom dan melakukan maksimalnya untuk mendapatkan yang lebih besar dari keduanya? Saya tidak bisa membuatnya bekerja. Apa yang saya miliki saat ini adalah: def Greater (Gabung, maksimumA, maksimumB): a = Gabung [maksimumA] b = Gabungkan [maksimumB] kembalikan max (a, b) Merger.groupby ("Search_Term"). Berlaku (Lebih besar, "Ratio_x "," Ratio_y ")
mathlover

3
@ Zelazny7 Saya menggunakan idxpendekatan kedua . Tapi, saya hanya mampu membayar maksimum satu untuk setiap grup (dan data saya memiliki beberapa duplikat-maks). apakah ada cara untuk mengatasi ini dengan solusi Anda?
3pitt

sebenarnya, itu tidak berhasil untuk saya. Saya tidak bisa melacak masalahnya, karena dataframe jika berhenti besar, tetapi solusi oleh @Rani bekerja dengan baik
Ladenkov Vladislav

Hai Zealzny, Jika saya ingin mengambil 3 baris maksimum, bukan satu nilai maks, Bagaimana saya dapat mengubah kode Anda?
Zephyr

transformmetode mungkin memiliki kinerja kumpulan ketika kumpulan data cukup besar, dapatkan nilai maks terlebih dahulu kemudian gabungkan dataframe akan lebih baik.
Woods Chen

170

Anda bisa mengurutkan dataFrame berdasarkan hitungan dan kemudian menghapus duplikat. Saya pikir ini lebih mudah:

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

4
Sangat bagus! Cepat dengan bingkai besar (baris 25rb)
Nolan Conaway

2
Bagi mereka yang agak baru dengan Python, Anda harus menetapkan ini ke variabel baru, itu tidak mengubah variabel df saat ini.
Tyler

1
@ Samir atau gunakan inplace = Truesebagai argumen untukdrop_duplicates
TMrtSmith

5
Ini adalah jawaban yang bagus ketika hanya membutuhkan satu baris dengan nilai maks yang sama, namun itu tidak akan berfungsi seperti yang diharapkan jika saya membutuhkan semua baris dengan nilai maks.
Woods Chen

1
@ WoodsChen, ia menjatuhkan duplikat [sp, mt], oleh karena itu dalam contoh Anda, output harus hanya satu baris.
Rani

54

Solusi mudahnya adalah dengan menerapkan: idxmax () berfungsi untuk mendapatkan indeks baris dengan nilai maksimal. Ini akan memfilter semua baris dengan nilai maksimal dalam grup.

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
Penanya di sini ditentukan "I want to get ALL the rows where count equals max in each group", sementara idxmax Return[s] index of first occurrence of maximum over requested axis"menurut dokumen (0,21).
Max Power

1
Ini adalah solusi hebat, tetapi untuk masalah yang berbeda
Carlos Souza

33

Setelah mencoba solusi yang disarankan oleh Zelazny pada DataFrame yang relatif besar (~ 400k baris) saya merasa sangat lambat. Berikut adalah alternatif yang saya temukan untuk menjalankan pesanan yang jauh lebih cepat pada kumpulan data saya.

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
memang ini jauh lebih cepat. Transformasi tampaknya lambat untuk dataset besar.
goh

1
Bisakah Anda menambahkan komentar untuk menjelaskan apa yang dilakukan setiap baris?
tommy.carstensen

fwiw: Saya menemukan solusi yang terlihat lebih elegan dari @ Zelazny7 membutuhkan waktu lama untuk dieksekusi untuk set ~ 100K baris saya, tetapi yang ini berjalan cukup cepat. (Saya menjalankan 0.13.0 yang sekarang sudah usang, yang bisa menyebabkan kelambatan).
Roland

2
Tetapi melakukan ini df[df['count'] == df['count_max']]akan kehilangan baris NaN, serta jawaban di atas.
Qy Zuo

Saya sangat menyarankan untuk menggunakan pendekatan ini, untuk bingkai data yang lebih besar itu jauh lebih cepat untuk menggunakan .appy () atau .agg ().
Touya D. Serdan

18

Anda mungkin tidak perlu melakukan dengan grup dengan, menggunakan 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

Logikanya juga hampir sama dengan menggunakan 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

Tidak hanya urutan besarnya lebih cepat dari solusi lain (setidaknya untuk kasus penggunaan saya), ini memiliki manfaat tambahan hanya dengan merantai sebagai bagian dari konstruksi kerangka data asli.
Clay

Aku menggaruk kepalaku berpikir pasti ini sederhana, terima kasih atas jawabanmu yang brilian seperti biasa Tuan Wen.
Datanovice

7

Bagi saya, solusi termudah adalah menjaga nilai ketika jumlah sama dengan maksimum. Oleh karena itu, perintah satu baris berikut sudah cukup:

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

4

Gunakan groupbydan idxmaxmetode:

  1. transfer col dateke datetime:

    df['date']=pd.to_datetime(df['date'])
  2. dapatkan indeks maxkolom date, setelah groupyby ad_id:

    idx=df.groupby(by='ad_id')['date'].idxmax()
  3. dapatkan data yang diinginkan:

    df_max=df.loc[idx,]

Keluar [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

Menyadari bahwa "menerapkan" "nlargest" ke objek groupby berfungsi dengan baik:

Keuntungan tambahan - juga dapat mengambil nilai n atas jika diperlukan:

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

Coba gunakan "nlargest" pada objek groupby. Keuntungan menggunakan nlargest adalah ia mengembalikan indeks baris tempat "item nlargest" diambil. Catatan: kami mengiris elemen kedua (1) dari indeks kami karena indeks kami dalam hal ini terdiri dari tupel (mis. (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

masukkan deskripsi gambar di sini


1

Saya telah menggunakan gaya fungsional ini untuk banyak operasi grup:

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) membuat Anda kembali ke indeks asli dengan menjatuhkan indeks grup.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.