Uyuşmuş bir vektörde en sık görülen sayıyı bulun


123

Python'da aşağıdaki listeye sahip olduğumu varsayalım:

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

Bu listedeki en sık sayıları düzgün bir şekilde nasıl bulabilirim?

Yanıtlar:


193

Listeniz tüm negatif olmayan girişleri içeriyorsa, numpy.bincounts'a bir göz atmalısınız:

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

ve sonra muhtemelen np.argmax kullanın:

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

Daha karmaşık bir liste için (negatif sayılar veya tam sayı olmayan değerler içeren) np.histogrambenzer bir şekilde kullanabilirsiniz. Alternatif olarak, numpy kullanmadan sadece python'da çalışmak istiyorsanız, collections.Counterbu tür verileri işlemenin iyi bir yoludur.

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

58
+1. Sadece olabilirnp.bincount([1, 2, 3, 1, 2, 1, 1, 1, 3, 2, 2, 1]).argmax()
Nikolai Fetissov

1
+1. Bu, daha scipy.stats.modeaz genel olmasına rağmen , en azından daha büyük bir mertebedir .
Fred Foo

Güzel cevap! Ancak, birisi python 2.6 kullanıyorsa, collections.Counter kullanılamaz. Bu durumda, aşağıdaki cevabıma bakın.
JJC

19
2016'dan sonra ziyaret edenlere: Bincount (arr), arr'daki en büyük eleman kadar büyük bir dizi döndürdüğünden, bu nedenle geniş bir aralığa sahip küçük bir dizi aşırı büyük bir dizi oluşturacağından, bu yanıtı beğenmedim. Apoengtus'un aşağıdaki cevabı çok daha iyi, ancak numpy.unique () 'in bu cevabın oluşturulduğu 2011'de var olduğunu düşünmüyorum.
Wehrdo

2
Python 3 :Counter(array).most_common(1)[0][0]
diralik

80

Kullanabilirsin

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

Bazı elemanlar bir başkası kadar sıksa, bu kod sadece ilk elemanı döndürecektir.


4
Bunu en yararlı buluyorum çünkü genel, kısa ve türetilmiş bir dizine göre değerlerden veya sayılardan öğe çekilmesine izin veriyor.
ryanjdillon

2
Birden fazla en sık değerimiz varsa, values[counts.argmax()]ilk değeri döndürür. Hepsini almak için kullanabiliriz values[counts == counts.max()].
W. Zhu

44

SciPy'yi kullanmak istiyorsanız :

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

30

Burada bulunan bazı çözümler için performanslar (iPython kullanarak):

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

En iyisi, problem gibi küçük diziler için 'set' ile 'max' dir .

@David Sanders'a göre, dizi boyutunu 100.000 öğe gibi bir şeye yükseltirseniz, "maks w / set" algoritması en kötüsü olurken "numpy bincount" yöntemi en iyisidir.


1
@IuliusCurt, birden fazla duruma karşı test etmemiz gereken en iyi yaklaşımı göstermek için: küçük diziler, büyük diziler, rastgele diziler, gerçek dünya dizileri ( timsort'un sıralama için yaptığı gibi ), ... Ama sana katılıyorum
iuridiniz

3
Yaklaşımınızda olduğu gibi sadece küçük bir dizi kullanmak, farklı algoritmalar arasında çok iyi bir ayrım yapmayacaktır.
David Sanders

10
Test listesi boyutunu 100000 ( a = (np.random.rand(100000) * 1000).round().astype('int'); a_list = list(a)) değerine yükseltirseniz, "maks. / Set" algoritmanız en kötüsü olurken "numpy bincount" yöntemi en iyisidir. Bu testi a_listyerel python kodunu kullanarak ve asıralama maliyetlerinin sonuçları altüst etmesini önlemek için numpy kodu kullanarak yaptım .
David Sanders

4

Ayrıca, herhangi bir modül yüklemeden en sık değeri (pozitif veya negatif) almak istiyorsanız, aşağıdaki kodu kullanabilirsiniz:

lVals = [1,2,3,1,2,1,1,1,3,2,2,1]
print max(map(lambda val: (lVals.count(val), val), set(lVals)))

1
Bu, bir süre öncesine ait, ancak gelecek nesil için: Bu, okunması daha kolay olana eşdeğerdir; bu, max(set(lVals), key=lVals.count)her benzersiz öğe lValsiçin yaklaşık O (n ^ 2) için bir O (n) sayar (O (n) benzersiz olduğu varsayılırsa) elementler). JoshAdel'in önerdiğicollections.Counter(lVals).most_common(1)[0][0] gibi standart kitaplıktan kullanmak sadece O (n) 'dir.
Dougal

3

Yukarıdaki cevapların çoğu yararlı olsa da: 1) pozitif olmayan tamsayı değerleri (örneğin, kayan sayılar veya negatif tamsayılar ;-)) desteklemesi gerekiyorsa ve 2) Python 2.7'de (hangi koleksiyonlar. gerektirir) ve 3) kodunuza scipy bağımlılığını (veya hatta numpy) eklememeyi tercih ederseniz, O (nlogn) (yani verimli) olan tamamen bir python 2.6 çözümü tam olarak şudur:

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]

2

JoshAdel'in çözümünü beğendim.

Ancak tek bir yakalama var.

np.bincount()Çözüm sadece sayılar üzerinde çalışır.

Dizeleriniz varsa, collections.Counterçözüm sizin için çalışacaktır.


1

Bu yöntemin genişletilmesi , değerin dağılımın merkezinden ne kadar uzakta olduğunu görmek için gerçek dizinin dizinine ihtiyaç duyabileceğiniz veri modunu bulmaya uygulanır.

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

Len (np.argmax (sayım))> 1 olduğunda modu atmayı unutmayın


1

Python 3'te aşağıdakiler çalışmalıdır:

max(set(a), key=lambda x: a.count(x))

1

Başlangıç ​​olarak Python 3.4, standart kitaplık statistics.modeen yaygın tek veri noktasını döndürme işlevini içerir .

from statistics import mode

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

Aynı frekansa sahip birden fazla mod varsa statistics.modekarşılaşılan ilkini döndürür.


Başlarken Python 3.8, statistics.multimodeişlev en sık karşılaşılan değerlerin bir listesini ilk karşılaştıkları sırayla döndürür:

from statistics import multimode

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

0

İşte, değerlerden bağımsız olarak, tamamen numpy kullanılarak bir eksen boyunca uygulanabilecek genel bir çözüm. Ayrıca, çok sayıda benzersiz değer varsa, bunun scipy.stats.mode'dan çok daha hızlı olduğunu buldum.

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]

-1

Son zamanlarda bir proje yapıyorum ve collections.Counter kullanıyorum. (Bana işkence eden).

Koleksiyonlardaki sayaç bence çok kötü bir performans sergiliyor. Bu sadece bir sınıf kaplama diktesi ().

Daha da kötüsü, yönteminin profilini çıkarmak için cProfile kullanırsanız, tüm zamanınızı boşa harcayan birçok "__missing__" ve "__instancecheck__" görmelisiniz.

Most_common () işlevini kullanırken dikkatli olun, çünkü her seferinde onu aşırı derecede yavaşlatan bir sıralama çağırır. ve most_common (x) kullanırsanız, aynı zamanda yavaş olan bir yığın sıralaması başlatacaktır.

Btw, numpy'nin bincount'unda da bir sorun var: np.bincount ([1,2,4000000]) kullanırsanız, 4000000 elemanlı bir dizi elde edersiniz.


3
Bir dikt, Python'daki en ince ayarlanmış veri yapısıdır ve rastgele nesneleri saymak için idealdir. Bunun tersine, gruplama yalnızca sayısal değerler üzerinde çalışır ve yakın aralıklı ayrık değerler arasında örtüşmeyi önlemenize izin vermez. Counter durumunda, __missing__ yöntemi yalnızca bir öğe ilk görüldüğünde çağrılır; aksi takdirde varlığı maliyetsizdir. Not most_common () yığın toplam veri kümesi ile karşılaştırıldığında çok küçük olduğu için bir yöntem hızlı çoğu durumda blazingly olup. Çoğu durumda, most_common () metodu çok az daha fazla karşılaştırmalar yapar min () .
Raymond Hettinger
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.