如何在一维的多个 numpy 数组中跨元素取模式

How to take the mode across elements in multiple numpy arrays of 1-d

我可以按照

逐个元素地计算多个 lists/arrays 的总和
sum([np.array([1,2,3,4,5]), np.array([1,2,3,4,5])]) = array([ 2,  4,  6,  8, 10])

我想对数组中每个元素的模式做类似的事情,预期结果:

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

在没有单一模式的情况下,我想 select 随机地 select 一个数组中的元素作为输出,按照:

mode([np.array([0,2,3,4,0]), np.array([1,2,9,4,5])]) = array([ 1, 2, 9, 4, 0])

最后,我希望能够跨任意数量的等长数组执行此操作。

注意:我尝试使用统计库模式,但出现以下错误:

>>> mode([np.array([1,2,3,4,5]), np.array([1,2,3,4,5])])
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/anaconda3/lib/python3.7/statistics.py", line 501, in mode
    table = _counts(data)
  File "/anaconda3/lib/python3.7/statistics.py", line 252, in _counts
    table = collections.Counter(iter(data)).most_common()
  File "/anaconda3/lib/python3.7/collections/__init__.py", line 566, in __init__
    self.update(*args, **kwds)
  File "/anaconda3/lib/python3.7/collections/__init__.py", line 653, in update
    _count_elements(self, iterable)
TypeError: unhashable type: 'numpy.ndarray'

仅使用列表时也会抛出错误。

因此我正在寻找实现此目标的方法。

一个关键要求是跨数组元素的模式输出向量必须与每个向量的长度相同(我将把它输入混淆矩阵以与参考向量进行比较)。

您可以使用 scipy.stats.mode 找到模式。您还可以将多个 numpy 数组连接成一个数组,然后将其提供给模式。

import numpy as np
import scipy.stats

arrays = [np.array([0,2,3,4,0]), np.array([1,2,9,4,5])]

result = scipy.stats.mode(np.concatenate(arrays))
# ModeResult(mode=array([0]), count=array([2]))

result.mode
# array([0])

scipy.stats.mode 的 return 值是一个命名元组 ModeResult,其中包括模式和值出现的次数。

要找到每列的众数,您可以将数组堆叠成一个二维数组,然后找到沿第一个轴的众数。

arrays = [
    np.array([0, 2, 3, 4, 0]), 
    np.array([1, 2, 9, 4, 5]), 
    np.array([0, 9, 9, 4, 1])]
result = scipy.stats.mode(np.stack(arrays), axis=0)
result.mode
# array([[0, 2, 9, 4, 0]])

以你的两个例子:

In [358]: alist = [np.array([1,2,3,4,5]), np.array([1,2,3,4,5])]                        
In [359]: alist1 = [np.array([0,2,3,4,0]), np.array([1,2,9,4,5])]                       

两个来源:

In [360]: import statistics                                                             
In [361]: from scipy import stats                                                       

zip(*alist)可以取对应'pairs'的模式:

In [362]: [statistics.mode(foo) for foo in zip(*alist)]                                 
Out[362]: [1, 2, 3, 4, 5]
In [363]: [statistics.mode(foo) for foo in zip(*alist1)]                                
....
StatisticsError: no unique mode; found 2 equally common values

mode 文档警告此错误。

scipy版本将列表变成二维数组;哪个

In [365]: stats.mode(alist,axis=0)                                                      
Out[365]: ModeResult(mode=array([[1, 2, 3, 4, 5]]), count=array([[2, 2, 2, 2, 2]]))
In [366]: stats.mode(alist1,axis=0)                                                     
Out[366]: ModeResult(mode=array([[0, 2, 3, 4, 0]]), count=array([[1, 2, 1, 2, 1]]))
In [367]: np.array(alist1)                                                              
Out[367]: 
array([[0, 2, 3, 4, 0],
       [1, 2, 9, 4, 5]])

stats.mode代码为Python,可以研究一下。通过这样的轴选择,它显然在列上迭代,在每个列上采用一维模式。因此速度将与列表理解案例相当。但是 StatsError 案例的处理方式不同。

但是我们可以用一些实用函数来处理错误情况:

In [375]: def myfn(foo): 
     ...:     try: 
     ...:         return statistics.mode(foo) 
     ...:     except statistics.StatisticsError: 
     ...:         return None # or a random value 
     ...:                                                                               
In [376]: [myfn(foo) for foo in zip(*alist)]                                            
Out[376]: [1, 2, 3, 4, 5]
In [377]: [myfn(foo) for foo in zip(*alist1)]                                           
Out[377]: [None, 2, None, 4, None]

列表理解时间有利:

In [378]: timeit [myfn(foo) for foo in zip(*alist1)]                                    
73.6 µs ± 278 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
In [379]: timeit stats.mode(alist1,axis=0)                                              
384 µs ± 1.09 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)