Clasificación personalizada en el dataframe de pandas

Tengo el dataframe de los pandas de Python, en el que una columna contiene el nombre del mes.

¿Cómo puedo hacer una ordenación personalizada usando un diccionario, por ejemplo:

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

Pandas 0.15 introdujo la serie categórica , que permite una forma mucho más clara de hacer esto:

Primero, haga que la columna del mes sea categórica y especifique el orden a utilizar.

 In [21]: df['m'] = pd.Categorical(df['m'], ["March", "April", "Dec"]) In [22]: df # looks the same! Out[22]: abm 0 1 2 March 1 5 6 Dec 2 3 4 April 

Ahora, cuando ordene la columna del mes, se ordenará con respecto a esa lista:

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

Nota: si un valor no está en la lista, se convertirá a NaN.


Una respuesta más antigua para los interesados ​​…

Podrías crear una serie intermedia, y set_index en eso:

 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]: abm 0 1 2 March 1 3 4 April 2 5 6 Dec 

Como comentamos, en los pandas más nuevos, la Serie tiene un método de replace para hacer esto de manera más elegante:

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

La pequeña diferencia es que esto no boostá si hay un valor fuera del diccionario (simplemente seguirá igual).

 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)) 

devuelve un DataFrame con columnas marzo, abril, diciembre

Un poco tarde para el juego, pero aquí hay una manera de crear una función que ordena los objetos Pandas Series, DataFrame y multiindex DataFrame usando funciones arbitrarias.

Hago uso del método df.iloc[index] , que hace referencia a una fila en una Serie / Marco de datos por posición (en comparación con df.loc , que hace referencia por valor). Usando esto, solo tenemos que tener una función que devuelva una serie de argumentos posicionales:

 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 

Puedes usar esto para crear funciones de clasificación personalizadas. Esto funciona en el dataframe utilizado en la respuesta de 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]: abm 0 1 2 March 2 3 4 April 1 5 6 Dec 

Esto también funciona en objetos MultiFindex DataFrames y 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 

Para mí, esto se siente limpio, pero utiliza las operaciones de Python en gran medida en lugar de confiar en las operaciones de pandas optimizadas. No he realizado ninguna prueba de estrés, pero me imagino que esto podría demorarse en DataFrames muy grandes. No estoy seguro de cómo se compara el rendimiento con agregar, ordenar y luego eliminar una columna. ¡Cualquier consejo para acelerar el código sería apreciado!

v0.23 + Respuesta: la sort está en desuso.
… Pero ese no es el punto de esta respuesta. Hay múltiples opciones para hacer esto.

Un método simple es usar la salida Series.map y Series.argsort para indexar en df usando DataFrame.iloc (ya que argsort produce posiciones enteras ordenadas); ya que tienes un diccionario; esto se vuelve fácil.

 # Setup df abm 0 1 2 March 1 5 6 Dec 2 3 4 April custom_dict = {'March': 0, 'April': 1, 'Dec': 3} df.iloc[df['m'].map(custom_dict).argsort()] abm 0 1 2 March 2 3 4 April 1 5 6 Dec 

Si necesita clasificar en orden descendente , invierta la asignación.

 df.iloc[(-df['m'].map(custom_dict)).argsort()] abm 1 5 6 Dec 2 3 4 April 0 1 2 March 

Tenga en cuenta que esto solo funciona en elementos numéricos. De lo contrario, deberá solucionar esto utilizando sort_values y accediendo al índice:

 df.loc[df['m'].map(custom_dict).sort_values(ascending=False).index] abm 1 5 6 Dec 2 3 4 April 0 1 2 March 

Hay más opciones disponibles con astype (ahora está en desuso), o pd.Categorical , pero necesita especificar ordered=True para que funcione correctamente .

 # 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 ) 

Ahora, una simple llamada sort_values hará el truco:

 df.sort_values('m') abm 0 1 2 March 2 3 4 April 1 5 6 Dec 

El ordenamiento categórico también se groupby cuando groupby la salida.