Ich habe Python Pandas Datenrahmen, in dem eine Spalte Monatsnamen enthält.
Wie kann ich eine benutzerdefinierte Sortierung mithilfe eines Wörterbuchs durchführen, zum Beispiel:
custom_dict = {'March':0, 'April':1, 'Dec':3}
Ich habe Python Pandas Datenrahmen, in dem eine Spalte Monatsnamen enthält.
Wie kann ich eine benutzerdefinierte Sortierung mithilfe eines Wörterbuchs durchführen, zum Beispiel:
custom_dict = {'March':0, 'April':1, 'Dec':3}
pd.Categorical
die Kategorien nicht wie standardmäßig sortiert interpretiert werden. Siehe diese Antwort .
Antworten:
Mit Pandas 0.15 wurde die kategoriale Serie eingeführt , mit der dies viel klarer möglich ist:
Machen Sie zuerst die Monatsspalte zu einer Kategorie und geben Sie die zu verwendende Reihenfolge an.
In [21]: df['m'] = pd.Categorical(df['m'], ["March", "April", "Dec"])
In [22]: df # looks the same!
Out[22]:
a b m
0 1 2 March
1 5 6 Dec
2 3 4 April
Wenn Sie nun die Monatsspalte sortieren, wird sie in Bezug auf diese Liste sortiert:
In [23]: df.sort_values("m")
Out[23]:
a b m
0 1 2 March
2 3 4 April
1 5 6 Dec
Hinweis: Wenn ein Wert nicht in der Liste enthalten ist, wird er in NaN konvertiert.
Eine ältere Antwort für Interessierte ...
Sie könnten eine Zwischenserie erstellen, und dazu set_index
:
df = pd.DataFrame([[1, 2, 'March'],[5, 6, 'Dec'],[3, 4, 'April']], columns=['a','b','m'])
s = df['m'].apply(lambda x: {'March':0, 'April':1, 'Dec':3}[x])
s.sort_values()
In [4]: df.set_index(s.index).sort()
Out[4]:
a b m
0 1 2 March
1 3 4 April
2 5 6 Dec
Wie bereits erwähnt, hat Series bei neueren Pandas eine replace
Methode, um dies eleganter zu tun:
s = df['m'].replace({'March':0, 'April':1, 'Dec':3})
Der kleine Unterschied besteht darin, dass dies nicht erhöht wird, wenn es einen Wert außerhalb des Wörterbuchs gibt (er bleibt einfach gleich).
s = df['m'].replace({'March':0, 'April':1, 'Dec':3})
funktioniert auch für Zeile 2 - nur für alle, die Pandas wie mich lernen
.apply({'March':0, 'April':1, 'Dec':3}.get)
:) In 0.15 werden wir kategoriale Reihen / Spalten haben, also wird der beste Weg sein, das zu verwenden und dann zu sortieren wird einfach funktionieren.
df.sort_values("m")
in neueren Pandas (anstelle von df.sort("m")
) verwenden, sonst erhalten Sie ein AttributeError: 'DataFrame' object has no attribute 'sort'
;)
Sie werden bald in der Lage sein, sort_values
mit key
Argumenten zu verwenden:
pd.__version__
# '1.1.0.dev0+2004.g8d10bfb6f'
custom_dict = {'March': 0, 'April': 1, 'Dec': 3}
df
a b m
0 1 2 March
1 5 6 Dec
2 3 4 April
df.sort_values(by=['m'], key=lambda x: x.map(custom_dict))
a b m
0 1 2 March
2 3 4 April
1 5 6 Dec
Das key
Argument nimmt eine Serie als Eingabe und gibt eine Serie zurück. Diese Reihe ist intern sortiert und die sortierten Indizes werden verwendet, um den eingegebenen DataFrame neu zu ordnen. Wenn mehrere Spalten sortiert werden müssen, wird die Schlüsselfunktion nacheinander auf jede Spalte angewendet. Siehe Sortieren mit Schlüsseln .
Eine einfache Methode besteht darin, die Ausgabe zu verwenden Series.map
und Series.argsort
in df
using zu indizieren DataFrame.iloc
(da argsort sortierte ganzzahlige Positionen erzeugt). da du ein Wörterbuch hast; das wird einfach.
df.iloc[df['m'].map(custom_dict).argsort()]
a b m
0 1 2 March
2 3 4 April
1 5 6 Dec
Wenn Sie in absteigender Reihenfolge sortieren müssen , kehren Sie die Zuordnung um.
df.iloc[(-df['m'].map(custom_dict)).argsort()]
a b m
1 5 6 Dec
2 3 4 April
0 1 2 March
Beachten Sie, dass dies nur bei numerischen Elementen funktioniert. Andernfalls müssen Sie dies umgehen sort_values
und auf den Index zugreifen:
df.loc[df['m'].map(custom_dict).sort_values(ascending=False).index]
a b m
1 5 6 Dec
2 3 4 April
0 1 2 March
Weitere Optionen sind verfügbar mit astype
(dies ist jetzt veraltet) oder pd.Categorical
, aber Sie müssen angeben, ordered=True
damit es ordnungsgemäß funktioniert .
# Older version,
# df['m'].astype('category',
# categories=sorted(custom_dict, key=custom_dict.get),
# ordered=True)
df['m'] = pd.Categorical(df['m'],
categories=sorted(custom_dict, key=custom_dict.get),
ordered=True)
Jetzt reicht ein einfacher sort_values
Anruf aus:
df.sort_values('m')
a b m
0 1 2 March
2 3 4 April
1 5 6 Dec
Die kategoriale Reihenfolge wird auch beim groupby
Sortieren der Ausgabe berücksichtigt.
ordered=None
standardmäßig. Wenn nicht eingestellt, ist die Reihenfolge falsch oder bricht auf V23. Insbesondere die Max-Funktion gibt einen TypeError aus (Categorical ist für Operation max nicht geordnet).
Ein bisschen spät im Spiel, aber hier ist eine Möglichkeit, eine Funktion zu erstellen, die Pandas Series-, DataFrame- und Multiindex-DataFrame-Objekte mit beliebigen Funktionen sortiert.
Ich benutze die df.iloc[index]
Methode, die eine Zeile in einem Series / DataFrame nach Position referenziert (im Vergleich zu der df.loc
, die nach Wert referenziert). Damit benötigen wir nur eine Funktion, die eine Reihe von Positionsargumenten zurückgibt:
def sort_pd(key=None,reverse=False,cmp=None):
def sorter(series):
series_list = list(series)
return [series_list.index(i)
for i in sorted(series_list,key=key,reverse=reverse,cmp=cmp)]
return sorter
Hiermit können Sie benutzerdefinierte Sortierfunktionen erstellen. Dies funktioniert mit dem Datenrahmen, der in Andy Haydens Antwort verwendet wird:
df = pd.DataFrame([
[1, 2, 'March'],
[5, 6, 'Dec'],
[3, 4, 'April']],
columns=['a','b','m'])
custom_dict = {'March':0, 'April':1, 'Dec':3}
sort_by_custom_dict = sort_pd(key=custom_dict.get)
In [6]: df.iloc[sort_by_custom_dict(df['m'])]
Out[6]:
a b m
0 1 2 March
2 3 4 April
1 5 6 Dec
Dies funktioniert auch bei Multiindex-DataFrames- und Serienobjekten:
months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']
df = pd.DataFrame([
['New York','Mar',12714],
['New York','Apr',89238],
['Atlanta','Jan',8161],
['Atlanta','Sep',5885],
],columns=['location','month','sales']).set_index(['location','month'])
sort_by_month = sort_pd(key=months.index)
In [10]: df.iloc[sort_by_month(df.index.get_level_values('month'))]
Out[10]:
sales
location month
Atlanta Jan 8161
New York Mar 12714
Apr 89238
Atlanta Sep 5885
sort_by_last_digit = sort_pd(key=lambda x: x%10)
In [12]: pd.Series(list(df['sales'])).iloc[sort_by_last_digit(df['sales'])]
Out[12]:
2 8161
0 12714
3 5885
1 89238
Für mich fühlt sich das sauber an, aber es verwendet Python-Operationen stark, anstatt sich auf optimierte Pandas-Operationen zu verlassen. Ich habe keine Stresstests durchgeführt, aber ich würde mir vorstellen, dass dies bei sehr großen DataFrames langsam werden könnte. Nicht sicher, wie die Leistung im Vergleich zum Hinzufügen, Sortieren und Löschen einer Spalte ist. Tipps zur Beschleunigung des Codes sind willkommen!
df.sort_index()
alle Indexebenen.
import pandas as pd
custom_dict = {'March':0,'April':1,'Dec':3}
df = pd.DataFrame(...) # with columns April, March, Dec (probably alphabetically)
df = pd.DataFrame(df, columns=sorted(custom_dict, key=custom_dict.get))
Gibt einen DataFrame mit den Spalten März, April, Dezember zurück