Korzystając z języka Python 3.x, mam listę ciągów znaków, dla których chciałbym wykonać naturalne sortowanie alfabetyczne.
Naturalne sortowanie: kolejność sortowania plików w systemie Windows.
Na przykład poniższa lista jest naturalnie posortowana (co chcę):
['elm0', 'elm1', 'Elm2', 'elm9', 'elm10', 'Elm11', 'Elm12', 'elm13']
A oto „posortowana” wersja powyższej listy (co mam):
['Elm11', 'Elm12', 'Elm2', 'elm0', 'elm1', 'elm10', 'elm13', 'elm9']
Szukam funkcji sortowania, która zachowuje się jak pierwsza.
python
sorting
python-3.x
snakile
źródło
źródło
!1, 1, !a, a
. Jedynym sposobem na uzyskanie sortowania takiego jak Windows wydaje się być użycieStrCmpLogicalW
samej funkcji Windows , ponieważ wydaje się, że nikt nie zaimplementował tej funkcji poprawnie (źródło byłoby mile widziane). Rozwiązanie: stackoverflow.com/a/48030307/2441026Odpowiedzi:
W PyPI dostępna jest biblioteka innej firmy o nazwie natsort (pełne ujawnienie, jestem autorem pakietu). W twoim przypadku możesz wykonać jedną z następujących czynności:
Należy zauważyć, że
natsort
używa on ogólnego algorytmu, więc powinien on działać dla niemal wszystkich wprowadzanych do niego danych wejściowych. Jeśli chcesz uzyskać więcej informacji na temat tego, dlaczego możesz wybrać bibliotekę, aby to zrobić, a nie uruchamiać własną funkcję, sprawdź stronęnatsort
dokumentacyjną Jak to działa , w szczególności specjalne przypadki wszędzie! Sekcja.Jeśli potrzebujesz klucza sortowania zamiast funkcji sortowania, użyj jednej z poniższych formuł.
źródło
natsort
również „naturalnie” obsługuje przypadek wielu oddzielnych liczb w ciągach. Świetne rzeczy!Spróbuj tego:
Wynik:
Kod zaadaptowany stąd: Sortowanie dla ludzi: Naturalny porządek sortowania .
źródło
return sorted(l, key)
zamiastl.sort(key)
? Czy ma to na celu zwiększenie wydajności, czy po prostu bycie bardziej pytonicznym?re.split('([0-9]+)', '0foo')
zwraca['', '0', 'foo']
. Z tego powodu ciągi zawsze będą znajdować się na indeksach parzystych i liczbach całkowitych na indeksach nieparzystych w tablicy.Oto znacznie bardziej pytoniczna wersja odpowiedzi Marka Byera:
Teraz ta funkcja może być używana jako klucz w każdej funkcji, która go używa, jak
list.sort
,sorted
,max
, itd.Jako lambda:
źródło
Napisałem funkcję opartą na http://www.codinghorror.com/blog/2007/12/sorting-for-humans-natural-sort-order.html, która dodaje możliwość przekazywania własnego parametru „klucza”. Potrzebuję tego, aby wykonać naturalny rodzaj list, które zawierają bardziej złożone obiekty (nie tylko ciągi znaków).
Na przykład:
źródło
natural_sort_key
, a następnie podczas sortowania listy można łańcuchowo połączyć klucze, np .:list.sort(key=lambda el: natural_sort_key(el['name']))
Przeanalizujmy dane. Pojemność cyfrowa wszystkich elementów wynosi 2. I są 3 litery we wspólnej części dosłownej
'elm'
.Zatem maksymalna długość elementu wynosi 5. Możemy zwiększyć tę wartość, aby się upewnić (na przykład do 8).
Mając to na uwadze, mamy jedno-liniowe rozwiązanie:
bez wyrażeń regularnych i bibliotek zewnętrznych!
Wyjaśnienie:
źródło
width = max(data, key=len)
do obliczenia tego, co wpisać w8
powyższe, a następnie'{0:0>{width}}'.format(x, width=width)
Dany:
Podobnie do rozwiązania SergO, 1-liniowy bez zewnętrznych bibliotek to :
lub
Wyjaśnienie:
W tym rozwiązaniu kluczową funkcją sortowania jest zdefiniowanie funkcji, która zostanie zastosowana do sortowania. Ponieważ wiemy, że każde wprowadzanie danych poprzedza „wiąz”, funkcja sortująca konwertuje na liczbę całkowitą część ciągu po 3. znaku (tj. Int (x [3:])). Jeśli część liczbowa danych znajduje się w innym miejscu, wówczas ta część funkcji musiałaby się zmienić.
Twoje zdrowie
źródło
Istnieje wiele implementacji i chociaż niektóre się zbliżyły, żadne nie oddało elegancji, jaką zapewnia współczesny python.
Ostrożnie podczas korzystania
from os.path import split
Inspiracja z
źródło
Wartość tego postu
Chodzi mi o to, aby zaoferować rozwiązanie niebędące wyrażeniami regularnymi, które można zastosować ogólnie.
Stworzę trzy funkcje:
find_first_digit
które pożyczyłem od @AnuragUniyal . Znajduje pozycję pierwszej cyfry lub cyfry w ciągu.split_digits
który jest generatorem, który rozdziela ciąg na części cyfrowe i niecyfrowe. Będzie takżeyield
liczbą całkowitą, gdy będzie cyfrą.natural_key
po prostu owijasplit_digits
się wtuple
. To, co możemy użyć jako kluczasorted
,max
,min
.Funkcje
Widzimy, że ogólnie rzecz biorąc, możemy mieć wielocyfrowe fragmenty:
Lub pozostaw jako wielkość liter:
Widzimy, że sortuje listę PO w odpowiedniej kolejności
Ale może obsługiwać również bardziej skomplikowane listy:
Mój odpowiednik wyrażenia regularnego to
źródło
Jedną z opcji jest przekształcenie łańcucha w krotkę i zastąpienie cyfr za pomocą rozszerzonej formy http://wiki.answers.com/Q/What_does_expanded_form_mean
w ten sposób a90 stałoby się („a”, 90,0), a a1 stałoby się („a”, 1)
poniżej znajduje się przykładowy kod (który nie jest zbyt wydajny ze względu na sposób, w jaki usuwa wiodące zera z liczb)
wynik:
źródło
('b', 1) < ('b', 'e', 't', 'a', 1, '.', 1)
powróciTypeError: unorderable types: int() < str()
natsort
, pypi.org/project/natsortNa podstawie odpowiedzi tutaj napisałem
natural_sorted
funkcję, która zachowuje się jak funkcja wbudowanasorted
:Kod źródłowy jest również dostępny w moim repozytorium fragmentów GitHub: https://github.com/bdrung/snippets/blob/master/natural_sorted.py
źródło
Powyższe odpowiedzi są dobre dla konkretnego przykładu, który został pokazany, ale brakuje kilku przydatnych przypadków bardziej ogólnego pytania rodzaju naturalnego. Właśnie dostałem kawałek jednego z tych przypadków, więc stworzyłem dokładniejsze rozwiązanie:
Kod testowy i kilka linków (włączanie i wyłączanie StackOverflow) znajduje się tutaj: http://productarchitect.com/code/better-natural-sort.py
Witamy mile widziane. To nie ma być ostateczne rozwiązanie; tylko krok do przodu.
źródło
natsorted
alehumansorted
nie powiodło się, ponieważ zostały użyte niepoprawnie ... próbowałeś przekazaćnatsorted
jako klucz, ale tak naprawdę sama funkcja sortowania. Powinieneś był spróbowaćnatsort_keygen()
.Najprawdopodobniej
functools.cmp_to_key()
jest ściśle związany z podstawową implementacją sortowania Pythona. Poza tym parametr cmp jest starszy. Współczesny sposób polega na przekształceniu elementów wejściowych w obiekty, które obsługują pożądane operacje porównywania bogatego.W CPython 2.x obiekty o różnych typach mogą być zamawiane, nawet jeśli odpowiednie operatory porównania bogatego nie zostały zaimplementowane. W CPython 3.x obiekty różnych typów muszą jawnie obsługiwać porównanie. Zobacz Jak Python porównuje ciąg znaków i int? który prowadzi do oficjalnej dokumentacji . Większość odpowiedzi zależy od tego dorozumianego uporządkowania. Przejście na Python 3.x będzie wymagało nowego typu do implementacji i ujednolicenia porównań między liczbami i łańcuchami.
Istnieją trzy różne podejścia. Pierwszy wykorzystuje zagnieżdżone klasy, aby skorzystać z
Iterable
algorytmu porównawczego Pythona . Drugi rozwija to zagnieżdżenie w jedną klasę. Trzecia rezygnuje z podklas,str
aby skupić się na wydajności. Wszystkie są na czas; drugi jest dwa razy szybszy, a trzeci prawie sześć razy szybszy. Podklasowaniestr
nie jest wymagane i prawdopodobnie było złym pomysłem, ale ma pewne udogodnienia.Znaki sortowania są duplikowane, aby wymusić sortowanie według wielkości liter, i zamieniane wielkością liter, aby wymusić sortowanie najpierw małych liter; jest to typowa definicja „rodzaju naturalnego”. Nie mogłem zdecydować o rodzaju grupowania; niektórzy mogą preferować następujące, co również przynosi znaczące korzyści w zakresie wydajności:
Jeśli są używane, operatory porównania są ustawione na wartość,
object
więc nie będą ignorowane przezfunctools.total_ordering
.Naturalne sortowanie jest zarówno dość skomplikowane, jak i niejasno zdefiniowane jako problem. Nie zapomnij uruchomić
unicodedata.normalize(...)
wcześniej, i rozważyć użyciestr.casefold()
zamiaststr.lower()
. Prawdopodobnie istnieją subtelne problemy z kodowaniem, których nie rozważałem. Dlatego wstępnie polecam bibliotekę natsort . Rzuciłem okiem na repozytorium github; utrzymanie kodu było gwiezdne.Wszystkie algorytmy, które widziałem, zależą od sztuczek, takich jak powielanie i obniżanie znaków oraz zamiana wielkości liter. Chociaż podwaja to czas działania, alternatywa wymagałaby całkowitego naturalnego uporządkowania zestawu znaków wejściowych. Nie sądzę, że jest to część specyfikacji Unicode, a ponieważ jest o wiele więcej cyfr Unicode
[0-9]
, tworzenie takiego sortowania byłoby równie zniechęcające. Jeśli chcesz porównań zależnych od ustawień regionalnych, przygotuj ciągi znaków, korzystając zlocale.strxfrm
instrukcji sortowania w Pythonie .źródło
Pozwól mi przedstawić własne podejście do tej potrzeby:
Teraz, jeśli mamy taką listę:
Możemy po prostu użyć
key=
kwarga, aby zrobić naturalny sposób:Wadą tego rozwiązania jest oczywiście, podobnie jak teraz, funkcja sortująca wielkie litery przed małymi.
Wdrożenie czytelnika bez znaczenia dla sprawy pozostawię czytelnikowi :-)
źródło
Sugeruję po prostu użyć
key
argumentu słowa kluczowego wsorted
celu uzyskania pożądanej listyNa przykład:
źródło
a_51
byłoby późnieja500
, chociaż 500> 51Po odpowiedzi @ Mark Byers, oto adaptacja, która akceptuje
key
parametr i jest bardziej zgodna z PEP8.Zrobiłem też Gist
źródło
key
parametr? Ale jest to również zilustrowane w odpowiedzi @ beauburrierPoprawa poprawy Claudiu w odpowiedzi Marka Byera ;-)
BTW, może nie wszyscy pamiętają, że wartości domyślne argumentów funkcji są oceniane w
def
czasieźródło
Podziękowania :
Bubble Sort Homework
Jak czytać ciąg po jednej literze w pythonie
źródło
źródło