在 numpy 数组中寻找模式的最有效方法

我有一个包含整数(正数或负数)的2D 数组。每一行表示特定空间站点随时间变化的值,而每一列表示给定时间内不同空间站点的值。

所以如果数组是这样的:

1 3 4 2 2 7
5 2 2 1 4 1
3 3 2 2 1 1

结果应该是

1 3 2 2 2 1

注意,当 mode 有多个值时,任何一个(随机选择)都可以设置为 mode。

我可以一次迭代一个列查找模式,但是我希望 numpy 可能有一些内置函数来做到这一点。或者如果有一个技巧可以不循环地高效地找到它。

282618 次浏览

检查 scipy.stats.mode()(受@tom10评论的启发) :

import numpy as np
from scipy import stats


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


m = stats.mode(a)
print(m)

产出:

ModeResult(mode=array([[1, 3, 2, 2, 1, 1]]), count=array([[1, 2, 2, 2, 1, 2]]))

如您所见,它既返回模式,也返回计数。你可以通过 m[0]直接选择模式:

print(m[0])

产出:

[[1 3 2 2 1 1]]

更新

scipy.stats.mode的功能已经显着优化,因为这个职位,并将是推荐的方法

旧答案

这是一个棘手的问题,因为没有太多的计算模式沿着一个轴。对于一维数组,解决方案是直截了当的,其中 numpy.bincount非常方便,还有 numpy.uniquereturn_counts arg 作为 True。我看到的最常见的 n 维函数是 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]

结果:

In [2]: a = numpy.array([[1, 3, 4, 2, 2, 7],
[5, 2, 2, 1, 4, 1],
[3, 3, 2, 2, 1, 1]])


In [3]: mode(a)
Out[3]: (array([1, 3, 2, 2, 1, 1]), array([1, 2, 2, 2, 1, 2]))

一些基准:

In [4]: import scipy.stats


In [5]: a = numpy.random.randint(1,10,(1000,1000))


In [6]: %timeit scipy.stats.mode(a)
10 loops, best of 3: 41.6 ms per loop


In [7]: %timeit mode(a)
10 loops, best of 3: 46.7 ms per loop


In [8]: a = numpy.random.randint(1,500,(1000,1000))


In [9]: %timeit scipy.stats.mode(a)
1 loops, best of 3: 1.01 s per loop


In [10]: %timeit mode(a)
10 loops, best of 3: 80 ms per loop


In [11]: a = numpy.random.random((200,200))


In [12]: %timeit scipy.stats.mode(a)
1 loops, best of 3: 3.26 s per loop


In [13]: %timeit mode(a)
1000 loops, best of 3: 1.75 ms per loop

编辑: 提供了更多的背景并修改了方法以提高内存效率

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

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

记住,当 len (np.argmax (count)) > 1时要丢弃模式,还要验证它是否真正代表了数据的中心分布,你可以检查它是否在你的标准差间隔内。

我认为一个非常简单的方法是使用 Counter 类。然后可以使用前面提到的 Counter 实例的 most _ common ()函数。

对于1-d 数组:

import numpy as np
from collections import Counter


nparr = np.arange(10)
nparr[2] = 6
nparr[3] = 6 #6 is now the mode
mode = Counter(nparr).most_common(1)
# mode will be [(6,3)] to give the count of the most occurring value, so ->
print(mode[0][0])

对于多维数组(差别不大) :

import numpy as np
from collections import Counter


nparr = np.arange(10)
nparr[2] = 6
nparr[3] = 6
nparr = nparr.reshape((10,2,5))     #same thing but we add this to reshape into ndarray
mode = Counter(nparr.flatten()).most_common(1)  # just use .flatten() method


# mode will be [(6,3)] to give the count of the most occurring value, so ->
print(mode[0][0])

这可能是一个有效的实现,也可能不是,但是它很方便。

一个简洁的解决方案,只有使用 numpy(而不是 scipyCounter类) :

A = np.array([[1,3,4,2,2,7], [5,2,2,1,4,1], [3,3,2,2,1,1]])


np.apply_along_axis(lambda x: np.bincount(x).argmax(), axis=0, arr=A)

Array ([1,3,2,2,1,1])

from collections import Counter


n = int(input())
data = sorted([int(i) for i in input().split()])


sorted(sorted(Counter(data).items()), key = lambda x: x[1], reverse = True)[0][0]


print(Mean)

Counter(data)计算频率并返回默认值。sorted(Counter(data).items())使用键排序,而不是使用频率。最后,需要排序的频率使用另一个排序与 key = lambda x: x[1]。反过来则告诉 Python 将频率从最大到最小进行排序。

在 Python 中获取列表或数组模式的最简单方法是

import statistics
a=[7,4,4,4,4,25,25,6,7,4867,5,6,56,52,32,44,4,4,44,4,44,4]
print(f"{statistics.mode(a)} is the mode (most frequently occurring number)")

就是这样

如果只想使用 numpy:

x = [-1, 2, 1, 3, 3]
vals,counts = np.unique(x, return_counts=True)

给予

(array([-1,  1,  2,  3]), array([1, 1, 1, 2]))

然后提取出来:

index = np.argmax(counts)
return vals[index]

如果你想找到模式,这里的 int 值是最简单的方法 我试图用 Scipy Stats 找出 Array 的模式,但问题是代码的输出看起来像:

ModeResult(mode=array(2), count=array([[1, 2, 2, 2, 1, 2]])) ,我只想要整数输出,所以如果你想要相同的只是尝试这一点

import numpy as np
from scipy import stats
numbers = list(map(int, input().split()))
print(int(stats.mode(numbers)[0]))

最后一行就足以打印 Python 中的 Mode Value: print(int(stats.mode(numbers)[0]))

如果您希望只使用 numpy,并且不使用数组的索引,则可以执行此操作。可以使用以下结合字典和 numpy 的实现。

val,count = np.unique(x,return_counts=True)

freq = {}
for v,c in zip(val,count):
freq[v] = c
mode = sorted(freq.items(),key =lambda kv :kv[1])[-1]