Usando o Python 3.x, tenho uma lista de cadeias de caracteres para as quais gostaria de executar uma classificação alfabética natural.
Tipo natural: a ordem pela qual os arquivos no Windows são classificados.
Por exemplo, a lista a seguir é classificada naturalmente (o que eu quero):
['elm0', 'elm1', 'Elm2', 'elm9', 'elm10', 'Elm11', 'Elm12', 'elm13']
E aqui está a versão "classificada" da lista acima (o que eu tenho):
['Elm11', 'Elm12', 'Elm2', 'elm0', 'elm1', 'elm10', 'elm13', 'elm9']
Estou procurando uma função de classificação que se comporte como a primeira.
python
sorting
python-3.x
snakile
fonte
fonte
!1, 1, !a, a
. A única maneira de classificar como o Windows parece ser usar aStrCmpLogicalW
própria função do Windows , pois ninguém parece ter reimplementado essa função corretamente (a fonte seria apreciada). Solução: stackoverflow.com/a/48030307/2441026Respostas:
Existe uma biblioteca de terceiros para isso no PyPI chamada natsort (divulgação completa, eu sou o autor do pacote). Para o seu caso, você pode fazer o seguinte:
Você deve observar que
natsort
usa um algoritmo geral, portanto ele deve funcionar para praticamente qualquer entrada que você lançar nele. Se você quiser mais detalhes sobre por que você pode escolher uma biblioteca para fazer isso em vez de rolar a sua própria função, consulte anatsort
documentação é Como Funciona página, em particular os casos especiais em toda parte! seção.Se você precisar de uma chave de classificação em vez de uma função de classificação, use uma das fórmulas abaixo.
fonte
natsort
também 'naturalmente' lida com o caso de vários números separados nas strings. Coisas boas!Tente o seguinte:
Resultado:
Código adaptado daqui: Classificação para seres humanos: ordem de classificação natural .
fonte
return sorted(l, key)
vez del.sort(key)
? É para qualquer ganho de desempenho ou apenas para ser mais pitônico?re.split('([0-9]+)', '0foo')
retorna['', '0', 'foo']
. Por isso, as strings sempre estarão em índices pares e números inteiros em índices ímpares na matriz.Aqui está uma versão muito mais pitônica da resposta de Mark Byer:
Agora, esta função pode ser usada como uma chave em qualquer função que usa-lo, como
list.sort
,sorted
,max
, etc.Como um lambda:
fonte
Eu escrevi uma função baseada em http://www.codinghorror.com/blog/2007/12/sorting-for-humans-natural-sort-order.html que adiciona a capacidade de ainda passar seu próprio parâmetro 'key'. Eu preciso disso para executar um tipo natural de listas que contêm objetos mais complexos (não apenas seqüências de caracteres).
Por exemplo:
fonte
natural_sort_key
e, em seguida, ao classificar uma lista você poderia fazer cadeia de suas chaves, por exemplo:list.sort(key=lambda el: natural_sort_key(el['name']))
Vamos analisar os dados. A capacidade de dígitos de todos os elementos é 2. E há 3 letras na parte literal comum
'elm'
.Portanto, o comprimento máximo do elemento é 5. Podemos aumentar esse valor para garantir (por exemplo, para 8).
Tendo isso em mente, temos uma solução de uma linha:
sem expressões regulares e bibliotecas externas!
Explicação:
fonte
width = max(data, key=len)
calcular o que deseja submarino para os8
'{0:0>{width}}'.format(x, width=width)
Dado:
Semelhante à solução da SergO, um liner sem bibliotecas externas seria :
ou
Explicação:
Esta solução usa os principais recursos de classificação para definir uma função que será empregada para a classificação. Como sabemos que toda entrada de dados é precedida por 'elm', a função de classificação converte em número inteiro a parte da sequência após o terceiro caractere (ou seja, int (x [3:])). Se a parte numérica dos dados estiver em um local diferente, essa parte da função precisará ser alterada.
Felicidades
fonte
Existem muitas implementações por aí e, embora algumas tenham chegado perto, nenhuma capturou a elegância que o python moderno oferece.
Cuidado ao usar
from os.path import split
Inspiração de
fonte
Valor deste post
Meu objetivo é oferecer uma solução não regex que possa ser aplicada em geral.
Vou criar três funções:
find_first_digit
que peguei emprestado da @AnuragUniyal . Ele encontrará a posição do primeiro dígito ou não dígito em uma sequência.split_digits
que é um gerador que separa uma string em pedaços de dígitos e não dígitos. Tambémyield
números inteiros quando é um dígito.natural_key
apenas envolvesplit_digits
em umtuple
. Isso é o que usamos como uma chave parasorted
,max
,min
.Funções
Podemos ver que é geral, pois podemos ter blocos de vários dígitos:
Ou deixe como diferencia maiúsculas de minúsculas:
Podemos ver que ele classifica a lista do OP na ordem apropriada
Mas ele também pode lidar com listas mais complicadas:
Meu equivalente regex seria
fonte
Uma opção é transformar a string em uma tupla e substituir dígitos usando o formulário expandido http://wiki.answers.com/Q/What_does_expanded_form_mean
assim a90 se tornaria ("a", 90,0) e a1 se tornaria ("a", 1)
abaixo está um código de exemplo (que não é muito eficiente devido à maneira como remove os zeros iniciais dos números)
resultado:
fonte
('b', 1) < ('b', 'e', 't', 'a', 1, '.', 1)
retornaráTypeError: unorderable types: int() < str()
natsort
, pypi.org/project/natsortCom base nas respostas aqui, escrevi uma
natural_sorted
função que se comporta como a função internasorted
:O código-fonte também está disponível no repositório de trechos do GitHub: https://github.com/bdrung/snippets/blob/master/natural_sorted.py
fonte
As respostas acima são boas para o exemplo específico que foi mostrado, mas faltam vários casos úteis para a questão mais geral de natureza natural. Acabei de entender um pouco desses casos, então criei uma solução mais completa:
O código de teste e vários links (dentro e fora do StackOverflow) estão aqui: http://productarchitect.com/code/better-natural-sort.py
Feedback bem-vindo. Isso não pretende ser uma solução definitiva; apenas um passo à frente.
fonte
natsorted
ehumansorted
falha porque foram usadas incorretamente ... você tentou passarnatsorted
como uma chave, mas na verdade é a própria função de classificação. Você deveria ter tentadonatsort_keygen()
.Muito provavelmente
functools.cmp_to_key()
está intimamente ligado à implementação subjacente da classificação do python. Além disso, o parâmetro cmp é legado. A maneira moderna é transformar os itens de entrada em objetos que suportam as operações de comparação avançada desejadas.No CPython 2.x, objetos de tipos diferentes podem ser pedidos, mesmo que os respectivos operadores de comparação avançada não tenham sido implementados. No CPython 3.x, objetos de diferentes tipos devem oferecer suporte explícito à comparação. Veja Como o Python compara string e int? que liga à documentação oficial . A maioria das respostas depende dessa ordem implícita. Mudar para o Python 3.x exigirá um novo tipo para implementar e unificar comparações entre números e seqüências de caracteres.
Existem três abordagens diferentes. O primeiro usa classes aninhadas para tirar proveito do
Iterable
algoritmo de comparação do Python . O segundo desenrola esse aninhamento em uma única classe. O terceiro antecede a subclassestr
para focar no desempenho. Todos são cronometrados; o segundo é duas vezes mais rápido, enquanto o terceiro quase seis vezes mais rápido. A subclassificaçãostr
não é necessária e, provavelmente, foi uma má ideia, mas vem com certas conveniências.Os caracteres de classificação são duplicados para forçar a ordenação por caso e trocados entre maiúsculas e minúsculas para forçar a letra minúscula a classificar primeiro; esta é a definição típica de "classificação natural". Não pude decidir sobre o tipo de agrupamento; alguns podem preferir o seguinte, o que também traz benefícios significativos de desempenho:
Onde utilizados, os operadores de comparação são configurados para
object
não serem ignoradosfunctools.total_ordering
.A classificação natural é bastante complicada e vagamente definida como um problema. Não se esqueça de executar com
unicodedata.normalize(...)
antecedência e considere o usostr.casefold()
e nãostr.lower()
. Provavelmente existem problemas sutis de codificação que não considerei. Então, eu recomendo a biblioteca natsort . Dei uma rápida olhada no repositório do github; a manutenção do código foi estelar.Todos os algoritmos que eu vi dependem de truques, como duplicar e diminuir caracteres e trocar de maiúsculas e minúsculas. Embora isso duplique o tempo de execução, uma alternativa exigiria uma ordem natural total no conjunto de caracteres de entrada. Eu não acho que isso faça parte da especificação unicode e, como existem muitos mais dígitos unicode do que
[0-9]
, criar essa classificação seria igualmente assustador. Se você deseja comparações com reconhecimento de localidade, prepare suas seqüências delocale.strxfrm
acordo com o HOWTO de Classificação do Python .fonte
Deixe-me enviar minha opinião sobre essa necessidade:
Agora, se tivermos a lista como esta:
Podemos simplesmente usar o
key=
kwarg para fazer um tipo natural:A desvantagem aqui é, obviamente, como é agora, a função classificará letras maiúsculas antes de letras minúsculas.
Vou deixar a implementação de uma garoupa que não diferencia maiúsculas de minúsculas para o leitor :-)
fonte
Sugiro que você simplesmente use o
key
argumento de palavra - chave desorted
para obter a lista desejada.Por exemplo:
fonte
a_51
seria depoisa500
, embora 500> 51Após a resposta do @Mark Byers, aqui está uma adaptação que aceita o
key
parâmetro e é mais compatível com PEP8.Eu também fiz um Gist
fonte
key
parâmetro? Mas isso também é exemplificado na resposta de @ beauburrierUma melhoria na melhoria de Claudiu na resposta de Mark Byer ;-)
BTW, talvez nem todos se lembrem de que os padrões dos argumentos das funções são avaliados no
def
momentofonte
Agradecimentos :
Bubble Sort Homework
Como ler uma string de uma letra por vez em python
fonte
fonte