¿Existe una función integrada para la clasificación natural de cadenas?

Resuelto Karthick Arumugam asked hace 13 años • 0 respuestas

Tengo una lista de cadenas para las que me gustaría realizar una ordenación alfabética natural .

Por ejemplo, la siguiente lista está ordenada de forma natural (lo que quiero):

['elm0', 'elm1', 'Elm2', 'elm9', 'elm10', 'Elm11', 'Elm12', 'elm13']

Y aquí está la versión "ordenada" de la lista anterior (lo que uso sorted()):

['Elm11', 'Elm12', 'Elm2', 'elm0', 'elm1', 'elm10', 'elm13', 'elm9']

Estoy buscando una función de clasificación que se comporte como la primera.

Karthick Arumugam avatar Jan 29 '11 18:01 Karthick Arumugam
Aceptado

Hay una biblioteca de terceros para esto en PyPI llamada natsort (divulgación completa, soy el autor del paquete). Para su caso, puede hacer cualquiera de las siguientes cosas:

>>> from natsort import natsorted, ns
>>> x = ['Elm11', 'Elm12', 'Elm2', 'elm0', 'elm1', 'elm10', 'elm13', 'elm9']
>>> natsorted(x, key=lambda y: y.lower())
['elm0', 'elm1', 'Elm2', 'elm9', 'elm10', 'Elm11', 'Elm12', 'elm13']
>>> natsorted(x, alg=ns.IGNORECASE)  # or alg=ns.IC
['elm0', 'elm1', 'Elm2', 'elm9', 'elm10', 'Elm11', 'Elm12', 'elm13']

Debes tener en cuenta que natsortutiliza un algoritmo general, por lo que debería funcionar para casi cualquier entrada que le introduzcas. Si desea obtener más detalles sobre por qué podría elegir una biblioteca para hacer esto en lugar de implementar su propia función, consulte la página Cómo funcionanatsort de la documentación , en particular Casos especiales en todas partes. sección.


Si necesita una clave de clasificación en lugar de una función de clasificación, utilice cualquiera de las fórmulas siguientes.

>>> from natsort import natsort_keygen, ns
>>> l1 = ['elm0', 'elm1', 'Elm2', 'elm9', 'elm10', 'Elm11', 'Elm12', 'elm13']
>>> l2 = l1[:]
>>> natsort_key1 = natsort_keygen(key=lambda y: y.lower())
>>> l1.sort(key=natsort_key1)
>>> l1
['elm0', 'elm1', 'Elm2', 'elm9', 'elm10', 'Elm11', 'Elm12', 'elm13']
>>> natsort_key2 = natsort_keygen(alg=ns.IGNORECASE)
>>> l2.sort(key=natsort_key2)
>>> l2
['elm0', 'elm1', 'Elm2', 'elm9', 'elm10', 'Elm11', 'Elm12', 'elm13']

Actualización noviembre 2020

Dado que una solicitud/pregunta popular es "¿cómo ordenar como el Explorador de Windows?" (o cualquiera que sea el navegador del sistema de archivos de su sistema operativo), a partir de natsortla versión 7.1.0 hay una función llamada os_sortedpara hacer exactamente esto. En Windows, se ordenará en el mismo orden que el Explorador de Windows, y en otros sistemas operativos debería ordenarse como el navegador del sistema de archivos local.

>>> from natsort import os_sorted
>>> os_sorted(list_of_paths)
# your paths sorted like your file system browser

Para aquellos que necesitan una clave de clasificación, pueden usar os_sort_keygen(o os_sort_keysi solo necesitan los valores predeterminados).

Advertencia : lea la documentación de la API para esta función antes de usarla para comprender las limitaciones y cómo obtener mejores resultados.

SethMMorton avatar Aug 24 '2013 05:08 SethMMorton

Prueba esto:

import re

def natural_sort(l): 
    convert = lambda text: int(text) if text.isdigit() else text.lower()
    alphanum_key = lambda key: [convert(c) for c in re.split('([0-9]+)', key)]
    return sorted(l, key=alphanum_key)

Producción:

['elm0', 'elm1', 'Elm2', 'elm9', 'elm10', 'Elm11', 'Elm12', 'elm13']

Código adaptado de aquí: Clasificación para humanos: orden de clasificación natural .

Mark Byers avatar Jan 29 '2011 12:01 Mark Byers

Aquí hay una versión mucho más pitónica de la respuesta de Mark Byer:

import re

def natural_sort_key(s, _nsre=re.compile('([0-9]+)')):
    return [int(text) if text.isdigit() else text.lower()
            for text in _nsre.split(s)]

Ahora esta función se puede usar como clave en cualquier función que la use, como list.sort, sorted, maxetc.

Como lambda:

lambda s: [int(t) if t.isdigit() else t.lower() for t in re.split('(\d+)', s)]

Código de demostración totalmente reproducible:

import re
natsort = lambda s: [int(t) if t.isdigit() else t.lower() for t in re.split('(\d+)', s)]
L = ["a1", "a10", "a11", "a2", "a22", "a3"]   
print(sorted(L, key=natsort))  
# ['a1', 'a2', 'a3', 'a10', 'a11', 'a22'] 
Claudiu avatar Apr 18 '2013 18:04 Claudiu
data = ['elm13', 'elm9', 'elm0', 'elm1', 'Elm11', 'Elm2', 'elm10']

Analicemos los datos. La capacidad de dígitos de todos los elementos es 2. Y hay 3 letras en la parte literal común 'elm'.

Entonces, la longitud máxima del elemento es 5. Podemos aumentar este valor para asegurarnos (por ejemplo, a 8).

Teniendo esto en cuenta, tenemos una solución de una sola línea:

data.sort(key=lambda x: '{0:0>8}'.format(x).lower())

¡Sin expresiones regulares ni bibliotecas externas!

print(data)

>>> ['elm0', 'elm1', 'Elm2', 'elm9', 'elm10', 'Elm11', 'elm13']

Explicación:

for elm in data:
    print('{0:0>8}'.format(elm).lower())

>>>
0000elm0
0000elm1
0000elm2
0000elm9
000elm10
000elm11
000elm13
SergO avatar Jul 15 '2015 14:07 SergO

Escribí una función basada en http://www.codinghorror.com/blog/2007/12/sorting-for-humans-natural-sort-order.html que agrega la capacidad de seguir pasando su propio parámetro 'clave'. Necesito esto para realizar una clasificación natural de listas que contienen objetos más complejos (no solo cadenas).

import re

def natural_sort(list, key=lambda s:s):
    """
    Sort the list into natural alphanumeric order.
    """
    def get_alphanum_key_func(key):
        convert = lambda text: int(text) if text.isdigit() else text 
        return lambda s: [convert(c) for c in re.split('([0-9]+)', key(s))]
    sort_key = get_alphanum_key_func(key)
    list.sort(key=sort_key)

Por ejemplo:

my_list = [{'name':'b'}, {'name':'10'}, {'name':'a'}, {'name':'1'}, {'name':'9'}]
natural_sort(my_list, key=lambda x: x['name'])
print my_list
[{'name': '1'}, {'name': '9'}, {'name': '10'}, {'name': 'a'}, {'name': 'b'}]
bburrier avatar Jan 20 '2012 10:01 bburrier