Bu kadar çok çözüm önerildiğinde, kimsenin bariz olanı düşündüğüm şeyi önermediğine şaşırdım (yıkanamaz ancak karşılaştırılabilir öğeler için) - [ itertools.groupby
] [1]. itertools
hızlı, yeniden kullanılabilir işlevsellik sunar ve bazı zor mantığı iyi test edilmiş standart kütüphane bileşenlerine devredebilmenizi sağlar. Örneğin şunu düşünün:
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]
Bu elbette daha kısaca yazılabilir, ama maksimum netliği hedefliyorum. print
Makineyi çalışırken daha iyi görmek için bu iki ifade önerilmemektedir; örneğin, ile baskı uncommented:
print most_common(['goose', 'duck', 'duck', 'goose'])
yayar:
SL: [('duck', 1), ('duck', 2), ('goose', 0), ('goose', 3)]
item 'duck', count 2, minind 1
item 'goose', count 2, minind 0
goose
Gördüğünüz gibi, SL
, çiftlerin bir listesidir, her bir öğe bir öğeyi ve ardından orijinal listede öğenin dizinini eşleştirir (aynı en yüksek sayıya sahip "en yaygın" öğelerin> 1 olması durumunda, sonucun en eski olanı ol).
groupby
yalnızca öğeye göre gruplandırır (üzerinden operator.itemgetter
). max
Hesaplama sırasında gruplama başına bir kez çağrılan yardımcı işlev, bir grubu alır ve dahili olarak açar - (item, iterable)
yinelenebilir öğelerin de iki öğeli tuples olduğu iki öğeli bir demet (item, original index)
[[öğeleri SL
]].
Daha sonra yardımcı fonksiyon, grubun yinelenebilir girişindeki giriş sayısını ve minimum orijinal dizini belirlemek için bir döngü kullanır ; bunları birleştirilmiş "kalite anahtarı" olarak döndürür, min dizini işareti değiştirilir, böylece max
işlem orijinal listede daha önce gerçekleşen öğeleri "daha iyi" olarak değerlendirir.
Bir endişe varsa bu kod çok daha basit olabilir , zaman ve mekandaki büyük O sorunları hakkında biraz daha az , örneğin ...
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]
aynı temel fikir, sadece daha basit ve kompakt bir şekilde ifade edildi ... ama, ne yazık ki, ekstra bir O (N) yardımcı alan (grupların yinelenebilirlerini listelere yerleştirmek için) ve O (N kare) süresi ( L.index
her öğeden almak için ) . Erken optimizasyon programlamadaki tüm kötülüklerin kökü olsa da, bir O (N log N) mevcut olduğunda kasıtlı olarak bir O (N kare) yaklaşımı seçmek, ölçeklenebilirlik tanesine karşı çok fazla gider! -)
Son olarak, netlik ve performans için "oneliners" i tercih edenler için, uygun şekilde karıştırılmış isimlere sahip bonus 1-liner versiyonu :-).
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]