Tengo un marco de datos de Python Pandas, 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}
Tengo un marco de datos de Python Pandas, 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}
pd.Categorical
que no interpreta las categorías como ordenadas por defecto. Vea esta respuesta .
Respuestas:
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]:
a b m
0 1 2 March
1 5 6 Dec
2 3 4 April
Ahora, cuando clasifique la columna del mes, se ordenará con respecto a esa 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: si un valor no está en la lista, se convertirá a NaN.
Una respuesta más antigua para los interesados ...
Podría crear una serie intermedia, y set_index
sobre 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_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
Como se comentó, en los pandas más nuevos, Series tiene un replace
método 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 aumentará si hay un valor fuera del diccionario (simplemente permanecerá igual).
s = df['m'].replace({'March':0, 'April':1, 'Dec':3})
también funciona para la línea 2, solo por el bien de cualquiera que esté aprendiendo pandas como yo
.apply({'March':0, 'April':1, 'Dec':3}.get)
¡Ha pasado un tiempo desde que escribí esto!) Reemplazar definitivamente la mejor opción, otra es usar :) En 0.15 tendremos Series / columnas categóricas, así que la mejor manera será usar eso y luego ordenar simplemente funcionará.
df.sort_values("m")
en pandas más nuevos (en lugar de df.sort("m")
), de lo contrario obtendrá un AttributeError: 'DataFrame' object has no attribute 'sort'
;)
Pronto podrás usar sort_values
con key
argumento:
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
El key
argumento toma como entrada una Serie y devuelve una Serie. Esta serie se ordena internamente y los índices ordenados se utilizan para reordenar el DataFrame de entrada. Si hay varias columnas para ordenar, la función clave se aplicará a cada una de ellas. Consulte Clasificación con claves .
Un método simple es usar la salida Series.map
e Series.argsort
indexar df
usandoDataFrame.iloc
(ya que argsort produce posiciones enteras ordenadas); ya que tienes un diccionario; esto se vuelve fácil.
df.iloc[df['m'].map(custom_dict).argsort()]
a b m
0 1 2 March
2 3 4 April
1 5 6 Dec
Si necesita ordenar en orden descendente , invierta la asignación.
df.iloc[(-df['m'].map(custom_dict)).argsort()]
a b m
1 5 6 Dec
2 3 4 April
0 1 2 March
Tenga en cuenta que esto solo funciona con elementos numéricos. De lo contrario, deberá solucionar este problema utilizandosort_values
y accediendo al índice:
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
Hay más opciones disponibles con astype
(esto está obsoleto ahora), o pd.Categorical
, pero debe 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, un simple sort_values
llamada hará el truco:
df.sort_values('m')
a b m
0 1 2 March
2 3 4 April
1 5 6 Dec
El orden categórico también se respetará cuando se groupby
clasifique la salida.
ordered=None
por defecto. Si no se establece, el pedido será incorrecto o se interrumpirá en V23. La función Max en particular da un TypeError (Categórico no está ordenado para la operación max).
Un poco tarde para el juego, pero aquí hay una manera de crear una función que clasifique los objetos Pandas Series, DataFrame y DataFrame multiindex usando funciones arbitrarias.
Hago uso del df.iloc[index]
método, que hace referencia a una fila en un Series / DataFrame 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
Puede usar esto para crear funciones de clasificación personalizadas. Esto funciona en el marco de datos 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]:
a b m
0 1 2 March
2 3 4 April
1 5 6 Dec
Esto también funciona en objetos DataFrames y Series de varios índices:
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 usa mucho las operaciones de Python en lugar de depender de las operaciones optimizadas de pandas. No he realizado ninguna prueba de estrés, pero me imagino que esto podría ralentizarse en DataFrames muy grandes. No estoy seguro de cómo se compara el rendimiento con agregar, ordenar y luego eliminar una columna. ¡Se agradecería cualquier consejo sobre cómo acelerar el código!
df.sort_index()
para ordenar todos los niveles de índice.
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