Ich habe einen Datenrahmen mit drei Zeichenfolgenspalten. Ich weiß, dass der einzige Wert in der 3. Spalte für jede Kombination der ersten beiden gültig ist. Um die Daten zu bereinigen, muss ich nach Datenrahmen nach den ersten beiden Spalten gruppieren und für jede Kombination den häufigsten Wert der dritten Spalte auswählen.
Mein Code:
import pandas as pd
from scipy import stats
source = pd.DataFrame({'Country' : ['USA', 'USA', 'Russia','USA'],
'City' : ['New-York', 'New-York', 'Sankt-Petersburg', 'New-York'],
'Short name' : ['NY','New','Spb','NY']})
print source.groupby(['Country','City']).agg(lambda x: stats.mode(x['Short name'])[0])
Die letzte Codezeile funktioniert nicht. Sie lautet "Schlüsselfehler 'Kurzname'". Wenn ich versuche, nur nach Stadt zu gruppieren, wird ein AssertionError angezeigt. Was kann ich tun, um das Problem zu beheben?
.value_counts(ascending=False)
?ascending=False
ist bereits der Standardwert, sodass die Reihenfolge nicht explizit festgelegt werden muss.pd.Series.mode
ist jetzt angemessener und schneller.Pandas> = 0,16
pd.Series.mode
ist verfügbar!Verwendung
groupby
,GroupBy.agg
und die Anwendungpd.Series.mode
Funktion jeder Gruppe:Wenn dies als DataFrame benötigt wird, verwenden Sie
Das Nützliche daran
Series.mode
ist, dass es immer eine Serie zurückgibt, was es sehr kompatibel mitagg
undapply
insbesondere bei der Rekonstruktion der Groupby-Ausgabe macht. Es ist auch schneller.Umgang mit mehreren Modi
Series.mode
macht auch einen guten Job, wenn es mehrere Modi gibt:Wenn Sie für jeden Modus eine eigene Zeile wünschen, können Sie Folgendes verwenden
GroupBy.apply
:Wenn es Ihnen egal ist, welcher Modus zurückgegeben wird, solange es sich um einen von beiden handelt, benötigen Sie ein Lambda, das
mode
das erste Ergebnis aufruft und extrahiert.Alternativen zu (nicht) zu berücksichtigen
Sie können auch
statistics.mode
von Python verwenden, aber ...... es funktioniert nicht gut, wenn mehrere Modi verwendet werden müssen; a
StatisticsError
wird angehoben. Dies wird in den Dokumenten erwähnt:Aber Sie können selbst sehen ...
quelle
df.groupby(cols).agg(pd.Series.mode)
scheint für mich zu arbeiten. Wenn das nicht funktioniert, wäre meine zweite Vermutungdf.groupby(cols).agg(lambda x: pd.Series.mode(x).values[0])
.IndexError: index 0 is out of bounds for axis 0 with size 0
(wahrscheinlich, weil es Gruppen gibt, in denen eine Serie nur NaNs hat). Das Hinzufügendropna=False
löst dieses Problem , scheint sich jedoch zu erhöhen'<' not supported between instances of 'float' and 'str'
(meine Serie besteht aus Zeichenfolgen). (Gerne machen wir daraus eine neue Frage, wenn Sie es vorziehen.)def foo(x): m = pd.Series.mode(x); return m.values[0] if not m.empty else np.nan
und dann verwendendf.groupby(cols).agg(foo)
. Wenn das nicht funktioniert, spielen Siefoo
ein bisschen mit der Implementierung von . Wenn Sie immer noch Probleme mit dem Start haben, empfehle ich, ein neues Q.np.nan
diesdf.groupy(cols).agg(lambda x: x.mode(dropna=False).iloc[0])
für den Modus tun kann , vorausgesetzt, man kümmert sich nicht um Krawatten und möchte nur einen Modus.Denn
agg
die Lambba-Funktion erhält einSeries
, das kein'Short name'
Attribut hat.stats.mode
Gibt ein Tupel aus zwei Arrays zurück, sodass Sie das erste Element des ersten Arrays in diesem Tupel übernehmen müssen.Mit diesen zwei einfachen Änderungen:
kehrt zurück
quelle
scipy.stats
.Ein bisschen zu spät zum Spiel hier, aber ich hatte einige Leistungsprobleme mit der HYRY-Lösung, also musste ich mir eine andere einfallen lassen.
Es funktioniert, indem die Häufigkeit jedes Schlüsselwerts ermittelt wird und dann für jeden Schlüssel nur der Wert beibehalten wird, der am häufigsten mit ihm angezeigt wird.
Es gibt auch eine zusätzliche Lösung, die mehrere Modi unterstützt.
Bei einem Skalentest, der repräsentativ für die Daten ist, mit denen ich arbeite, wurde die Laufzeit von 37,4 auf 0,5 Sekunden reduziert!
Hier ist der Code für die Lösung, einige Beispiele für die Verwendung und der Skalentest:
Wenn Sie diesen Code ausführen, wird Folgendes gedruckt:
Hoffe das hilft!
quelle
agg({'f1':mode,'f2':np.sum})
agg
Methode unterstützt.Die beiden besten Antworten hier legen nahe:
oder vorzugsweise
Beide scheitern jedoch in einfachen Randfällen, wie hier gezeigt:
Der Erste:
Ausbeuten
IndexError
(wegen der leeren Reihe, die von der Gruppe zurückgegeben wirdC
). Der Zweite:gibt zurück
ValueError: Function does not reduce
, da die erste Gruppe eine Liste von zwei zurückgibt (da es zwei Modi gibt). (Wie hier dokumentiert , würde dies funktionieren, wenn die erste Gruppe einen einzelnen Modus zurückgeben würde!)Zwei mögliche Lösungen für diesen Fall sind:
Und die Lösung, die mir cs95 in den Kommentaren hier gegeben hat :
All dies ist jedoch langsam und nicht für große Datenmengen geeignet. Eine Lösung, mit der ich am Ende a) diese Fälle behandeln kann und b) viel, viel schneller ist, ist eine leicht modifizierte Version der Antwort von abw33 (die höher sein sollte):
Im Wesentlichen arbeitet die Methode jeweils mit einer Spalte und gibt einen df aus. Statt
concat
intensiv zu behandeln, behandeln Sie den ersten als df und fügen dann iterativ das Ausgabearray (values.flatten()
) als Spalte im df hinzu.quelle
Formal ist die richtige Antwort die @eumiro-Lösung. Das Problem der @ HYRY-Lösung ist, dass wenn Sie eine Folge von Zahlen wie [1,2,3,4] haben, die Lösung falsch ist, dh Sie haben nicht den Modus . Beispiel:
Wenn Sie wie @HYRY rechnen, erhalten Sie:
Was eindeutig falsch ist (siehe den A- Wert, der 1 und nicht 4 sein sollte ), weil er nicht mit eindeutigen Werten umgehen kann.
Somit ist die andere Lösung richtig:
quelle
Wenn Sie einen anderen Lösungsansatz wünschen, der nicht davon abhängt,
value_counts
oderscipy.stats
Sie dieCounter
Sammlung verwenden könnenWelches kann auf das obige Beispiel wie folgt angewendet werden
quelle
pd.Series.mode
oderpd.Series.value_counts().iloc[0]
- aber wenn Sie NaN-Werte haben, die Sie zählen möchten, schlägt dies fehl. Jedes NaN-Vorkommen wird als von den anderen NaNs verschieden angesehen, so dass jedes NaN als gezählt gezählt wird1
. Siehe stackoverflow.com/questions/61102111/…Das Problem hierbei ist die Leistung. Wenn Sie viele Zeilen haben, ist dies ein Problem.
Wenn es Ihr Fall ist, versuchen Sie bitte Folgendes:
quelle
Ein etwas ungeschickterer, aber schnellerer Ansatz für größere Datensätze besteht darin, die Anzahl für eine interessierende Spalte abzurufen, die Anzahl der höchsten bis niedrigsten zu sortieren und dann eine Teilmenge zu duplizieren, um nur die größten Fälle beizubehalten. Das Codebeispiel lautet wie folgt:
quelle
Wenn Sie keine NaN-Werte einschließen möchten , ist die Verwendung
Counter
viel schneller alspd.Series.mode
oderpd.Series.value_counts()[0]
:sollte arbeiten. Dies schlägt fehl, wenn Sie NaN-Werte haben, da jedes NaN separat gezählt wird.
quelle