Temukan angka yang paling sering dalam vektor numpy

123

Misalkan saya memiliki daftar berikut di python:

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

Bagaimana menemukan nomor paling sering dalam daftar ini dengan cara yang rapi?

Tepat waktu
sumber

Jawaban:

193

Jika daftar Anda berisi semua int non-negatif, Anda harus melihat di numpy.bincounts:

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

dan mungkin menggunakan 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))

Untuk daftar yang lebih rumit (yang mungkin berisi angka negatif atau nilai non-integer), Anda dapat menggunakan np.histogramdengan cara yang serupa. Alternatifnya, jika Anda hanya ingin bekerja dengan python tanpa menggunakan numpy, collections.Counteradalah cara yang baik untuk menangani data semacam ini.

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
sumber
58
+1. Bisa jadi hanyanp.bincount([1, 2, 3, 1, 2, 1, 1, 1, 3, 2, 2, 1]).argmax()
Nikolai Fetissov
1
+1. Ini setidaknya urutan besarnya lebih cepat daripada scipy.stats.mode, meskipun kurang umum.
Fred Foo
Jawaban bagus! Namun, jika seseorang menggunakan python 2.6, collections.Counter tidak tersedia. Kalau begitu, lihat jawaban saya di bawah.
JJC
19
Bagi kita yang mengunjungi setelah 2016: Saya tidak suka jawaban ini, karena bincount (arr) mengembalikan array sebesar elemen terbesar dalam arr, jadi array kecil dengan jangkauan besar akan membuat array yang terlalu besar. Jawaban Apoengtus di bawah ini jauh lebih baik, meskipun menurut saya numpy.unique () tidak ada pada tahun 2011, ketika jawaban ini dibuat.
Wehrdo
2
Python 3 :Counter(array).most_common(1)[0][0]
diralik
80

Anda dapat menggunakan

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

Jika beberapa elemen sama seringnya dengan yang lain, kode ini hanya akan mengembalikan elemen pertama.

Apogentus
sumber
4
Saya menemukan ini yang paling membantu karena generik, pendek dan memungkinkan menarik elemen dari nilai atau hitungan dengan beberapa indeks turunan.
ryanjdillon
2
Jika kita memiliki beberapa nilai yang paling sering, values[counts.argmax()]akan mengembalikan nilai pertama. Untuk mendapatkan semuanya, kita bisa menggunakan values[counts == counts.max()].
W. Zhu
44

Jika Anda ingin menggunakan 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
sumber
30

Pertunjukan (menggunakan iPython) untuk beberapa solusi yang ditemukan di sini:

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

Yang terbaik adalah 'max' dengan 'set' untuk array kecil seperti masalahnya.

Menurut @David Sanders, jika Anda meningkatkan ukuran array hingga 100.000 elemen, algoritme "max w / set" akan menjadi yang terburuk sejauh ini sedangkan metode "numpy bincount" adalah yang terbaik.

iuridiniz.dll
sumber
1
@IuliusCurt untuk menunjukkan pendekatan terbaik, kami perlu mengujinya terhadap beberapa kasus: array kecil, array besar, array acak, array dunia nyata (seperti timsort untuk menyortir), ... Tapi saya setuju dengan Anda
iuridiniz
3
Menggunakan hanya larik kecil, seperti dalam pendekatan Anda, tidak akan membedakan dengan baik antara algoritme yang berbeda.
David Sanders
10
Jika Anda meningkatkan ukuran daftar pengujian menjadi 100000 ( a = (np.random.rand(100000) * 1000).round().astype('int'); a_list = list(a)), algoritme "max w / set" Anda akan menjadi yang terburuk sejauh ini sedangkan metode "numpy bincount" adalah yang terbaik. Saya melakukan tes ini menggunakan a_listkode python asli dan auntuk kode numpy untuk menghindari biaya penyusunan yang mengacaukan hasil.
David Sanders
4

Juga jika Anda ingin mendapatkan nilai paling sering (positif atau negatif) tanpa memuat modul apa pun, Anda dapat menggunakan kode berikut:

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
sumber
1
Ini dari beberapa waktu yang lalu, tetapi untuk anak cucu: ini setara dengan yang lebih mudah dibaca max(set(lVals), key=lVals.count), yang menghitung O (n) untuk setiap elemen unik lValsuntuk sekitar O (n ^ 2) (dengan asumsi O (n) unik elemen). Menggunakan collections.Counter(lVals).most_common(1)[0][0]dari pustaka standar, seperti yang disarankan oleh JoshAdel , hanya O (n).
Dougal
3

Meskipun sebagian besar jawaban di atas berguna, jika Anda: 1) membutuhkannya untuk mendukung nilai non-positif-integer (misalnya float atau integer negatif ;-)), dan 2) tidak ada di Python 2.7 (yang koleksi. memerlukan), dan 3) memilih untuk tidak menambahkan ketergantungan scipy (atau bahkan numpy) ke kode Anda, maka solusi murni python 2.6 yaitu O (nlogn) (yaitu, efisien) hanya ini:

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

Saya suka solusi dari JoshAdel.

Tapi hanya ada satu tangkapan.

The np.bincount()solusi hanya bekerja pada nomor.

Jika Anda memiliki string, collections.Countersolusi akan bekerja untuk Anda.

Vikas
sumber
1

Memperluas metode ini , diterapkan untuk menemukan mode data di mana Anda mungkin memerlukan indeks array sebenarnya untuk melihat seberapa jauh nilai tersebut dari pusat distribusi.

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

Ingatlah untuk membuang mode ketika len (np.argmax (hitungan))> 1

Lean Bravo
sumber
1

Di Python 3, berikut ini seharusnya berfungsi:

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

Dimulai Python 3.4, pustaka standar menyertakan statistics.modefungsi untuk mengembalikan satu titik data paling umum.

from statistics import mode

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

Jika ada beberapa mode dengan frekuensi yang sama, statistics.modemengembalikan mode yang pertama kali ditemukan.


Dimulai Python 3.8, statistics.multimodefungsi mengembalikan daftar nilai yang paling sering muncul dalam urutan saat pertama kali ditemukan:

from statistics import multimode

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

Berikut adalah solusi umum yang dapat diterapkan di sepanjang sumbu, berapa pun nilainya, menggunakan numpy murni. Saya juga menemukan bahwa ini jauh lebih cepat daripada scipy.stats.mode jika ada banyak nilai unik.

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

Saya baru-baru ini melakukan proyek dan menggunakan collections.Counter (Yang menyiksa saya).

Counter dalam koleksi memiliki performa yang sangat sangat buruk menurut saya. Ini hanya diktik pembungkus kelas ().

Yang lebih buruk, Jika Anda menggunakan cProfile untuk membuat profil metodenya, Anda akan melihat banyak hal '__missing__' dan '__instancecheck__' yang terbuang percuma.

Hati-hati menggunakan most_common (), karena setiap kali itu akan memanggil semacam yang membuatnya sangat lambat. dan jika Anda menggunakan most_common (x), ini akan memanggil jenis heap, yang juga lambat.

Btw, numpy bincount juga bermasalah: jika Anda menggunakan np.bincount ([1,2,4000000]), Anda akan mendapatkan array dengan 4000000 elemen.

Weichu Liu
sumber
3
A dict adalah struktur data yang paling sempurna di Python dan ideal untuk menghitung objek arbitrer. Sebaliknya, binning hanya berfungsi pada nilai numerik dan tidak memungkinkan Anda mencegah aliasing di antara nilai diskrit yang berjarak dekat. Dalam kasus Counter, metode __missing__ hanya dipanggil ketika sebuah elemen pertama kali terlihat; jika tidak, kehadirannya bebas biaya. Perhatikan, metode most_common () sangat cepat dalam banyak kasus karena heap sangat kecil dibandingkan dengan total kumpulan data. Dalam kebanyakan kasus, metode most_common () hanya membuat sedikit lebih banyak perbandingan daripada min () .
Raymond Hettinger