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.histogramin 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_listper il codice nativo Python e aper 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 lValsapprossimativamente 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.modefunzione 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.moderestituisce la prima incontrata.
A partire da Python 3.8, la statistics.multimodefunzione 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()