Znajdź najczęstszą liczbę w wektorze numpy

123

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?

JustInTime
źródło

Odpowiedzi:

193

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.histogramw podobny sposób. Alternatywnie, jeśli chcesz po prostu pracować w Pythonie bez używania numpy, collections.Counterjest 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))
JoshAdel
źródło
58
+1. Może być po prostunp.bincount([1, 2, 3, 1, 2, 1, 1, 1, 3, 2, 2, 1]).argmax()
Nikolai Fetissov
1
+1. Jest to co najmniej o rząd wielkości szybsze niż scipy.stats.mode, chociaż mniej ogólne.
Fred Foo
Niezła odpowiedź! Jeśli jednak ktoś korzysta z Pythona 2.6, collections.Counter nie jest dostępny. W takim razie zobacz moją odpowiedź poniżej.
JJC
19
Do tych z nas, którzy odwiedzają nas po 2016 roku: nie podoba mi się ta odpowiedź, ponieważ bincount (arr) zwraca tablicę tak dużą, jak największy element w arr, więc mała tablica z dużym zakresem utworzyłaby zbyt dużą tablicę. Poniższa odpowiedź Apoengtusa jest znacznie lepsza, chociaż nie sądzę, aby numpy.unique () istniało w 2011 roku, kiedy ta odpowiedź została utworzona.
Wehrdo
2
Python 3 :Counter(array).most_common(1)[0][0]
diralik
80

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.

Apogentus
źródło
4
Uważam, że jest to najbardziej pomocne, ponieważ jest ogólne, krótkie i umożliwia wyciąganie elementów z wartości lub liczników przez jakiś wyprowadzony indeks.
ryanjdillon,
2
Jeśli mamy wiele najczęstszych wartości, values[counts.argmax()]zwróci pierwszą wartość. Aby uzyskać je wszystkie, możemy użyć values[counts == counts.max()].
W. Zhu
44

Jeśli chcesz używać SciPy :

>>> from scipy.stats import mode
>>> mode([1,2,3,1,2,1,1,1,3,2,2,1])
(array([ 1.]), array([ 6.]))
>>> most_frequent = mode([1,2,3,1,2,1,1,1,3,2,2,1])[0][0]
>>> most_frequent
1.0
Fred Foo
źródło
30

Wydajność (przy użyciu iPythona) dla niektórych rozwiązań znalezionych tutaj:

>>> # 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.

iuridiniz
źródło
1
@IuliusCurt, aby wskazać najlepsze podejście musimy przetestować go przed wielu przypadkach: małych, dużych tablic tablic, tablice, tablice losowych świata rzeczywistego (jak timsort robi dla sortowania) ... Ale zgadzam się z tobą
iuridiniz
3
Używanie tylko małej tablicy, jak w twoim podejściu, nie pozwoli dobrze rozróżnić różnych algorytmów.
David Sanders
10
Jeśli zwiększysz rozmiar listy testowej do 100000 ( 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_listnatywnego kodu Pythona i akodu numpy, aby uniknąć kosztów krosowania, które zepsułyby wyniki.
David Sanders
4

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)))
Artsiom Rudzenka
źródło
1
To jest od jakiegoś czasu, ale dla potomnych: jest to równoważne z łatwiejszym do odczytania max(set(lVals), key=lVals.count), które oblicza O (n) dla każdego unikalnego elementu o lValsokoł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).
Dougal
3

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]
JJC
źródło
2

Podoba mi się rozwiązanie JoshAdela.

Ale jest tylko jeden haczyk.

np.bincount()Rozwiązanie działa tylko na liczbach.

Jeśli masz sznurki, collections.Counterrozwiązanie będzie działać dla Ciebie.

Vikas
źródło
1

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

Lean Bravo
źródło
1

W Pythonie 3 powinno działać:

max(set(a), key=lambda x: a.count(x))
Yury Kliachko
źródło
1

Począwszy od programu Python 3.4, biblioteka standardowa zawiera statistics.modefunkcję 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.modezwraca pierwszy napotkany.


Począwszy od Python 3.8, statistics.multimodefunkcja 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]
Xavier Guihot
źródło
0

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]
Devin Cairns
źródło
-1

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.

Weichu Liu
źródło
3
Dykt jest najlepiej dostrojoną strukturą danych w Pythonie i jest idealny do liczenia dowolnych obiektów. W przeciwieństwie do tego, kategoryzowanie działa tylko na wartościach liczbowych i nie pozwala uniknąć aliasingu między blisko rozmieszczonymi wartościami dyskretnymi. W przypadku Counter metoda __missing__ jest wywoływana tylko wtedy, gdy element jest widziany po raz pierwszy; w przeciwnym razie jego obecność jest bezpłatna. Zauważ, że metoda most_common () jest w większości przypadków niesamowicie szybka, ponieważ sterta jest bardzo mała w porównaniu z całkowitym zbiorem danych. W większości przypadków metoda most_common () wykonuje tylko trochę więcej porównań niż min () .
Raymond Hettinger