2012-12-12 46 views
32

Ho il dataframe di panda python, in cui una colonna contiene il nome del mese.Ordinamento personalizzato in dataframe panda

Come posso fare un ordinamento personalizzato utilizzando un dizionario, per esempio:

custom_dict = {'March':0, 'April':1, 'Dec':3} 
+1

Una colonna contiene il nome del mese significa che esiste una colonna che contiene i nomi dei mesi (come risposta) o molte colonne con i nomi delle colonne come nomi dei mesi (come eumiro)? –

risposta

48

Pandas 0,15 introdotto Categorical Series, che permette ad un modo molto più chiaro per fare questo:

Prima di rendere la colonna del mese un categorico e specificare 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 mese sarà ordinare rispetto a quella lista:

In [23]: df.sort("m") 
Out[23]: 
    a b  m 
0 1 2 March 
2 3 4 April 
1 5 6 Dec 

Nota: se un valore non è nella lista che verrà convertito in NaN.


Una risposta più anziani per chi è interessato ...

Si potrebbe creare una serie di intermediari, e su quel 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() 

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, in panda più recenti, La serie ha un metodo replace per eseguire questa operazione in modo più elegante:

s = df['m'].replace({'March':0, 'April':1, 'Dec':3}) 

La leggera differenza è che questo non si alza se c'è un valore al di fuori del dizionario (rimarrà lo stesso).

+0

's = df ['m']. Replace ({'March': 0, 'April': 1, 'Dec': 3})' funziona anche per la riga 2 - solo per il bene di chiunque stia imparando i panda come me – kdauria

+0

@kdauria buon punto! (da un po 'di tempo da quando ho scritto questo!) sostituire definitivamente l'opzione migliore, un'altra è usare '.apply ({' March ': 0,' April ': 1,' Dec ': 3} .get)' :) In 0.15 avremo categorie/colonne categoriali, quindi il modo migliore sarà quello di usarlo e poi sortirà solo il suo funzionamento. –

+0

@AndyHayden Mi sono preso la libertà di sostituire la seconda riga con il metodo 'replace'. Spero che sia Ok. –

2
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 marzo, aprile, dicembre

+0

Funziona e semplifica la scrittura di qualsiasi codice esistente. Grazie! – posdef

8

Un po 'in ritardo rispetto al gioco, ma ecco un modo per creare una funzione che ordina gli oggetti serieFrame, DataFrame e multiindex DataFrame utilizzando funzioni arbitrarie.

Mi avvalgo del metodo df.iloc[index], che fa riferimento a una riga in un Series/DataFrame in base alla posizione (rispetto a df.loc, che fa riferimento per valore). Usando questo, dobbiamo solo avere una funzione che restituisce 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 utilizzare questo per creare funzioni di ordinamento personalizzato. Questo funziona sulla dataframe utilizzato in 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 

Questo funziona anche su DataFrames multiindex e serie di oggetti:

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 si sente pulito, ma utilizza le operazioni di pitone pesantemente piuttosto che fare affidamento su operazioni panda ottimizzate. Non ho effettuato test di stress ma immagino che questo potrebbe rallentare su DataFrames di grandi dimensioni. Non sei sicuro di come il rendimento si paragona all'aggiunta, all'ordinamento e all'eliminazione di una colonna. Qualche consiglio su come accelerare il codice sarebbe apprezzato!

+0

Questo dovrebbe essere usato per ordinare più colonne/indici? – ConanG

+0

sì, ma la risposta selezionata è un modo molto migliore per farlo. Se hai più indici, sistemali secondo l'ordine che preferisci, quindi usa 'df.sort_index()' per ordinare tutti i livelli di indice. – delgadom