2015-04-11 46 views
8

Ho un DataFrame panda con indici che voglio ordinare naturalmente. Natsort non sembra funzionare. L'ordinamento degli indici prima della creazione di DataFrame non sembra essere di aiuto in quanto le manipolazioni che faccio sul DataFrame sembrano rovinare l'ordinamento nel processo. Qualche idea su come posso ricorrere naturalmente agli indici?Ordinamento naturale Pandas DataFrame

from natsort import natsorted 
import pandas as pd 

# An unsorted list of strings 
a = ['0hr', '128hr', '72hr', '48hr', '96hr'] 
# Sorted incorrectly 
b = sorted(a) 
# Naturally Sorted 
c = natsorted(a) 

# Use a as the index for a DataFrame 
df = pd.DataFrame(index=a) 
# Sorted Incorrectly 
df2 = df.sort() 
# Natsort doesn't seem to work 
df3 = natsorted(df) 

print(a) 
print(b) 
print(c) 
print(df.index) 
print(df2.index) 
print(df3.index) 
+0

@sethMMorton Supponevo che mi sarebbe piaciuto 'df3.index' per essere uguale a' c' mentre ordinai i dati per mantenerlo in linea con i suoi valori di indice – agf1997

+0

Sarebbe bello se 'pd.sort' avesse un tasto' 'opzione, ma non è così. [Questa risposta] (http://stackoverflow.com/a/27009771/1399279) fornisce una soluzione alternativa che consente di passare una chiave generata da 'natsort_keygen'. – SethMMorton

+1

Ho appena fatto una richiesta ufficiale agli sviluppatori 'pandas' di aggiungere' chiave' ai metodi 'sort' qui: https://github.com/pydata/pandas/issues/9855 – SethMMorton

risposta

6

Se si desidera ordinare il df, appena sorta l'indice oi dati e assegnare direttamente all'indice della df piuttosto che cercare di passare il df come arg come quello produce una lista vuota:

In [7]: 

df.index = natsorted(a) 
df.index 
Out[7]: 
Index(['0hr', '48hr', '72hr', '96hr', '128hr'], dtype='object') 

noti che df.index = natsorted(df.index) funziona anche

se si passa il df come arg produce una lista vuota, in questo caso perché il DF è vuoto (non ha le colonne), altrimenti restituirà le colonne ordinate, che non è quello che vuoi:

In [10]: 

natsorted(df) 
Out[10]: 
[] 

EDIT

Se si desidera ordinare l'indice in modo che i dati vengono riordinati con l'indice quindi utilizzare reindex:

In [13]: 

df=pd.DataFrame(index=a, data=np.arange(5)) 
df 
Out[13]: 
     0 
0hr 0 
128hr 1 
72hr 2 
48hr 3 
96hr 4 
In [14]: 

df = df*2 
df 
Out[14]: 
     0 
0hr 0 
128hr 2 
72hr 4 
48hr 6 
96hr 8 
In [15]: 

df.reindex(index=natsorted(df.index)) 
Out[15]: 
     0 
0hr 0 
48hr 6 
72hr 4 
96hr 8 
128hr 2 

Nota che si deve assegnare il risultato di reindex a un nuovo df oa sé stesso, non accetta il parametro inplace.

+0

Ciao, sviluppatore 'natsort' qui. 'natsort' al momento non ha alcun supporto esplicito per la gestione di interi oggetti dataframe. Quale sarebbe il risultato atteso per il passaggio di un oggetto dataframe? – SethMMorton

+0

Credo che manchi questo punto. Mi rendo conto che posso naturalmente ordinare l'a e usarlo come indice, ma il mio codice effettivo rovina l'ordinamento dell'indice del frame dei dati a causa delle manipolazioni che eseguo sul dataframe. Ho bisogno di ricorrere all'indice e ai dati associati mentre si trova nel dataframe. – agf1997

+2

Allora, che cosa stai chiedendo qui, vuoi ordinare l'indice dopo le manipolazioni dei dati? Puoi usare 'reindex' e chiamare' natsorted' sull'indice 'df.reindex (index = natsorted (df.index))' – EdChum

13

Il accepted answer risponde alla domanda che viene posta. Mi piacerebbe anche aggiungere come usare natsort su colonne in un DataFrame, dal momento che sarà la prossima domanda posta.

In [1]: from pandas import DataFrame 

In [2]: from natsort import natsorted, index_natsorted, order_by_index 

In [3]: df = DataFrame({'a': ['a5', 'a1', 'a10', 'a2', 'a12'], 'b': ['b1', 'b1', 'b2', 'b2', 'b1']}, index=['0hr', '128hr', '72hr', '48hr', '96hr']) 

In [4]: df 
Out[4]: 
     a b 
0hr  a5 b1 
128hr a1 b1 
72hr a10 b2 
48hr a2 b2 
96hr a12 b1 

quanto accepted answer spettacoli, l'ordinamento per l'indice è abbastanza semplice:

In [5]: df.reindex(index=natsorted(df.index)) 
Out[5]: 
     a b 
0hr  a5 b1 
48hr a2 b2 
72hr a10 b2 
96hr a12 b1 
128hr a1 b1 

Se si desidera ordinare su una colonna nello stesso modo, è necessario ordinare l'indice dall'ordine che la colonna desiderata è stata riordinata. natsort fornisce le funzioni comfort index_natsorted e order_by_index per fare proprio questo.

In [6]: df.reindex(index=order_by_index(df.index, index_natsorted(df.a))) 
Out[6]: 
     a b 
128hr a1 b1 
48hr a2 b2 
0hr  a5 b1 
72hr a10 b2 
96hr a12 b1 

In [7]: df.reindex(index=order_by_index(df.index, index_natsorted(df.b))) 
Out[7]: 
     a b 
0hr  a5 b1 
128hr a1 b1 
96hr a12 b1 
72hr a10 b2 
48hr a2 b2 

Se si desidera riordinare da un numero arbitrario di colonne (o una colonna e l'indice), è possibile utilizzare zip (o itertools.izip su python2) per specificare l'ordinamento su più colonne. La prima colonna data sarà la colonna di ordinamento primario, secondario, terziario poi, ecc ...

In [8]: df.reindex(index=order_by_index(df.index, index_natsorted(zip(df.b, df.a)))) 
Out[8]: 
     a b 
128hr a1 b1 
0hr  a5 b1 
96hr a12 b1 
48hr a2 b2 
72hr a10 b2 

In [9]: df.reindex(index=order_by_index(df.index, index_natsorted(zip(df.b, df.index)))) 
Out[9]: 
     a b 
0hr  a5 b1 
96hr a12 b1 
128hr a1 b1 
48hr a2 b2 
72hr a10 b2 

Ecco un metodo alternativo utilizzando Categorical oggetti che è stato detto dai pandas sviluppatori è il modo "corretto" per farlo. Ciò richiede (per quanto posso vedere) panda> = 0.16.0. Attualmente funziona solo su colonne, ma apparentemente in panda> = 0.17.0 aggiungeranno CategoricalIndex che consentirà di utilizzare questo metodo su un indice.

In [1]: from pandas import DataFrame 

In [2]: from natsort import natsorted 

In [3]: df = DataFrame({'a': ['a5', 'a1', 'a10', 'a2', 'a12'], 'b': ['b1', 'b1', 'b2', 'b2', 'b1']}, index=['0hr', '128hr', '72hr', '48hr', '96hr']) 

In [4]: df.a = df.a.astype('category') 

In [5]: df.a.cat.reorder_categories(natsorted(df.a), inplace=True, ordered=True) 

In [6]: df.b = df.b.astype('category') 

In [8]: df.b.cat.reorder_categories(natsorted(set(df.b)), inplace=True, ordered=True) 

In [9]: df.sort('a') 
Out[9]: 
     a b 
128hr a1 b1 
48hr a2 b2 
0hr  a5 b1 
72hr a10 b2 
96hr a12 b1 

In [10]: df.sort('b') 
Out[10]: 
     a b 
0hr  a5 b1 
128hr a1 b1 
96hr a12 b1 
72hr a10 b2 
48hr a2 b2 

In [11]: df.sort(['b', 'a']) 
Out[11]: 
     a b 
128hr a1 b1 
0hr  a5 b1 
96hr a12 b1 
48hr a2 b2 
72hr a10 b2 

L'oggetto Categorical consente di definire un ordinamento per la DataFrame da usare.Gli elementi forniti quando si chiama reorder_categories devono essere univoci, da qui la chiamata a set per la colonna "b".

Lascio l'utente a decidere se questo è meglio che il metodo reindex o no, dal momento che richiede di ordinare i dati della colonna in modo indipendente prima della cernita all'interno del DataFrame (anche se immagino che secondo tipo è piuttosto efficiente).


Full disclosure, sono l'autore natsort.