查找 NumPy 数组中最频繁的数字

假设我有以下 NumPy 数组:

a = np.array([1,2,3,1,2,1,1,1,3,2,2,1])

如何找到这个数组中最频繁的数字?

243203 次浏览

如果你的列表包含所有非负整型,你应该看看 numpy.bincount:

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

如果你愿意使用 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

此外,如果你想得到最频繁的值(正面或负面)而不加载任何模块,你可以使用以下代码:

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

我最近在做一个项目和使用集合。计数器。(折磨我)。

在我看来,计数器在集合中有一个非常非常糟糕的性能。它只是一个类包装结果()。

更糟糕的是,如果您使用 cProfile 来分析它的方法,您应该会看到很多“ _ _ miss _ _”和“ _ _ instanccheck _ _”的东西浪费了整个时间。

小心使用它的 most _ common () ,因为每次它都会调用一个排序,这会使它变得非常慢。如果使用 most _ common (x) ,它将调用堆排序,这也很慢。

顺便说一下,numpy 的 bincount 也有一个问题: 如果使用 np.bincount ([1,2,4000000]) ,您将得到一个包含4000000个元素的数组。

虽然以上大部分答案都很有用,但如果你: 1)需要它支持非正整数值(例如浮点数或负整数; ——) ,以及 2)不在 Python 2.7上(这是集合.Counter 所需要的) ,以及 3)不喜欢在代码中添加 scypy (甚至 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]

在这里找到的一些解决方案的性能(使用 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
>>>

最好的是’最大’与’设置’用于小型数组喜欢的问题。

根据@David Sanders 的说法,如果你将数组大小增加到100,000个元素,“ max w/set”算法的结果是 是目前为止最糟糕的,而“ numpy bincount”方法是最好的。

你可以用

values, counts = np.unique(a, return_counts=True)


ind = np.argmax(counts)
print(values[ind])  # prints the most frequent element


ind = np.argpartition(-counts, kth=10)[:10]
print(values[ind])  # prints the 10 most frequent elements

如果某个元素与另一个元素一样频繁,则此代码将仅返回第一个元素。

我喜欢 JoshAdel 的解决方案。

但有一个问题。

np.bincount()解决方案只对数字有效。

如果您有字符串,collections.Counter解决方案将为您工作。

这里有一个通用的解决方案,可以沿着一个轴应用,不管值是什么,使用纯粹的 numpy。我还发现,如果存在很多唯一值,那么这个函数的运行速度要比 cipy.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]

这种方法上展开,应用于查找数据模式,您可能需要实际数组的索引来查看值离分布的中心有多远。

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

记住在 len (np.argmax (count)) > 1时丢弃模式

在 Python 3中,下面的代码应该可以工作:

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

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]

你可以使用以下方法:

x = np.array([[2, 5, 5, 2], [2, 7, 8, 5], [2, 5, 7, 9]])
u, c = np.unique(x, return_counts=True)
print(u[c == np.amax(c)])

这将给出答案: array([2, 5])