Ho dataframe panda python, in cui una colonna contiene il nome del mese.
Come posso eseguire un ordinamento personalizzato utilizzando un dizionario, ad esempio:
custom_dict = {'March':0, 'April':1, 'Dec':3}
Ho dataframe panda python, in cui una colonna contiene il nome del mese.
Come posso eseguire un ordinamento personalizzato utilizzando un dizionario, ad esempio:
custom_dict = {'March':0, 'April':1, 'Dec':3}
pd.Categoricalnon interpreta le categorie come ordinate per impostazione predefinita. Vedi questa risposta .
Risposte:
Pandas 0.15 ha introdotto la serie categoriale , che consente un modo molto più chiaro per farlo:
Per prima cosa rendi categorica la colonna del mese e specifica l'ordine da utilizzare.
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
Ora, quando si ordina la colonna del mese, verrà ordinata rispetto a quella lista:
In [23]: df.sort_values("m")
Out[23]:
a b m
0 1 2 March
2 3 4 April
1 5 6 Dec
Nota: se un valore non è nell'elenco verrà convertito in NaN.
Una vecchia risposta per chi è interessato ...
Potresti creare una serie intermedia e set_indexsu questo:
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
Come commentato, nei nuovi panda, Series ha un replacemetodo per farlo in modo più elegante:
s = df['m'].replace({'March':0, 'April':1, 'Dec':3})
La leggera differenza è che questo non aumenterà se c'è un valore al di fuori del dizionario (rimarrà lo stesso).
s = df['m'].replace({'March':0, 'April':1, 'Dec':3})funziona anche per la riga 2 - solo per il bene di chiunque impari i panda come me
.apply({'March':0, 'April':1, 'Dec':3}.get):) In 0.15 avremo Serie / colonne categoriali, quindi il modo migliore sarà usarle e poi l'ordinamento funzionerà.
df.sort_values("m")nei nuovi panda (invece di df.sort("m")), altrimenti otterrai un AttributeError: 'DataFrame' object has no attribute 'sort';)
Presto potrai usare sort_valuescon keyargomento:
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
L' keyargomento prende come input una serie e restituisce una serie. Questa serie è classificata internamente e gli indici ordinati vengono utilizzati per riordinare il DataFrame di input. Se sono presenti più colonne su cui ordinare, la funzione chiave verrà applicata a ciascuna di esse a turno. Vedere Ordinamento con i tasti .
Un metodo semplice è usare l'output Series.mape Series.argsortindicizzare in dfusing DataFrame.iloc(poiché argsort produce posizioni intere ordinate); visto che hai un dizionario; questo diventa facile.
df.iloc[df['m'].map(custom_dict).argsort()]
a b m
0 1 2 March
2 3 4 April
1 5 6 Dec
Se è necessario ordinare in ordine decrescente , invertire la mappatura.
df.iloc[(-df['m'].map(custom_dict)).argsort()]
a b m
1 5 6 Dec
2 3 4 April
0 1 2 March
Nota che questo funziona solo su elementi numerici. Altrimenti, dovrai risolvere questo problema utilizzando sort_valuese accedendo all'indice:
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
Sono disponibili più opzioni con astype(ora è deprecato), o pd.Categorical, ma è necessario specificarlo ordered=Trueaffinché funzioni correttamente .
# 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)
Ora, una semplice sort_valueschiamata farà il trucco:
df.sort_values('m')
a b m
0 1 2 March
2 3 4 April
1 5 6 Dec
Anche l'ordinamento categoriale sarà rispettato quando groupbyordina l'output.
ordered=Noneper impostazione predefinita. Se non impostato, l'ordine sarà sbagliato o si interromperà su V23. La funzione Max in particolare fornisce un TypeError (Categorical non è ordinato per l'operazione max).
usa la risposta selezionata ! è più recente di questo post e non è solo il modo ufficiale per mantenere i dati ordinati nei panda, è migliore sotto ogni aspetto, comprese le caratteristiche / prestazioni, ecc. Non usare il mio metodo hacky che descrivo di seguito.
Sto solo scrivendo questo aggiornamento perché le persone continuano a votare la mia risposta, ma è decisamente peggio di quella accettata :)
Un po 'in ritardo per il gioco, ma ecco un modo per creare una funzione che ordina gli oggetti panda Series, DataFrame e multiindex DataFrame utilizzando funzioni arbitrarie.
Uso il df.iloc[index]metodo, che fa riferimento a una riga in un Series / DataFrame in base alla posizione (rispetto a df.loc, che fa riferimento in base al valore). Usando questo, dobbiamo solo avere una funzione che restituisca una serie di argomenti posizionali:
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
È possibile utilizzarlo per creare funzioni di ordinamento personalizzate. Funziona sul dataframe utilizzato nella risposta di Andy Hayden:
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
Funziona anche su DataFrame multiindex e oggetti Series:
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
Per me questo sembra pulito, ma utilizza pesantemente le operazioni python piuttosto che fare affidamento su operazioni panda ottimizzate. Non ho eseguito alcun test di stress, ma immagino che questo potrebbe rallentare su DataFrame molto grandi. Non sono sicuro di come le prestazioni si confrontino con l'aggiunta, l'ordinamento e l'eliminazione di una colonna. Eventuali suggerimenti per velocizzare il codice sarebbero apprezzati!
df.sort_index()per ordinare tutti i livelli di indice.
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))
restituisce un DataFrame con colonne March, April, Dec