Tìm số thường xuyên nhất trong một vectơ numpy


123

Giả sử tôi có danh sách sau trong python:

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

Làm thế nào để tìm số thường xuyên nhất trong danh sách này một cách gọn gàng?

Câu trả lời:


193

Nếu danh sách của bạn chứa tất cả các int không âm, bạn nên xem qua numpy.bincounts:

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

và sau đó có thể sử dụng 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))

Đối với danh sách phức tạp hơn (có thể chứa số âm hoặc giá trị không phải số nguyên), bạn có thể sử dụng np.histogramtheo cách tương tự. Ngoài ra, nếu bạn chỉ muốn làm việc trong python mà không sử dụng numpy, collections.Counterlà một cách tốt để xử lý loại dữ liệu này.

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. Có thể chỉ lànp.bincount([1, 2, 3, 1, 2, 1, 1, 1, 3, 2, 2, 1]).argmax()
Nikolai Fetissov

1
+1. Đây ít nhất là một thứ tự cường độ nhanh hơn scipy.stats.mode, mặc dù ít tổng quát hơn.
Fred Foo

Câu trả lời hay! Tuy nhiên, nếu ai đó đang sử dụng python 2.6, thì bộ sưu tập.Counter không khả dụng. Trong trường hợp đó, hãy xem câu trả lời của tôi bên dưới.
JJC

19
Đối với những người trong chúng ta ghé thăm sau năm 2016: Tôi không thích câu trả lời này, vì bincount (arr) trả về một mảng lớn bằng phần tử lớn nhất trong arr, vì vậy một mảng nhỏ với một phạm vi lớn sẽ tạo ra một mảng quá lớn. Câu trả lời của Apoengtus dưới đây tốt hơn nhiều, mặc dù tôi không nghĩ rằng numpy.unique () tồn tại vào năm 2011, khi câu trả lời này được tạo ra.
Wehrdo

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

80

Bạn có thể sử dụng

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

Nếu một số phần tử thường xuyên như một phần tử khác, mã này sẽ chỉ trả về phần tử đầu tiên.


4
Tôi thấy điều này hữu ích nhất vì nó chung chung, ngắn gọn và cho phép kéo các phần tử từ các giá trị hoặc số lượng theo một số chỉ mục bắt nguồn.
ryanjdillon

2
Nếu chúng ta có nhiều giá trị thường xuyên nhất, values[counts.argmax()]sẽ trả về giá trị đầu tiên. Để có được tất cả chúng, chúng ta có thể sử dụng values[counts == counts.max()].
W. Zhu

44

Nếu bạn sẵn sàng sử dụng 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

30

Biểu diễn (sử dụng iPython) cho một số giải pháp được tìm thấy tại đây:

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

Tốt nhất là 'max' với 'set' cho các mảng nhỏ như vấn đề.

Theo @David Sanders, nếu bạn tăng kích thước mảng lên khoảng 100.000 phần tử, thuật toán "max w / set" sẽ là tệ nhất cho đến nay, trong khi phương pháp "numpy bincount" là tốt nhất.


1
@IuliusCurt để chỉ phương pháp tốt nhất chúng ta cần phải kiểm tra nó chống lại nhiều trường hợp: mảng nhỏ, mảng lớn, mảng ngẫu nhiên, mảng thế giới thực (như timsort không để phân loại), ... Nhưng tôi đồng ý với bạn
iuridiniz

3
Chỉ sử dụng một mảng nhỏ, như trong cách tiếp cận của bạn, sẽ không phân biệt rõ ràng giữa các thuật toán khác nhau.
David Sanders

10
Nếu bạn tăng kích thước danh sách thử nghiệm lên 100000 ( a = (np.random.rand(100000) * 1000).round().astype('int'); a_list = list(a)), thuật toán "max w / set" của bạn sẽ là tệ nhất cho đến nay, trong khi phương pháp "numpy bincount" là tốt nhất. Tôi đã tiến hành thử nghiệm này bằng cách sử dụng a_listmã python gốc và amã numpy để tránh chi phí điều chỉnh làm ảnh hưởng đến kết quả.
David Sanders

4

Ngoài ra, nếu bạn muốn nhận giá trị thường xuyên nhất (dương hoặc âm) mà không cần tải bất kỳ mô-đun nào, bạn có thể sử dụng mã sau:

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
Điều này là của một thời gian trước đây, nhưng đối với hậu thế: điều này tương đương với dễ đọc hơn max(set(lVals), key=lVals.count), tính một O (n) cho mỗi phần tử duy nhất của lValskhoảng O (n ^ 2) (giả sử O (n) duy nhất phần tử). Sử dụng collections.Counter(lVals).most_common(1)[0][0]từ thư viện tiêu chuẩn, theo đề xuất của JoshAdel , chỉ là O (n).
Dougal

3

Mặc dù hầu hết các câu trả lời ở trên đều hữu ích, trong trường hợp bạn: 1) cần nó hỗ trợ các giá trị không phải số nguyên dương (ví dụ: số thực hoặc số nguyên âm ;-)) và 2) không có trên Python 2.7 (bộ sưu tập nào. yêu cầu) và 3) không muốn thêm sự phụ thuộc của scipy (hoặc thậm chí là numpy) vào mã của bạn, thì một giải pháp python 2.6 thuần túy là O (nlogn) (tức là hiệu quả) chỉ là:

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

Tôi thích giải pháp của JoshAdel.

Nhưng chỉ có một cách bắt.

Các np.bincount()giải pháp duy nhất hoạt động trên những con số.

Nếu bạn có chuỗi, collections.Countergiải pháp sẽ phù hợp với bạn.


1

Mở rộng trên phương pháp này , được áp dụng để tìm chế độ dữ liệu nơi bạn có thể cần chỉ mục của mảng thực tế để xem giá trị cách tâm phân phối bao xa.

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

Nhớ bỏ chế độ khi len (np.argmax (counts))> 1


1

Trong Python 3, những điều sau sẽ hoạt động:

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

1

Bắt đầu từ Python 3.4, thư viện tiêu chuẩn bao gồm statistics.modechức năng trả về một điểm dữ liệu chung nhất.

from statistics import mode

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

Nếu có nhiều chế độ có cùng tần số, statistics.modetrả về chế độ đầu tiên gặp phải.


Bắt đầu từ Python 3.8, statistics.multimodehàm trả về danh sách các giá trị xuất hiện thường xuyên nhất theo thứ tự chúng gặp lần đầu tiên:

from statistics import multimode

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

0

Đây là một giải pháp chung có thể được áp dụng dọc theo một trục, bất kể các giá trị, sử dụng hoàn toàn numpy. Tôi cũng thấy rằng điều này nhanh hơn nhiều so với scipy.stats.mode nếu có nhiều giá trị duy nhất.

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

Gần đây tôi đang thực hiện một dự án và sử dụng collection.Counter. (Điều này đã hành hạ tôi).

Theo ý kiến ​​của tôi, Counter trong bộ sưu tập có hiệu suất rất tệ. Nó chỉ là một lớp bao gói dict ().

Điều tồi tệ hơn, Nếu bạn sử dụng cProfile để lập hồ sơ phương thức của nó, bạn sẽ thấy rất nhiều thứ '__missing__' và '__instancecheck__' lãng phí toàn bộ thời gian.

Hãy cẩn thận khi sử dụng most_common () của nó, bởi vì mọi lúc nó sẽ gọi một loại làm cho nó cực kỳ chậm. và nếu bạn sử dụng most_common (x), nó sẽ gọi một loại đống, cũng chậm.

Btw, bincount của numpy cũng có một vấn đề: nếu bạn sử dụng np.bincount ([1,2,4000000]), bạn sẽ nhận được một mảng có 4000000 phần tử.


3
Một dict là cấu trúc dữ liệu được tinh chỉnh tốt nhất trong Python và lý tưởng để đếm các đối tượng tùy ý. Ngược lại, binning chỉ hoạt động trên các giá trị số và không cho phép bạn ngăn chặn răng cưa giữa các giá trị rời rạc có khoảng cách gần nhau. Trong trường hợp Counter, phương thức __missing__ chỉ được gọi khi một phần tử được nhìn thấy lần đầu tiên; nếu không, sự hiện diện của nó là miễn phí. Lưu ý, phương thức most_common () cực kỳ nhanh trong hầu hết các trường hợp vì heap rất nhỏ so với tổng tập dữ liệu. Trong hầu hết các trường hợp, phương thức most_common () chỉ tạo ra nhiều so sánh hơn min () một chút .
Raymond Hettinger,
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.