Знайдіть найчастіше число в нумерованому векторі


123

Припустимо, у мене є такий список у python:

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

Як акуратно знайти найчастішу кількість у цьому списку?

Відповіді:


193

Якщо ваш список містить усі негативні вставки, слід переглянути numpy.bincounts:

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

а потім, ймовірно, використовувати 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))

Для більш складного списку (який, можливо, містить від’ємні числа чи не цілі значення), ви можете використовувати np.histogramаналогічним чином. Крім того, якщо ви просто хочете працювати в python, не використовуючи numpy, collections.Counterце хороший спосіб обробки такого роду даних.

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. Може бути простоnp.bincount([1, 2, 3, 1, 2, 1, 1, 1, 3, 2, 2, 1]).argmax()
Микола Фетисов

1
+1. Це принаймні на порядок швидше, ніж scipy.stats.mode, хоча і менш загальне.
Фред Фоо

Гарна відповідь! Однак якщо хтось перебуває на python 2.6, collection.Counter недоступний. У такому випадку дивіться мою відповідь нижче.
JJC

19
Для тих, хто відвідує нас після 2016 року: мені не подобається ця відповідь, оскільки bincount (arr) повертає масив таким же великим, як і найбільший елемент arr, тому малий масив із великим діапазоном створює надмірно великий масив. Відповідь Апоенгта нижче набагато краща, хоча я не думаю, що numpy.unique () існував у 2011 році, коли ця відповідь була створена.
Вердо

2
Пітон 3 :Counter(array).most_common(1)[0][0]
диралік

80

Ви можете використовувати

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

Якщо якийсь елемент настільки ж частий, як інший, цей код поверне лише перший елемент.


4
Я вважаю це найбільш корисним, оскільки він є загальним, коротким і дозволяє витягувати елементи зі значень чи підрахунків за деяким похідним індексом.
ryanjdillon

2
Якщо у нас є кілька найчастіших значень, values[counts.argmax()]повернемо перше значення. Щоб отримати їх усі, ми можемо використати values[counts == counts.max()].
В. Чжу

44

Якщо ви готові використовувати 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

Вистави (використовуючи iPython) для деяких рішень, знайдених тут:

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

Найкраще - "max" з "set" для невеликих масивів, як проблема.

За даними @David Sanders, якщо збільшити розмір масиву до приблизно 100 000 елементів, алгоритм "max w / set" в кінцевому підсумку стає найгіршим, тоді як метод "numpy bincount" є найкращим.


1
@IuliusCurt, щоб вказати на найкращий підхід, нам потрібно перевірити його на декілька випадків: малі масиви, великі масиви, випадкові масиви, масиви реального світу (як це робить timsort для сортування), ... Але я згоден з вами
iuridiniz

3
Використання лише невеликого масиву, як у вашому підході, не буде дуже добре розрізняти різні алгоритми.
Девід Сандерс

10
Якщо ви збільшите розмір тестового списку до 100000 ( a = (np.random.rand(100000) * 1000).round().astype('int'); a_list = list(a)), ваш алгоритм "макс. Ш / набір" в кінцевому підсумку стає найгіршим, тоді як метод "нумерований рахунок" є найкращим. Я провів цей тест, використовуючи a_listнативний код python та anumpy код, щоб уникнути витрат на марширування та накручування результатів.
Девід Сандерс

4

Крім того, якщо ви хочете отримати найчастіше значення (позитивне або негативне), не завантажуючи жодних модулів, ви можете використовувати наступний код:

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
Це було певний час тому, але для нащадків: це еквівалентно легкому для читання max(set(lVals), key=lVals.count), який рахує O (n) для кожного унікального елемента lValsприблизно для O (n ^ 2) (якщо вважати O (n) унікальним елементи). Використання collections.Counter(lVals).most_common(1)[0][0]зі стандартної бібліотеки, як це запропонував JoshAdel , є лише O (n).
Дугал

3

Хоча більшість наведених вище відповідей корисні, якщо вам: 1) це потрібно для підтримки непозитивних цілих значень (наприклад, поплавці чи від'ємні цілі числа ;-)), і 2) не знаходяться на Python 2.7 (який колекції.Counter 3) воліють не додавати залежність scipy (або навіть numpy) до вашого коду, тоді суто рішення python 2.6, яке є O (nlogn) (тобто ефективним), це саме це:

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.

Але є лише один улов.

np.bincount()Рішення працює тільки на номери.

Якщо у вас є струни, collections.Counterрішення підійде для вас.


1

Розширюючи цей метод , застосовується для пошуку режиму даних, де, можливо, знадобиться індекс фактичного масиву, щоб побачити, наскільки далеко знаходиться значення від центру розподілу.

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

Не забудьте відмовитись від режиму, коли len (np.argmax (counts))> 1



1

Починаючи з Python 3.4, стандартна бібліотека включає statistics.modeфункцію повернення єдиної найпоширенішої точки даних.

from statistics import mode

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

Якщо є кілька режимів з однаковою частотою, statistics.modeповертається перший, що виник.


Починаючи з цього Python 3.8, statistics.multimodeфункція повертає список найбільш часто зустрічаються значень у тому порядку, в якому вони вперше зустрічалися:

from statistics import multimode

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

0

Ось загальне рішення, яке можна застосувати уздовж осі, незалежно від значень, використовуючи чисто нумеровані. Я також виявив, що це набагато швидше, ніж scipy.stats.mode, якщо існує багато унікальних значень.

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

Нещодавно я займаюся проектом і використовую колекції.Counter. (Що мене мучило).

Лічильник колекцій на мою думку має дуже погані показники. Це просто клас обгортки ().

Що ще гірше, якщо ви використовуєте cProfile для профілю свого методу, ви повинні побачити багато "__missing__" і "__instancheck__" матеріалів, що витрачають весь час.

Будьте обережні, використовуючи його most_common (), оскільки кожного разу він викликає такий вид, який робить його надзвичайно повільним. і якщо ви використовуєте most_common (x), він викликатиме сортування купи, що також повільно.

Btw, рахунок рахунку numpy також має проблему: якщо ви використовуєте np.bincount ([1,2400000000]), ви отримаєте масив з 4000000 елементів.


3
Диктант є найбільш тонко налаштованою структурою даних в Python і ідеально підходить для підрахунку довільних об'єктів. Навпаки, бінінг працює лише на числових значеннях і не дозволяє запобігти псевдоніму між близько розташованими дискретними значеннями. У випадку Counter метод __missing__ викликається лише тоді, коли елемент вперше побачений; в іншому випадку його присутність є безкоштовною. Зауважте, метод most_common () у більшості випадків надзвичайно швидкий, оскільки купа дуже мала порівняно із загальним набором даних. У більшості випадків метод most_common () робить лише трохи більше порівнянь, ніж min () .
Реймонд Хеттінгер
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.