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

时间:2013-05-02 05:20:54

标签: python numpy 2d mode

我有一个包含整数的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

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

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

9 个答案:

答案 0 :(得分:73)

检查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]]

答案 1 :(得分:19)

<强>更新

自此帖以来scipy.stats.mode函数已经过显着优化,并且是推荐的方法

旧答案

这是一个棘手的问题,因为沿着轴计算模式并不多。该解决方案可直接用于1-D阵列,其中numpy.bincount非常方便,numpy.unique的{​​{1}} arg为return_counts。我看到的最常见的n维函数是scipy.stats.mode,虽然它非常慢 - 特别是对于具有许多唯一值的大型数组。作为一种解决方案,我开发了这个功能,并大量使用它:

True

结果:

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

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

答案 2 :(得分:7)

扩展this method,用于查找数据模式,您可能需要实际数组的索引,以查看该值与分布中心的距离。

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

当len(np.argmax(计数))&gt;时,请记住放弃模式。 1,也是为了验证它是否实际代表您的数据的中心分布,您可以检查它是否属于您的标准偏差区间。

答案 3 :(得分:5)

如果您只想使用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]

答案 4 :(得分:3)

我认为一种非常简单的方法是使用Counter类。然后,您可以使用Counter实例的most_common()函数,如here所述。

对于一维数组:

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

这可能是也可能不是有效的实施方式,但很方便。

答案 5 :(得分:2)

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)对频率进行计数并返回defaultdict。 sorted(Counter(data).items())使用键而不是频率进行排序。最后,需要使用另一个用key = lambda x: x[1]排序的频率来排序。反之则告诉Python将频率从最大到最小排序。

答案 6 :(得分:1)

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

   import statistics
   print("mode = "+str(statistics.(mode(a)))

就这样

答案 7 :(得分:0)

使用numpy(不是scipy也不是Counter类的一种简洁的解决方案):

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

答案 8 :(得分:0)

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

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 中打印模式值:print(int(stats.mode(numbers)[0]))

相关问题