Finden Sie die häufigste Zahl in einem Numpy-Vektor

123

Angenommen, ich habe die folgende Liste in Python:

a = [1,2,3,1,2,1,1,1,3,2,2,1]

Wie finde ich die häufigste Nummer in dieser Liste auf übersichtliche Weise?

Gerade rechtzeitig
quelle

Antworten:

192

Wenn Ihre Liste alle nicht negativen Ints enthält, sollten Sie sich numpy.bincounts ansehen:

http://docs.scipy.org/doc/numpy/reference/generated/numpy.bincount.html

und dann wahrscheinlich np.argmax verwenden:

a = np.array([1,2,3,1,2,1,1,1,3,2,2,1])
counts = np.bincount(a)
print np.argmax(counts)

Für eine kompliziertere Liste (die möglicherweise negative Zahlen oder nicht ganzzahlige Werte enthält) können Sie sie np.histogramauf ähnliche Weise verwenden. Wenn Sie nur in Python arbeiten möchten, ohne numpy zu verwenden, collections.Counterist dies eine gute Möglichkeit, mit dieser Art von Daten umzugehen .

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
quelle
58
+1. Könnte nur seinnp.bincount([1, 2, 3, 1, 2, 1, 1, 1, 3, 2, 2, 1]).argmax()
Nikolai Fetissov
1
+1. Dies ist mindestens eine Größenordnung schneller als scipy.stats.mode, wenn auch weniger allgemein.
Fred Foo
Gute Antwort! Wenn sich jedoch jemand in Python 2.6 befindet, ist collection.Counter nicht verfügbar. In diesem Fall siehe meine Antwort unten.
JJC
19
Für diejenigen von uns, die nach 2016 zu Besuch sind: Ich mag diese Antwort nicht, da bincount (arr) ein Array zurückgibt, das so groß ist wie das größte Element in arr, sodass ein kleines Array mit einem großen Bereich ein übermäßig großes Array erzeugen würde. Die Antwort von Apoengtus unten ist viel besser, obwohl ich nicht glaube, dass numpy.unique () im Jahr 2011 existierte, als diese Antwort erstellt wurde.
Wehrdo
2
Python 3 :Counter(array).most_common(1)[0][0]
Diralik
80

Sie können verwenden

(values,counts) = np.unique(a,return_counts=True)
ind=np.argmax(counts)
print values[ind]  # prints the most frequent element

Wenn ein Element so häufig ist wie ein anderes, gibt dieser Code nur das erste Element zurück.

Apogentus
quelle
4
Ich finde dies am hilfreichsten, da es generisch und kurz ist und das Abrufen von Elementen aus Werten oder Zählungen anhand eines abgeleiteten Index ermöglicht.
Ryanjdillon
2
Wenn wir mehrere häufigste Werte haben, values[counts.argmax()]wird der erste Wert zurückgegeben. Um sie alle zu bekommen, können wir verwenden values[counts == counts.max()].
W. Zhu
44

Wenn Sie bereit sind, SciPy zu verwenden :

>>> 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
quelle
30

Performances (mit iPython) für einige Lösungen finden Sie hier:

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

Am besten ist 'max' mit 'set' für kleine Arrays wie das Problem.

Laut @David Sanders ist der Algorithmus "max w / set" bei weitem der schlechteste , wenn Sie die Arraygröße auf etwa 100.000 Elemente erhöhen, während die Methode "numpy bincount" die beste ist.

iuridiniz
quelle
1
@IuliusCurt, um den besten Ansatz aufzuzeigen, den wir brauchen, um ihn gegen mehrere Fälle zu testen: kleine Arrays, große Arrays, zufällige Arrays, reale Arrays (wie Timsort zum Sortieren), ... Aber ich stimme Ihnen zu
iuridiniz
3
Die Verwendung nur eines kleinen Arrays, wie in Ihrem Ansatz, wird die verschiedenen Algorithmen nicht sehr gut unterscheiden.
David Sanders
10
Wenn Sie die Größe der Testliste auf 100000 ( a = (np.random.rand(100000) * 1000).round().astype('int'); a_list = list(a)) erhöhen , ist Ihr "max w / set" -Algorithmus bei weitem der schlechteste, während die "numpy bincount" -Methode die beste ist. Ich habe diesen Test mit a_listnativem Python-Code und anumpy-Code durchgeführt, um zu vermeiden, dass die Kosten für das Marshalling die Ergebnisse verfälschen.
David Sanders
4

Auch wenn Sie den häufigsten Wert (positiv oder negativ) erhalten möchten, ohne Module zu laden, können Sie den folgenden Code verwenden:

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
quelle
1
Dies ist von vor einiger Zeit, aber für die Nachwelt: Dies entspricht dem leichter zu lesenden max(set(lVals), key=lVals.count), bei dem für jedes eindeutige Element eine O (n) -Zählung von lValsungefähr O (n ^ 2) durchgeführt wird (unter der Annahme, dass O (n) eindeutig ist Elemente). Die Verwendung collections.Counter(lVals).most_common(1)[0][0]aus der Standardbibliothek, wie von JoshAdel vorgeschlagen , ist nur O (n).
Dougal
3

Während die meisten der obigen Antworten nützlich sind, falls Sie: 1) nicht positive ganzzahlige Werte (z. B. Gleitkommazahlen oder negative Ganzzahlen ;-)) unterstützen müssen und 2) nicht in Python 2.7 (welche Sammlungen.Counter) enthalten sind erfordert), und 3) ziehen Sie es vor, die Abhängigkeit von scipy (oder sogar numpy) nicht zu Ihrem Code hinzuzufügen, dann ist eine reine Python 2.6-Lösung, die O (nlogn) (dh effizient) ist, genau dies:

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
quelle
2

Ich mag die Lösung von JoshAdel.

Aber es gibt nur einen Haken.

Das np.bincount() Lösung funktioniert nur mit Zahlen.

Wenn Sie Zeichenfolgen haben, collections.Counterfunktioniert die Lösung für Sie.

Vikas
quelle
1

Erweitern Sie diese Methode , um den Modus der Daten zu ermitteln, in dem Sie möglicherweise den Index des tatsächlichen Arrays benötigen, um zu sehen, wie weit der Wert vom Zentrum der Verteilung entfernt ist.

(_, idx, counts) = np.unique(a, return_index=True, return_counts=True)
index = idx[np.argmax(counts)]
mode = a[index]

Denken Sie daran, den Modus zu verwerfen, wenn len (np.argmax (Anzahl))> 1 ist

Lean Bravo
quelle
1

In Python 3 sollte Folgendes funktionieren:

max(set(a), key=lambda x: a.count(x))
Yury Kliachko
quelle
1

Beginnend enthält Python 3.4die Standardbibliothek die statistics.modeFunktion, den häufigsten Datenpunkt zurückzugeben.

from statistics import mode

mode([1, 2, 3, 1, 2, 1, 1, 1, 3, 2, 2, 1])
# 1

Wenn mehrere Modi mit derselben Frequenz vorhanden sind, wird statistics.modeder erste gefundene zurückgegeben.


Beginnend Python 3.8gibt die statistics.multimodeFunktion eine Liste der am häufigsten vorkommenden Werte in der Reihenfolge zurück, in der sie zuerst angetroffen wurden:

from statistics import multimode

multimode([1, 2, 3, 1, 2])
# [1, 2]
Xavier Guihot
quelle
0

Hier ist eine allgemeine Lösung, die unabhängig von den Werten mit nur Numpy entlang einer Achse angewendet werden kann. Ich habe auch festgestellt, dass dies viel schneller ist als scipy.stats.mode, wenn es viele eindeutige Werte gibt.

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
quelle
-1

Ich mache vor kurzem ein Projekt und benutze Sammlungen. Counter. (Was mich gefoltert hat).

Der Counter in Sammlungen hat meiner Meinung nach eine sehr sehr schlechte Leistung. Es ist nur ein Klassenumbruch-Diktat ().

Was noch schlimmer ist: Wenn Sie cProfile verwenden, um die Methode zu profilieren, sollten Sie die ganze Zeit über eine Menge '__missing__' und '__instancecheck__' Dinge verschwenden.

Seien Sie vorsichtig, wenn Sie most_common () verwenden, da es jedes Mal eine Sortierung aufruft, die es extrem langsam macht. und wenn Sie most_common (x) verwenden, wird eine Heap-Sortierung aufgerufen, die ebenfalls langsam ist.

Übrigens hat der Bincount von numpy auch ein Problem: Wenn Sie np.bincount ([1,2,4000000]) verwenden, erhalten Sie ein Array mit 4000000 Elementen.

Weichu Liu
quelle
3
Ein Diktat ist die am besten abgestimmte Datenstruktur in Python und eignet sich ideal zum Zählen beliebiger Objekte. Im Gegensatz dazu funktioniert das Binning nur bei numerischen Werten und lässt Sie kein Aliasing zwischen eng beieinander liegenden diskreten Werten verhindern. Im Fall von Counter wird die Methode __missing__ nur aufgerufen, wenn ein Element zum ersten Mal angezeigt wird. Andernfalls ist die Anwesenheit kostenlos. Beachten Sie, dass die Methode most_common () in den meisten Fällen unglaublich schnell ist, da der Heap im Vergleich zum Gesamtdatensatz sehr klein ist. In den meisten Fällen führt die Methode most_common () nur geringfügig mehr Vergleiche durch als min () .
Raymond Hettinger