python python-2.7 sorting pandas natsort

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 .