找到主要包含零的数组中值的位置

时间:2018-04-09 19:27:13

标签: python-3.x sorting numpy indexing median

我有一个非常大的1d数组,其中大多数元素为零,而非零元素都聚集在由许多零分隔的几个岛周围:(这是为MWE目的的较小版本)

In [1]: import numpy as np

In [2]: A=np.array([0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,3,6,20,14,10,5,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,4,5,5,18,18,16,14,10,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,3,3,6,16,4,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0])

我想根据对应于每个岛的中值的指数找到中位数及其位置(甚至大约)。毫不奇怪,我得到零,这不是我想要的:

In [3]: np.median(A)
Out[3]: 0.0

In [4]: np.argsort(A)[len(A)//2]
Out[4]: 12

对于单个非零元素岛,要解决这个警告并满足我的要求,即只有非零元素具有物理意义,我先删除所有零,然后取剩余元素的中位数:

In [5]: masks = np.where(A>0)
In [6]: A[masks]
Out[6]: array([ 1,  3,  6, 20, 14, 10,  5,  1])

这一次,我正确地得到了新数组的中位数,但是位置(索引)不正确,因为它很明显,并且在评论中也指出数学上是错误定义的。

In [7]: np.median(A[masks])
Out[7]: 5.5

In [8]: np.argsort(A[masks])[len(A[masks])//2]
Out[8]: 2

根据这个近似值,我知道真正的中位数位于修改后的数组的第三个索引中,但我想将其转换回原始数组的格式,其中中位数的位置(索引)应该在某处在非零元素的第一个岛的中间,对应于较大的索引(其中零的索引都被正确计数)。评论中还回答了两个建议,即在零海中间给出一个非零元素岛的中位数。但如果有多个这样的岛呢?怎么可能在原始直方图数组的上下文中计算对应于每个岛的中位数的索引,其中零都被计算在内?

我想知道是否有任何简单的方法来计算这些多个零数组中的中位数。如果没有,在知道修改后的数组中的位置后,我还应该在我的代码行中添加什么呢?非常感谢您的帮助。

1 个答案:

答案 0 :(得分:1)

根据评论“A实际上是一个带有许多箱子的离散直方图”,我认为你想要的是计算值的中位数。如果A是一个整数计数数组,那么中位数的精确(但可能非常低效,如果你有高达1e7的值)公式是

np.median(np.repeat(np.arange(len(A)), A))  # Do not use if A contains very large values!

或者,您可以使用

np.searchsorted(A.cumsum(), 0.5*A.sum())

这将是中位数的整数部分。

例如:

In [157]: A
Out[157]: 
array([ 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
        0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  1,  3,
        6, 20, 14, 10,  5,  1,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
        0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
        0,  0,  0,  0])

In [158]: np.median(np.repeat(np.arange(len(A)), A))
Out[158]: 35.5

In [159]: np.searchsorted(A.cumsum(), 0.5*A.sum())
Out[159]: 35

另一个例子:

In [167]: B
Out[167]: 
array([  0,   0,   0,   1, 100,  21,   8,   3,   2,   1,   0,   0,   0,
         0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
         0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0])

In [168]: np.median(np.repeat(np.arange(len(B)), B))
Out[168]: 4.0

In [169]: np.searchsorted(B.cumsum(), 0.5*B.sum())
Out[169]: 4