python - Ordenando naturalmente Pandas DataFrame
python-2.7 sorting (2)
Tengo un DataFrame de pandas con índices que quiero ordenar de forma natural. Natsort no parece funcionar. Ordenar los índices antes de construir el DataFrame no parece ayudar porque las manipulaciones que hago al DataFrame parecen estropear la clasificación en el proceso. ¿Alguna idea sobre cómo puedo recurrir a los índices de forma natural?
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)
La
respuesta aceptada responde a
la pregunta que se hace.
También me gustaría agregar cómo usar
natsort
en columnas en un
DataFrame
, ya que esa será la siguiente pregunta.
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
Como muestra la respuesta aceptada , ordenar por el índice es bastante sencillo:
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
Si desea ordenar en una columna de la misma manera, debe ordenar el índice por el orden en que se reordenó la columna deseada.
natsort
proporciona las funciones de conveniencia
index_natsorted
y
order_by_index
para hacer precisamente eso.
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
Si desea reordenar por un número arbitrario de columnas (o una columna y el índice), puede usar
zip
(o
itertools.izip
en Python2) para especificar la ordenación en varias columnas.
La primera columna dada será la columna de clasificación primaria, luego la secundaria, luego la terciaria, etc.
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
Aquí hay un método alternativo usando objetos
Categorical
que los desarrolladores de
pandas
me han dicho que es la forma "adecuada" de hacer esto.
Esto requiere (por lo que puedo ver) pandas> = 0.16.0.
Actualmente, solo funciona en columnas, pero aparentemente en pandas> = 0.17.0 agregarán
CategoricalIndex
que permitirá que este método se use en un índice.
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
El objeto
Categorical
permite definir un orden de clasificación para que
DataFrame
el
DataFrame
.
Los elementos dados al llamar a
reorder_categories
deben ser únicos, de ahí la llamada a
set
para la columna "b".
Dejo que el usuario decida si esto es mejor que el método
reindex
o no, ya que requiere que ordene los datos de la columna de forma independiente antes de
DataFrame
dentro del
DataFrame
(aunque imagino que el segundo ordenamiento es bastante eficiente).
natsort
completa, soy el autor
natsort
.
Si desea ordenar el df, simplemente ordene el índice o los datos y asígnelos directamente al índice del df en lugar de intentar pasar el df como un argumento, ya que eso genera una lista vacía:
In [7]:
df.index = natsorted(a)
df.index
Out[7]:
Index([''0hr'', ''48hr'', ''72hr'', ''96hr'', ''128hr''], dtype=''object'')
Tenga en cuenta que
df.index = natsorted(df.index)
también funciona
si pasa el df como un argumento, produce una lista vacía, en este caso porque el df está vacío (no tiene columnas), de lo contrario, devolverá las columnas ordenadas, que no es lo que desea:
In [10]:
natsorted(df)
Out[10]:
[]
EDITAR
Si desea ordenar el índice para que los datos se reordenen junto con el índice, use
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
Tenga en cuenta que debe asignar el resultado de
reindex
a un nuevo df o a sí mismo, no acepta el
inplace
.