Supponiamo di avere il seguente elenco in Python:
a = [1,2,3,1,2,1,1,1,3,2,2,1]
Come trovare il numero più frequente in questo elenco in modo ordinato?
Supponiamo di avere il seguente elenco in Python:
a = [1,2,3,1,2,1,1,1,3,2,2,1]
Come trovare il numero più frequente in questo elenco in modo ordinato?
Risposte:
Se la tua lista contiene tutti gli int non negativi, dovresti dare un'occhiata a numpy.bincounts:
http://docs.scipy.org/doc/numpy/reference/generated/numpy.bincount.html
e quindi probabilmente usa 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))
Per un elenco più complicato (che forse contiene numeri negativi o valori non interi), puoi usarlo np.histogram
in modo simile. In alternativa, se vuoi solo lavorare in Python senza usare numpy, collections.Counter
è un buon modo per gestire questo tipo di dati.
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
, sebbene meno generale.
Counter(array).most_common(1)[0][0]
Puoi usare
(values,counts) = np.unique(a,return_counts=True)
ind=np.argmax(counts)
print values[ind] # prints the most frequent element
Se un elemento è frequente come un altro, questo codice restituirà solo il primo elemento.
values[counts.argmax()]
restituirà il primo valore. Per ottenerli tutti, possiamo usare 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
>>>
La cosa migliore è 'max' con 'set' per piccoli array come il problema.
Secondo @David Sanders, se aumenti la dimensione dell'array a qualcosa come 100.000 elementi, l'algoritmo "max w / set" finisce per essere di gran lunga il peggiore mentre il metodo "numpy bincount" è il migliore.
a = (np.random.rand(100000) * 1000).round().astype('int'); a_list = list(a)
), il tuo algoritmo "max w / set" finisce per essere di gran lunga il peggiore mentre il metodo "numpy bincount" è il migliore. Ho condotto questo test utilizzando a_list
per il codice nativo Python e a
per il codice numpy per evitare che i costi di marshalling rovinino i risultati.
Inoltre se vuoi ottenere il valore più frequente (positivo o negativo) senza caricare alcun modulo puoi usare il seguente codice:
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)
, che conta un O (n) per ogni elemento unico di lVals
approssimativamente O (n ^ 2) (assumendo O (n) unico elementi). L'utilizzo collections.Counter(lVals).most_common(1)[0][0]
dalla libreria standard, come suggerito da JoshAdel , è solo O (n).
Sebbene la maggior parte delle risposte sopra siano utili, nel caso in cui: 1) ne hai bisogno per supportare valori interi non positivi (ad esempio float o numeri interi negativi ;-)), e 2) non sono su Python 2.7 (quali raccolte. richiede) e 3) preferisci non aggiungere la dipendenza di scipy (o anche numpy) al tuo codice, quindi una soluzione puramente python 2.6 che è O (nlogn) (cioè efficiente) è proprio questa:
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]
Espandendo questo metodo , applicato alla ricerca della modalità dei dati in cui potrebbe essere necessario l'indice dell'array effettivo per vedere quanto è lontano il valore dal centro della distribuzione.
(_, idx, counts) = np.unique(a, return_index=True, return_counts=True)
index = idx[np.argmax(counts)]
mode = a[index]
Ricordarsi di eliminare la modalità quando len (np.argmax (counts))> 1
In Python 3 dovrebbe funzionare quanto segue:
max(set(a), key=lambda x: a.count(x))
A partire da Python 3.4
, la libreria standard include la statistics.mode
funzione per restituire il singolo punto dati più comune.
from statistics import mode
mode([1, 2, 3, 1, 2, 1, 1, 1, 3, 2, 2, 1])
# 1
Se sono presenti più modalità con la stessa frequenza, statistics.mode
restituisce la prima incontrata.
A partire da Python 3.8
, la statistics.multimode
funzione restituisce un elenco dei valori ricorrenti nell'ordine in cui sono stati rilevati per la prima volta:
from statistics import multimode
multimode([1, 2, 3, 1, 2])
# [1, 2]
Ecco una soluzione generale che può essere applicata lungo un asse, indipendentemente dai valori, utilizzando esclusivamente numpy. Ho anche scoperto che è molto più veloce di scipy.stats.mode se ci sono molti valori univoci.
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]
Recentemente sto facendo un progetto e usando le collezioni.Counter. (Il che mi ha torturato).
Il contatore nelle collezioni ha una pessima prestazione secondo me. È solo un dict di wrapping di classe ().
Quel che è peggio, se usi cProfile per profilare il suo metodo, dovresti vedere molte cose '__missing__' e '__instancecheck__' che sprecano tutto il tempo.
Fai attenzione a usare most_common (), perché ogni volta invoca un ordinamento che lo rende estremamente lento. e se usi most_common (x), richiamerà un ordinamento di heap, anch'esso lento.
A proposito, anche il bincount di numpy ha un problema: se usi np.bincount ([1,2,4000000]), otterrai un array con 4000000 elementi.
np.bincount([1, 2, 3, 1, 2, 1, 1, 1, 3, 2, 2, 1]).argmax()