Bei so vielen vorgeschlagenen Lösungen bin ich erstaunt, dass niemand vorgeschlagen hat, was ich für offensichtlich halte (für nicht hashbare, aber vergleichbare Elemente) - [ itertools.groupby
] [1]. itertools
bietet schnelle, wiederverwendbare Funktionen und ermöglicht es Ihnen, einige knifflige Logik an bewährte Standardbibliothekskomponenten zu delegieren. Betrachten Sie zum Beispiel:
import itertools
import operator
def most_common(L):
# get an iterable of (item, iterable) pairs
SL = sorted((x, i) for i, x in enumerate(L))
# print 'SL:', SL
groups = itertools.groupby(SL, key=operator.itemgetter(0))
# auxiliary function to get "quality" for an item
def _auxfun(g):
item, iterable = g
count = 0
min_index = len(L)
for _, where in iterable:
count += 1
min_index = min(min_index, where)
# print 'item %r, count %r, minind %r' % (item, count, min_index)
return count, -min_index
# pick the highest-count/earliest item
return max(groups, key=_auxfun)[0]
Dies könnte natürlich präziser geschrieben werden, aber ich strebe nach maximaler Klarheit. Die beiden print
Aussagen können unkommentiert werden, um die Maschinerie in Aktion besser zu sehen. Zum Beispiel mit unkommentierten Abzügen:
print most_common(['goose', 'duck', 'duck', 'goose'])
emittiert:
SL: [('duck', 1), ('duck', 2), ('goose', 0), ('goose', 3)]
item 'duck', count 2, minind 1
item 'goose', count 2, minind 0
goose
Wie Sie sehen, SL
handelt es sich um eine Liste von Paaren, wobei jedes Paar ein Element gefolgt vom Index des Elements in der ursprünglichen Liste ist (um die Schlüsselbedingung zu implementieren, dass das Ergebnis sein muss, wenn die "häufigsten" Elemente mit derselben höchsten Anzahl> 1 sind am frühesten auftreten).
groupby
Gruppen nur nach Artikel (via operator.itemgetter
). Die Hilfsfunktion, die während der max
Berechnung einmal pro Gruppierung aufgerufen wird , empfängt und entpackt intern eine Gruppe - ein Tupel mit zwei Elementen, (item, iterable)
wobei die Elemente des Iterables auch Tupel mit zwei Elementen sind, (item, original index)
[[die Elemente von SL
]].
Dann verwendet die Hilfsfunktion eine Schleife, um sowohl die Anzahl der Einträge in der iterierbaren Gruppe als auch den minimalen ursprünglichen Index zu bestimmen . Diese werden als kombinierter "Qualitätsschlüssel" zurückgegeben, wobei das Vorzeichen des Min-Index geändert wird, sodass die max
Operation die Elemente berücksichtigt, die zuvor in der ursprünglichen Liste aufgetreten sind.
Dieser Code könnte viel einfacher sein, wenn er sich ein wenig weniger Gedanken über Big-O-Probleme in Zeit und Raum macht, z. B.::
def most_common(L):
groups = itertools.groupby(sorted(L))
def _auxfun((item, iterable)):
return len(list(iterable)), -L.index(item)
return max(groups, key=_auxfun)[0]
Dieselbe Grundidee, nur einfacher und kompakter ausgedrückt ... aber leider ein zusätzlicher O (N) -Hilfsraum (um die Iterablen der Gruppen in Listen zu verkörpern) und O (N-Quadrat) -Zeit (um die L.index
von jedem Element zu erhalten) . Während vorzeitige Optimierung die Wurzel allen Übels in der Programmierung ist, widerspricht die bewusste Auswahl eines O (N-Quadrat) -Ansatzes, wenn ein O (N log N) verfügbar ist, einfach zu sehr der Skalierbarkeit! -)
Für diejenigen, die "Oneliners" gegenüber Klarheit und Leistung bevorzugen, eine Bonus-1-Liner-Version mit entsprechend verstümmelten Namen :-).
from itertools import groupby as g
def most_common_oneliner(L):
return max(g(sorted(L)), key=lambda(x, v):(len(list(v)),-L.index(x)))[0]