Python 在numpy阵列中查找模式的最有效方法

Python 在numpy阵列中查找模式的最有效方法,python,numpy,2d,mode,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可能有一些内置函数来实现这一点。或者如果有一个技巧可以在不循环的情况下高效地找到它。检查(受@tom10评论的启发): 输出: ModeR

我有一个包含整数(正数或负数)的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可能有一些内置函数来实现这一点。或者如果有一个技巧可以在不循环的情况下高效地找到它。

检查(受@tom10评论的启发):

输出:

ModeResult(mode=array([[1, 3, 2, 2, 1, 1]]), count=array([[1, 2, 2, 2, 1, 2]]))
[[1 3 2 2 1 1]]
如您所见,它返回模式和计数。您可以通过
m[0]
直接选择模式:

print(m[0])
输出:

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

更新

自本帖发布以来,
scipy.stats.mode
功能已得到显著优化,这将是推荐的方法

旧答案

这是一个棘手的问题,因为没有太多沿轴计算模式。对于一维阵列,解决方案是直接的,
numpy.bincount
numpy.unique
一起使用
return\u counts
arg作为
True
非常方便。我看到的最常见的n维函数是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]
结果:

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(counts))>1时,要放弃该模式,还要验证它是否真正代表了数据的中心分布。您可以检查它是否在标准偏差区间内。

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

对于一维阵列:

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])    
对于多维数组(差别不大):

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

一个只使用
numpy
(不是
scipy
也不是
计数器
类)的简洁解决方案:

数组([1,3,2,2,1,1])

从集合导入计数器
n=int(输入())
数据=已排序([int(i)表示输入中的i().split()]))
已排序(已排序(计数器(数据).items()),key=lambda x:x[1],reverse=True)[0][0]
打印(平均值)

计数器(数据)
计算频率并返回defaultdict<代码>排序(计数器(数据).items())使用键而不是频率进行排序。最后,需要使用另一个使用
key=lambda x:x[1]
排序的频率进行排序。相反,Python会将频率从最大值排序到最小值

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

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

如果您只想使用numpy,请执行以下操作:

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

并将其提取:

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

如果要将模式作为int值查找,这里是最简单的方法 我试图使用Scipy Stats找出数组的模式,但问题是代码的输出如下所示:

ModeResult(mode=array(2),count=array([[1,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(number)[0])

这里有答案:@tom10:你的意思是,对吗?另一个似乎输出了一个屏蔽数组。@fgb:好的,谢谢你的更正(和+1的回答)。所以numpy本身不支持任何这样的功能?显然不支持,但是,你可以将代码复制到你自己的函数中。只需注意,对于将来关注这一点的人:您需要显式地导入scipy.stats,当您简单地执行导入scipy时,它不包括在内。您能解释一下它是如何精确地显示模式值和计数的吗?我无法将输出与输入提供的关联。@ RaHuL:您必须考虑默认的第二个参数<代码>轴= 0 < /代码>。上面的代码报告了每列输入的模式。计数告诉我们在每个列中看到报告模式的次数。如果需要整体模式,则需要指定
axis=None
。有关更多信息,请参阅请将其贡献给scipy的统计模块,以便其他人也能从中受益。对于具有大整数数组的高维问题,您的解决方案似乎仍然比scipy.stats.mode快得多。我不得不沿着4x250x500 ndarray的第一个轴计算模式,你的函数用了10秒,而scipy.stats.mode几乎用了600秒。我同意上面的评论。对于更大的矩阵,您的函数仍然比scipy的实现快(尽管我从scipy获得的性能比我的600秒要好得多)。自从6年前提出这个问题以来,他没有获得太多声誉是正常的。如果不指定轴,np.argmax何时会返回长度大于1的内容?非常简洁,但如果原始数组包含非常大的数字,则应谨慎使用,因为bincount将为每个原始数组a[i]创建len(max(a[i])的bin数组.这是一个很棒的解决方案。实际上,
scipy.stats.mode
有一个缺点。当多个值出现次数最多(多个模式)时,它将抛出一个期望值。但是这个方法会自动采用“第一种模式”。就像这个方法一样,因为它不仅支持整数,还支持浮点甚至字符串!
index = np.argmax(counts)
return vals[index]
import numpy as np
from scipy import stats
numbers = list(map(int, input().split())) 
print(int(stats.mode(numbers)[0]))