Con così tante soluzioni proposte, sono stupito che nessuno abbia proposto ciò che considererei ovvio (per elementi non hashable ma comparabili) - [ itertools.groupby] [1]. itertoolsoffre funzionalità veloci e riutilizzabili e consente di delegare una logica complessa a componenti di libreria standard ben collaudati. Considera ad esempio:
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]
Questo potrebbe essere scritto in modo più conciso, ovviamente, ma sto puntando alla massima chiarezza. Le due printaffermazioni possono essere decommentate per vedere meglio i macchinari in azione; ad esempio, con stampe non commentate:
print most_common(['goose', 'duck', 'duck', 'goose'])
emette:
SL: [('duck', 1), ('duck', 2), ('goose', 0), ('goose', 3)]
item 'duck', count 2, minind 1
item 'goose', count 2, minind 0
goose
Come vedi, SLè un elenco di coppie, ciascuna coppia di un elemento seguita dall'indice dell'articolo nell'elenco originale (per implementare la condizione chiave che, se gli elementi "più comuni" con lo stesso conteggio più alto sono> 1, il risultato deve essere il primo che si verifica).
groupbyraggruppa solo per l'articolo (via operator.itemgetter). La funzione ausiliaria, chiamata una volta per raggruppamento durante il maxcalcolo, riceve e disimballa internamente un gruppo: una tupla con due elementi in (item, iterable)cui gli elementi dell'iterabile sono anche tuple a due elementi, (item, original index)[[gli elementi di SL]].
Quindi la funzione ausiliaria utilizza un ciclo per determinare sia il conteggio delle voci nell'iterabile del gruppo, sia l'indice minimo originale; restituisce quelli come "chiave di qualità" combinata, con il segno dell'indice minimo modificato in modo che l' maxoperazione consideri "migliori" quegli elementi che si sono verificati in precedenza nell'elenco originale.
Questo codice potrebbe essere molto più semplice se si preoccupasse un po ' meno dei problemi della Big-O nel tempo e nello spazio, ad esempio ...:
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]
stessa idea di base, appena espressa in modo più semplice e compatto ... ma, ahimè, uno spazio ausiliario O (N) in più (per incarnare gli iterabili dei gruppi nelle liste) e il tempo O (N al quadrato) (per ottenere il valore L.indexdi ogni elemento) . Mentre l'ottimizzazione prematura è la radice di tutti i mali della programmazione, scegliere deliberatamente un approccio O (N al quadrato) quando è disponibile uno O (N log N) va semplicemente contro il grano della scalabilità! -)
Infine, per coloro che preferiscono gli "oneliners" alla chiarezza e alle prestazioni, una versione bonus da 1 linea con nomi opportunamente alterati :-).
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]