Jaki jest skuteczny sposób na znalezienie najbardziej powszechnego elementu na liście w Pythonie?
Moje elementy listy mogą nie podlegać haszowaniu, więc nie można używać słownika. Również w przypadku losowań należy zwrócić pozycję o najniższym indeksie. Przykład:
>>> most_common(['duck', 'duck', 'goose'])
'duck'
>>> most_common(['goose', 'duck', 'duck', 'goose'])
'goose'
Odpowiedzi:
Przy tak wielu proponowanych rozwiązaniach jestem zdumiony, że nikt nie zaproponował tego, co uważam za oczywiste (dla elementów niekasowalnych, ale porównywalnych) - [
itertools.groupby
] [1].itertools
oferuje szybką funkcjonalność wielokrotnego użytku i pozwala delegować skomplikowaną logikę do dobrze przetestowanych komponentów bibliotek standardowych. Rozważmy na przykład:Można to oczywiście napisać bardziej zwięźle, ale dążę do maksymalnej jasności. Te dwa
print
stwierdzenia można pominąć, aby lepiej zobaczyć maszynę w akcji; na przykład z wydrukami bez komentarzy:emituje:
Jak widać,
SL
jest to lista par, każda para to element, po którym następuje indeks elementu na pierwotnej liście (aby zaimplementować kluczowy warunek, że jeśli „najczęściej” elementy o tej samej największej liczbie są> 1, wynik musi być najwcześniej występującym).groupby
grupuje tylko według elementu (przezoperator.itemgetter
). Funkcja pomocnicza, wywoływana raz na grupę podczasmax
obliczania, odbiera i wewnętrznie rozpakowuje grupę - krotkę z dwoma elementami,(item, iterable)
gdzie elementy iterowalne są również krotkami dwuelementowymi,(item, original index)
[[elementySL
]].Następnie funkcja pomocnicza używa pętli do określenia zarówno liczby wpisów w iterowalnej grupie, jak i minimalnego pierwotnego indeksu; zwraca je jako połączony „klucz jakości”, ze zmienionym znakiem indeksu min, więc
max
operacja uzna za „lepsze” te elementy, które wystąpiły wcześniej na pierwotnej liście.Ten kod mógłby być znacznie prostszy, gdyby martwił się trochę mniej problemami z dużymi O w czasie i przestrzeni, np ...:
ta sama podstawowa idea, po prostu wyrażona prościej i zwięźle ... ale, niestety, dodatkowa przestrzeń pomocnicza O (N) (aby ująć iterowalne grupy do list) i O (N do kwadratu) czas (aby uzyskać
L.index
z każdej pozycji) . Podczas gdy przedwczesna optymalizacja jest źródłem wszelkiego zła w programowaniu, celowe wybieranie podejścia O (N do kwadratu), gdy dostępne jest O (N log N), jest po prostu zbyt duże wbrew ziarnu skalowalności! -)Wreszcie dla tych, którzy wolą „oneliner” od przejrzystości i wydajności, bonusowa wersja 1-liniowa z odpowiednio zniekształconymi nazwami :-).
źródło
groupby
wymaga najpierw sortowania (O (NlogN)); użycieCounter()
withmost_common()
może to pokonać, ponieważ używa heapq do znalezienia elementu o najwyższej częstotliwości (tylko dla 1 elementu to czas O (N)). PonieważCounter()
obecnie jest mocno zoptymalizowany (liczenie odbywa się w pętli C), może z łatwością pokonać to rozwiązanie nawet w przypadku małych list. Wydmuchuje go z wody w przypadku dużych list.Prostszy, jednoliniowy:
źródło
set(lst)
, cała lista musi być ponownie sprawdzona)… Prawdopodobnie wystarczająco szybkie dla większości zastosowań, chociaż…set(lst)
zlst
i będzie pracować z elementami non-hashable też; aczkolwiek wolniej.list.count()
musi przechodzić listy w całości , i to zrobić dla każdej unikalnej pozycji na liście. To sprawia, że jest to rozwiązanie O (NK) (w najgorszym przypadku O (N ^ 2)). Użycie aCounter()
zajmuje tylko O (N) czasu!Pożyczając stąd , można tego użyć z Pythonem 2.7:
Działa około 4-6 razy szybciej niż rozwiązania Alexa i jest 50 razy szybszy niż jednolinijkowy proponowany przez newacct.
Aby pobrać element, który występuje jako pierwszy na liście w przypadku remisów:
źródło
most_common
jest sortowane według liczby, a nie nieuporządkowane. To powiedziawszy, nie wybierze pierwszego elementu w przypadku remisów; Dodałem inny sposób korzystania z licznika, który wybiera pierwszy element.To, czego chcesz, jest znane w statystykach jako tryb, a Python ma oczywiście wbudowaną funkcję, która robi dokładnie to za Ciebie:
Zwróć uwagę, że jeśli nie ma „najbardziej powszechnego elementu”, takiego jak przypadki, w których dwie pierwsze są równe , to wzrośnie
StatisticsError
, ponieważ statystycznie rzecz biorąc, w tym przypadku nie ma trybu .źródło
set
i jest prawdopodobnaO(n^3)
.Jeśli nie można ich haszować, można je posortować i wykonać pojedynczą pętlę po wyniku zliczając elementy (identyczne elementy będą obok siebie). Ale może być szybsze uczynienie ich haszowalnymi i użycie dyktowania.
źródło
Counter()
rozwiązaniem AlexaTo jest rozwiązanie O (n).
(odwrócony jest używany, aby upewnić się, że zwraca najniższą pozycję indeksu)
źródło
Bez wymogu dotyczącego najniższego indeksu możesz użyć
collections.Counter
do tego:źródło
Sortuj kopię listy i znajdź najdłuższy bieg. Możesz ozdobić listę przed posortowaniem jej indeksem każdego elementu, a następnie wybrać przebieg, który w przypadku remisu zaczyna się od najniższego indeksu.
źródło
Jednowierszowy:
źródło
źródło
Proste rozwiązanie w jednej linii
Zwróci najczęściej występujący element z jego częstotliwością.
źródło
Prawdopodobnie już tego nie potrzebujesz, ale to właśnie zrobiłem dla podobnego problemu. (Wygląda na dłuższą niż jest z powodu komentarzy.)
źródło
Opierając się na odpowiedzi Luiza , ale spełniając warunek „ w przypadku remisów pozycja o najniższym indeksie powinna zostać zwrócona ”:
Przykład:
źródło
Tutaj:
Mam niejasne wrażenie, że gdzieś w standardowej bibliotece jest metoda, która poda liczbę każdego elementu, ale nie mogę jej znaleźć.
źródło
Jest to oczywiste powolne rozwiązanie (O (n ^ 2)), jeśli ani sortowanie, ani haszowanie nie są możliwe, ale
==
dostępne jest porównanie równości ( ):Jednak umożliwienie hashowania lub sortowania elementów (zgodnie z zaleceniami innych odpowiedzi) prawie zawsze przyspieszyłoby znalezienie najczęściej używanego elementu, jeśli długość listy (n) jest duża. O (n) średnio z haszowaniem, a O (n * log (n)) w najgorszym przypadku do sortowania.
źródło
źródło
Musiałem to zrobić w ostatnim programie. Przyznaję, nie mogłem zrozumieć odpowiedzi Alexa, więc na tym skończyłem.
Zmierzyłem czas z rozwiązaniem Alexa i jest około 10-15% szybszy w przypadku krótkich list, ale gdy przejdziesz ponad 100 elementów lub więcej (przetestowano do 200000), jest około 20% wolniej.
źródło
Cześć, to bardzo proste rozwiązanie z dużym O (n)
Gdzie numerować element na liście, który powtarza się przez większość czasu
źródło
źródło
źródło
źródło