Ottieni le righe che hanno il conteggio massimo in gruppi usando groupby


244

Come posso trovare tutte le righe in un frame di dati Panda che hanno il valore massimo per countcolonna, dopo il raggruppamento per ['Sp','Mt']colonne?

Esempio 1: il seguente dataFrame, che raggruppo per ['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 previsto: ottenere le righe dei risultati il ​​cui conteggio è massimo tra i gruppi, come:

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

Esempio 2: questo frame di dati, che raggruppo per ['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

Per l'esempio sopra, voglio ottenere tutte le righe dove è countuguale a max, in ciascun gruppo, ad esempio:

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

In quale formato è il tuo frame di dati?
David Robinson,

2
Non capisco Che cos'è esattamente un gruppo? Perché inizia la seconda riga nel risultato 1 3?
Jo So,


1
Questa risposta è la soluzione più veloce che ho trovato: stackoverflow.com/a/21007047/778533
tommy.carstensen

Simile a questa domanda, qualcuno potrebbe rispondere a questo: stackoverflow.com/questions/62069465/… Grazie.
ds_Abc

Risposte:


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

Per ottenere gli indici del DF originale puoi fare:

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

Nota che se hai più valori massimi per gruppo, tutti verranno restituiti.

Aggiornare

In una grandissima possibilità che questo è ciò che l'OP richiede:

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, c'è un modo per adottare questa risposta da applicare al raggruppamento per colonna e quindi guardare 2 colonne e farne un massimo per ottenere un maggiore dei due? Non riesco a farlo funzionare. Quello che ho attualmente è: def Maggiore (Unisci, massimo A, massimo B): a = Unisci [massimo A] b = Unisci [massimo B] restituisce max (a, b) Fusione.gruppo ("Cerca_Term"). Applica (Maggiore, "Rapporto_x "," Ratio_y ")
mathlover,

3
@ Zelazny7 Sto usando il secondo idxapproccio. Ma posso permettermi solo un massimo per ogni gruppo (e i miei dati hanno pochi duplicati-max). c'è un modo per aggirare questo con la tua soluzione?
3pitt

in realtà, ciò non funziona per me. Non riesco a rintracciare il problema, perché il frame di dati se smette di funzionare, ma la soluzione di @Rani funziona bene
Ladenkov Vladislav,

Ciao Zealzny, se voglio prendere la prima riga massima 3 anziché un valore massimo, come posso modificare il tuo codice?
Zefiro,

transformIl metodo può avere prestazioni del pool quando il set di dati è abbastanza grande, ottenere prima il valore massimo, quindi unire i frame di dati sarà migliore.
Woods Chen

170

È possibile ordinare dataFrame in base al conteggio e quindi rimuovere i duplicati. Penso che sia più facile:

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

4
Molto bella! Veloce con frame di grandi dimensioni (25k righe)
Nolan Conaway,

2
Per coloro che sono in qualche modo nuovi con Python, dovrai assegnarlo a una nuova variabile, non cambia l'attuale variabile df.
Tyler,

1
@Samir o utilizzare inplace = Truecome argomento perdrop_duplicates
TMrtSmith

5
Questa è un'ottima risposta quando serve solo una delle righe con gli stessi valori massimi, tuttavia non funzionerà come previsto se avrò bisogno di tutte le righe con i valori massimi.
Woods Chen

1
@WoodsChen, rilascia duplicati di [sp, mt], quindi nel tuo esempio, l'output dovrebbe essere solo una riga.
Rani,

54

Una soluzione semplice sarebbe applicare: la funzione idxmax () per ottenere indici di righe con valori massimi. Ciò eliminerebbe tutte le righe con il valore massimo nel gruppo.

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
L'interrogatore qui specificato "I want to get ALL the rows where count equals max in each group", mentre idxmax Return[s] index of first occurrence of maximum over requested axis"secondo i documenti (0,21).
Potenza massima

1
Questa è un'ottima soluzione, ma per un problema diverso
Carlos Souza,

33

Dopo aver provato la soluzione suggerita da Zelazny su un DataFrame relativamente grande (~ 400k righe) l'ho trovato molto lento. Ecco un'alternativa che ho trovato per eseguire ordini di grandezza più velocemente sul mio set di dati.

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
in effetti questo è molto più veloce. la trasformazione sembra essere lenta per un set di dati di grandi dimensioni.
Goh,

1
Puoi aggiungere commenti per spiegare cosa fa ogni riga?
tommy.carstensen,

fwiw: ho trovato che la soluzione dall'aspetto più elegante di @ Zelazny7 ha richiesto molto tempo per essere eseguita per il mio set di ~ 100K righe, ma questa è stata eseguita abbastanza rapidamente. (Sto eseguendo un 0.13.0 ormai obsoleto, che potrebbe spiegare la lentezza).
Roland,

2
Ma facendo questo df[df['count'] == df['count_max']]perderanno le righe NaN, così come le risposte sopra.
Qy Zuo,

Consiglio vivamente di usare questo approccio, per frame di dati più grandi è molto più veloce usare .appy () o .agg ().
Touya D. Serdan,

18

Potrebbe non essere necessario avere a che fare con il gruppo utilizzando 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

Anche quasi la stessa logica usando 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

Non solo questo è un ordine di grandezza più veloce delle altre soluzioni (almeno per il mio caso d'uso), ma ha anche il vantaggio di concatenarsi semplicemente come parte della costruzione del dataframe originale.
Clay

Mi stavo grattando la testa pensando sicuramente che fosse semplice, grazie per la tua brillante risposta come sempre il signor Wen.
Datanovice,

7

Per me, la soluzione più semplice sarebbe mantenere il valore quando il conteggio è uguale al massimo. Pertanto, è sufficiente il seguente comando a una riga:

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

4

Uso groupbye idxmaxmetodi:

  1. trasferire col datea datetime:

    df['date']=pd.to_datetime(df['date'])
  2. ottieni l'indice di maxdella colonna date, dopo groupyby ad_id:

    idx=df.groupby(by='ad_id')['date'].idxmax()
  3. ottenere i dati desiderati:

    df_max=df.loc[idx,]

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

Rendendosi conto che "l'applicazione" "nlargest" per GroupBy oggetto funziona altrettanto bene:

Vantaggio aggiuntivo: è anche possibile recuperare i primi n valori se necessario:

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

Prova a utilizzare "nlargest" sull'oggetto groupby. Il vantaggio dell'uso di nlargest è che restituisce l'indice delle righe da cui sono stati recuperati "gli elementi più grandi". Nota: tagliamo il secondo (1) elemento del nostro indice poiché il nostro indice in questo caso è costituito da tuple (ad es. (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

inserisci qui la descrizione dell'immagine


1

Ho usato questo stile funzionale per molte operazioni di gruppo:

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) ti riporta all'indice originale rilasciando l'indice di gruppo.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.