¿Una columna contiene el nombre del mes significa que hay una columna que contiene los nombres de los meses (como mi respuesta), o muchas columnas con los nombres de las columnas como nombres de los meses (como los de eumiro)?
Andy Hayden
1
La respuesta aceptada está desactualizada y también es técnicamente incorrecta, ya pd.Categoricalque no interpreta las categorías como ordenadas por defecto. Vea esta respuesta .
cs95
Respuestas:
141
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
012 March
156 Dec
234 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
012 March
234 April
156 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_indexsobre 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
012 March
134 April
256 Dec
Como se comentó, en los pandas más nuevos, Series tiene un replacemé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
kdauria
@kdauria buen lugar! ( .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á.
Andy Hayden
@AndyHayden Me he tomado la libertad de reemplazar la segunda línea con el método 'reemplazar'. Espero que esté bien.
Faheem Mitha
@AndyHayden edit rechazada, pero sigo pensando que es un cambio razonable.
Faheem Mitha
7
Solo asegúrese de usar 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';)
lluvia de ideas
17
pandas> = 1.1
Pronto podrás usar sort_valuescon keyargumento:
pd.__version__
# '1.1.0.dev0+2004.g8d10bfb6f'
custom_dict = {'March': 0, 'April': 1, 'Dec': 3}
df
a b m
012 March
156 Dec
234 April
df.sort_values(by=['m'], key=lambda x: x.map(custom_dict))
a b m
012 March
234 April
156 Dec
El keyargumento 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 .
pandas <= 1.0.X
Un método simple es usar la salida Series.mape Series.argsortindexar dfusandoDataFrame.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
012 March
234 April
156 Dec
Si necesita ordenar en orden descendente , invierta la asignación.
df.iloc[(-df['m'].map(custom_dict)).argsort()]
a b m
156 Dec
234 April
012 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
156 Dec
234 April
012 March
Hay más opciones disponibles con astype(esto está obsoleto ahora), o pd.Categorical, pero debe especificar ordered=Truepara que funcione correctamente .
Ya lo ha enfatizado, pero me gustaría reiterar en caso de que alguien más lo pase por alto y lo pierda: Pandas Categorical establece ordered=Nonepor 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).
Dave Liu
16
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:
defsort_pd(key=None,reverse=False,cmp=None):defsorter(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
012 March
234 April
156 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]:
2816101271435885189238
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!
¿Funcionaría esto para ordenar múltiples columnas / índices?
ConanG
sí, pero la respuesta seleccionada es una forma mucho mejor de hacerlo. Si tiene varios índices, simplemente organícelos de acuerdo con el orden de clasificación que prefiera y luego utilícelos df.sort_index()para ordenar todos los niveles de índice.
Michael Delgado
9
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
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).
fuente
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 dedf.sort("m")
), de lo contrario obtendrá unAttributeError: 'DataFrame' object has no attribute 'sort'
;)pandas> = 1.1
Pronto podrás usar
sort_values
conkey
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 .pandas <= 1.0.X
Un método simple es usar la salida
Series.map
eSeries.argsort
indexardf
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 utilizando
sort_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), opd.Categorical
, pero debe especificarordered=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.fuente
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 condf.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!
fuente
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
fuente