Holen Sie sich mit groupby die Zeilen, die die maximale Anzahl in Gruppen haben

242

Wie finde ich alle Zeilen in einem Pandas-Datenrahmen, die countnach der Gruppierung nach ['Sp','Mt']Spalten den Maximalwert für die Spalte haben ?

Beispiel 1: Der folgende Datenrahmen, nach dem ich gruppiere ['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**

Erwartete Ausgabe: Ermittelt die Ergebniszeilen, deren Anzahl zwischen den Gruppen maximal ist, wie z.

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

Beispiel 2: Dieser Datenrahmen, nach dem ich gruppiere ['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

Für das obige Beispiel möchte ich alle Zeilen erhalten, in denen countmax gleich ist, in jeder Gruppe, z.

MM2  S4   bg     10
MM4  S2   cb     8
MM4  S2   uyi    8
jojo12
quelle
In welchem ​​Format befindet sich Ihr Datenrahmen?
David Robinson
2
Ich verstehe es nicht Was genau ist eine Gruppe? Warum beginnt die zweite Zeile im Ergebnis mit 1 3?
Jo So
stackoverflow.com/questions/18879782/… Könnte nützlich sein
J_Arthur
1
Diese Antwort ist die schnellste Lösung, die ich finden konnte: stackoverflow.com/a/21007047/778533
tommy.carstensen
Könnte jemand, ähnlich wie bei dieser Frage, dies bitte beantworten: stackoverflow.com/questions/62069465/… Danke.
ds_Abc

Antworten:

323
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

Um die Indizes des ursprünglichen DF zu erhalten, können Sie Folgendes tun:

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

Beachten Sie, dass bei mehreren Maximalwerten pro Gruppe alle zurückgegeben werden.

Aktualisieren

Auf eine Hagel-Mary-Chance, dass dies das ist, was das OP verlangt:

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
quelle
@ Zelazny7, gibt es eine Möglichkeit, diese Antwort auf die Gruppierung nach einer Spalte anzuwenden und dann 2 Spalten zu betrachten und maximal diese zu verwenden, um eine größere der beiden zu erhalten? Ich kann das nicht zum Laufen bringen. Was ich derzeit habe, ist: def Größer (Zusammenführen, MaximumA, MaximumB): a = Zusammenführen [MaximumA] b = Zusammenführen [MaximumB] Rückgabe max (a, b) Merger.groupby ("Search_Term"). Apply (Größer, "Ratio_x "," Ratio_y ")
Mathlover
3
@ Zelazny7 Ich benutze den zweiten idxAnsatz. Ich kann mir jedoch nur ein einziges Maximum für jede Gruppe leisten (und meine Daten haben einige doppelte Duplikate). Gibt es eine Möglichkeit, dies mit Ihrer Lösung zu umgehen?
Björks Nummer eins Fan
Eigentlich funktioniert das bei mir nicht. Ich kann das Problem nicht verfolgen, weil der Datenrahmen groß ist, aber die Lösung von @Rani funktioniert gut
Ladenkov Vladislav
Hallo Zealzny, wenn ich anstelle eines Maximalwerts die Top-3-Maximalzeile verwenden möchte, wie kann ich Ihren Code optimieren?
Zephyr
transformDie Methode kann eine Poolleistung aufweisen, wenn der Datensatz groß genug ist. Ermitteln Sie zuerst den Maximalwert und führen Sie dann das Zusammenführen der Datenrahmen besser aus.
Woods Chen
169

Sie können den dataFrame nach Anzahl sortieren und dann Duplikate entfernen. Ich denke es ist einfacher:

df.sort_values('count', ascending=False).drop_duplicates(['Sp','Mt'])
Rani
quelle
4
Sehr schön! Schnell mit großen Rahmen (25k Reihen)
Nolan Conaway
2
Für diejenigen, die mit Python etwas neu sind, müssen Sie dies einer neuen Variablen zuweisen, es ändert nichts an der aktuellen df-Variablen.
Tyler
1
@ Samir oder inplace = Trueals Argument fürdrop_duplicates
TMrtSmith
5
Dies ist eine gute Antwort, wenn nur eine der Zeilen mit denselben Maximalwerten benötigt wird. Es funktioniert jedoch nicht wie erwartet, wenn ich alle Zeilen mit Maximalwerten benötige.
Woods Chen
1
@WoodsChen werden Duplikate von [sp, mt] gelöscht. In Ihrem Beispiel sollte die Ausgabe daher nur eine Zeile lang sein.
Rani
54

Eine einfache Lösung wäre die Anwendung der Funktion: idxmax (), um Zeilenindizes mit Maximalwerten abzurufen. Dies würde alle Zeilen mit dem Maximalwert in der Gruppe herausfiltern.

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])
Surya
quelle
4
Der Fragesteller hat hier angegeben "I want to get ALL the rows where count equals max in each group", während idxmax Return[s] index of first occurrence of maximum over requested axis"gemäß den Dokumenten (0.21).
Max Power
1
Dies ist eine großartige Lösung, aber für ein anderes Problem
Carlos Souza
33

Nachdem ich die von Zelazny vorgeschlagene Lösung auf einem relativ großen DataFrame (~ 400.000 Zeilen) ausprobiert hatte, stellte ich fest, dass sie sehr langsam ist. Hier ist eine Alternative, die ich gefunden habe, um Größenordnungen schneller in meinem Datensatz auszuführen.

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']]
Landewednack
quelle
1
in der Tat ist dies viel schneller. Die Transformation scheint für große Datenmengen langsam zu sein.
Goh
1
Können Sie Kommentare hinzufügen, um zu erklären, was jede Zeile tut?
Tommy.carstensen
fwiw: Ich habe festgestellt, dass die Ausführung der eleganteren Lösung von @ Zelazny7 für meinen Satz von ~ 100.000 Zeilen lange gedauert hat, aber diese lief ziemlich schnell. (Ich verwende eine mittlerweile veraltete Version 0.13.0, die möglicherweise für Langsamkeit verantwortlich ist.)
Roland
2
Wenn Sie dies tun df[df['count'] == df['count_max']], verlieren Sie jedoch NaN-Zeilen sowie die obigen Antworten.
Qy Zuo
Ich empfehle dringend, diesen Ansatz zu verwenden. Bei größeren Datenrahmen ist die Verwendung von .appy () oder .agg () viel schneller.
Touya D. Serdan
18

Möglicherweise müssen Sie nicht mit sort_values+ gruppierendrop_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

Auch fast die gleiche Logik mit 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
YOBEN_S
quelle
Dies ist nicht nur eine Größenordnung schneller als die anderen Lösungen (zumindest für meinen Anwendungsfall), sondern hat auch den zusätzlichen Vorteil, dass sie im Rahmen der Erstellung des ursprünglichen Datenrahmens einfach verkettet werden.
Clay
Ich kratzte mir am Kopf und dachte, dass dies einfach ist. Vielen Dank für Ihre brillante Antwort, wie immer, Herr Wen.
Datanovice
7

Für mich wäre die einfachste Lösung, den Wert beizubehalten, wenn die Anzahl gleich dem Maximum ist. Daher reicht der folgende einzeilige Befehl aus:

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

Verwendung groupbyund idxmaxMethoden:

  1. Übertragen Sie col datean datetime:

    df['date']=pd.to_datetime(df['date'])
  2. Holen Sie sich den Index maxder Spalte datenach groupyby ad_id:

    idx=df.groupby(by='ad_id')['date'].idxmax()
  3. Holen Sie sich die gewünschten Daten:

    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
blueear
quelle
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'))
George Liu
quelle
2

Erkennen, dass "Anwenden" "am größten" auf Groupby-Objekt genauso gut funktioniert:

Zusätzlicher Vorteil - kann bei Bedarf auch Top-n-Werte abrufen:

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
Surya
quelle
2

Versuchen Sie, "nlargest" für das groupby-Objekt zu verwenden. Der Vorteil der Verwendung von nlargest besteht darin, dass der Index der Zeilen zurückgegeben wird, aus denen "die größten Elemente" abgerufen wurden. Hinweis: Wir schneiden das zweite (1) Element unseres Index auf, da unser Index in diesem Fall aus Tupeln besteht (z. B. (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

Geben Sie hier die Bildbeschreibung ein

escha
quelle
1

Ich habe diesen Funktionsstil für viele Gruppenoperationen verwendet:

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) bringt Sie zurück zum ursprünglichen Index, indem Sie den Gruppenindex löschen.

joh-mue
quelle