Załóżmy, że mam następującą listę w Pythonie:
a = [1,2,3,1,2,1,1,1,3,2,2,1]
Jak w zgrabny sposób znaleźć najczęstszy numer na tej liście?
Załóżmy, że mam następującą listę w Pythonie:
a = [1,2,3,1,2,1,1,1,3,2,2,1]
Jak w zgrabny sposób znaleźć najczęstszy numer na tej liście?
Odpowiedzi:
Jeśli twoja lista zawiera wszystkie nieujemne liczby całkowite, powinieneś przyjrzeć się numpy.bincounts:
http://docs.scipy.org/doc/numpy/reference/generated/numpy.bincount.html
a potem prawdopodobnie użyj np.argmax:
a = np.array([1,2,3,1,2,1,1,1,3,2,2,1])
counts = np.bincount(a)
print(np.argmax(counts))
W przypadku bardziej skomplikowanej listy (która może zawierać liczby ujemne lub niecałkowite), możesz użyć np.histogram
w podobny sposób. Alternatywnie, jeśli chcesz po prostu pracować w Pythonie bez używania numpy, collections.Counter
jest to dobry sposób na obsługę tego rodzaju danych.
from collections import Counter
a = [1,2,3,1,2,1,1,1,3,2,2,1]
b = Counter(a)
print(b.most_common(1))
scipy.stats.mode
, chociaż mniej ogólne.
Counter(array).most_common(1)[0][0]
Możesz użyć
(values,counts) = np.unique(a,return_counts=True)
ind=np.argmax(counts)
print values[ind] # prints the most frequent element
Jeśli jakiś element występuje tak samo często jak inny, ten kod zwróci tylko pierwszy element.
values[counts.argmax()]
zwróci pierwszą wartość. Aby uzyskać je wszystkie, możemy użyć values[counts == counts.max()]
.
>>> # small array
>>> a = [12,3,65,33,12,3,123,888000]
>>>
>>> import collections
>>> collections.Counter(a).most_common()[0][0]
3
>>> %timeit collections.Counter(a).most_common()[0][0]
100000 loops, best of 3: 11.3 µs per loop
>>>
>>> import numpy
>>> numpy.bincount(a).argmax()
3
>>> %timeit numpy.bincount(a).argmax()
100 loops, best of 3: 2.84 ms per loop
>>>
>>> import scipy.stats
>>> scipy.stats.mode(a)[0][0]
3.0
>>> %timeit scipy.stats.mode(a)[0][0]
10000 loops, best of 3: 172 µs per loop
>>>
>>> from collections import defaultdict
>>> def jjc(l):
... d = defaultdict(int)
... for i in a:
... d[i] += 1
... return sorted(d.iteritems(), key=lambda x: x[1], reverse=True)[0]
...
>>> jjc(a)[0]
3
>>> %timeit jjc(a)[0]
100000 loops, best of 3: 5.58 µs per loop
>>>
>>> max(map(lambda val: (a.count(val), val), set(a)))[1]
12
>>> %timeit max(map(lambda val: (a.count(val), val), set(a)))[1]
100000 loops, best of 3: 4.11 µs per loop
>>>
Najlepsze jest „max” z „set” dla małych tablic, takich jak problem.
Według @Davida Sandersa, jeśli zwiększysz rozmiar tablicy do około 100 000 elementów, algorytm „max w / set ” okazuje się zdecydowanie najgorszy, podczas gdy metoda „numpy bincount” jest najlepsza.
a = (np.random.rand(100000) * 1000).round().astype('int'); a_list = list(a)
), twój algorytm „max w / set” okaże się zdecydowanie najgorszy, podczas gdy metoda „numpy bincount” jest najlepsza. Przeprowadziłem ten test, używając a_list
natywnego kodu Pythona i a
kodu numpy, aby uniknąć kosztów krosowania, które zepsułyby wyniki.
Jeśli chcesz uzyskać najczęstszą wartość (dodatnią lub ujemną) bez ładowania jakichkolwiek modułów, możesz użyć następującego kodu:
lVals = [1,2,3,1,2,1,1,1,3,2,2,1]
print max(map(lambda val: (lVals.count(val), val), set(lVals)))
max(set(lVals), key=lVals.count)
, które oblicza O (n) dla każdego unikalnego elementu o lVals
około O (n ^ 2) (zakładając O (n) unikalne elementy). Korzystanie collections.Counter(lVals).most_common(1)[0][0]
z biblioteki standardowej, zgodnie z sugestią JoshAdela , to tylko O (n).
Chociaż większość powyższych odpowiedzi jest przydatna, w przypadku, gdy: 1) potrzebujesz jej do obsługi liczb całkowitych innych niż dodatnie (np. Zmiennoprzecinkowe lub ujemne liczby całkowite ;-)) i 2) nie są w Pythonie 2.7 (które kolekcje. wymaga) i 3) wolę nie dodawać zależności scipy (lub nawet numpy) do swojego kodu, to rozwiązanie czysto Python 2.6, które jest O (nlogn) (tj. wydajne), jest takie:
from collections import defaultdict
a = [1,2,3,1,2,1,1,1,3,2,2,1]
d = defaultdict(int)
for i in a:
d[i] += 1
most_frequent = sorted(d.iteritems(), key=lambda x: x[1], reverse=True)[0]
Rozwinięcie tej metody , zastosowane do znalezienia trybu danych, w którym może być potrzebny indeks rzeczywistej tablicy, aby zobaczyć, jak daleko wartość znajduje się od środka rozkładu.
(_, idx, counts) = np.unique(a, return_index=True, return_counts=True)
index = idx[np.argmax(counts)]
mode = a[index]
Pamiętaj, aby odrzucić tryb, gdy len (np.argmax (counts))> 1
W Pythonie 3 powinno działać:
max(set(a), key=lambda x: a.count(x))
Począwszy od programu Python 3.4
, biblioteka standardowa zawiera statistics.mode
funkcję zwracającą pojedynczy najczęściej używany punkt danych.
from statistics import mode
mode([1, 2, 3, 1, 2, 1, 1, 1, 3, 2, 2, 1])
# 1
Jeśli istnieje wiele trybów o tej samej częstotliwości, statistics.mode
zwraca pierwszy napotkany.
Począwszy od Python 3.8
, statistics.multimode
funkcja zwraca listę najczęściej występujących wartości w kolejności, w jakiej zostały napotkane po raz pierwszy:
from statistics import multimode
multimode([1, 2, 3, 1, 2])
# [1, 2]
Oto ogólne rozwiązanie, które można zastosować wzdłuż osi, niezależnie od wartości, używając czysto numpy. Odkryłem również, że jest to znacznie szybsze niż tryb scipy.stats.mode, jeśli istnieje wiele unikalnych wartości.
import numpy
def mode(ndarray, axis=0):
# Check inputs
ndarray = numpy.asarray(ndarray)
ndim = ndarray.ndim
if ndarray.size == 1:
return (ndarray[0], 1)
elif ndarray.size == 0:
raise Exception('Cannot compute mode on empty array')
try:
axis = range(ndarray.ndim)[axis]
except:
raise Exception('Axis "{}" incompatible with the {}-dimension array'.format(axis, ndim))
# If array is 1-D and numpy version is > 1.9 numpy.unique will suffice
if all([ndim == 1,
int(numpy.__version__.split('.')[0]) >= 1,
int(numpy.__version__.split('.')[1]) >= 9]):
modals, counts = numpy.unique(ndarray, return_counts=True)
index = numpy.argmax(counts)
return modals[index], counts[index]
# Sort array
sort = numpy.sort(ndarray, axis=axis)
# Create array to transpose along the axis and get padding shape
transpose = numpy.roll(numpy.arange(ndim)[::-1], axis)
shape = list(sort.shape)
shape[axis] = 1
# Create a boolean array along strides of unique values
strides = numpy.concatenate([numpy.zeros(shape=shape, dtype='bool'),
numpy.diff(sort, axis=axis) == 0,
numpy.zeros(shape=shape, dtype='bool')],
axis=axis).transpose(transpose).ravel()
# Count the stride lengths
counts = numpy.cumsum(strides)
counts[~strides] = numpy.concatenate([[0], numpy.diff(counts[~strides])])
counts[strides] = 0
# Get shape of padded counts and slice to return to the original shape
shape = numpy.array(sort.shape)
shape[axis] += 1
shape = shape[transpose]
slices = [slice(None)] * ndim
slices[axis] = slice(1, None)
# Reshape and compute final counts
counts = counts.reshape(shape).transpose(transpose)[slices] + 1
# Find maximum counts and return modals/counts
slices = [slice(None, i) for i in sort.shape]
del slices[axis]
index = numpy.ogrid[slices]
index.insert(axis, numpy.argmax(counts, axis=axis))
return sort[index], counts[index]
Ostatnio robię projekt i używam kolekcji Counter (co mnie torturowało).
Liczniki w kolekcjach mają moim zdaniem bardzo, bardzo złe działanie. To tylko dykt zawijania klas ().
Co gorsza, jeśli użyjesz cProfile do profilowania swojej metody, powinieneś zobaczyć wiele rzeczy „__missing__” i „__instancecheck__” marnujących się przez cały czas.
Uważaj, używając jej most_common (), ponieważ za każdym razem wywoływałaby sortowanie, które czyniło ją wyjątkowo powolną. a jeśli użyjesz most_common (x), wywoła sortowanie sterty, które również jest powolne.
Btw, numpy's bincount również ma problem: jeśli używasz np.bincount ([1,2,4000000]), otrzymasz tablicę z 4000000 elementami.
np.bincount([1, 2, 3, 1, 2, 1, 1, 1, 3, 2, 2, 1]).argmax()