将一个数组分成相等数量的区间

7

我有一个未排序的由N个元素组成的数组。我想保留N的原始顺序,但是不使用实际的元素,而是使用它们的bin号码,其中N被分成m个相等的(如果N可被m整除)或近似相等的值(如果N不能被m整除)。我需要一个向量化的解决方案(因为N非常大,所以标准的Python方法效率不高)。在scipy或numpy中有什么可以做到这一点的东西吗?

e.g.
N = [0.2, 1.5, 0.3, 1.7, 0.5]
m = 2
Desired output: [0, 1, 0, 1, 0]

我已经查看了numpy.histogram,但它并未给出不等间距的箱子。
2个回答

4

本文介绍了使用基于NumPy的矢量化方法,通过使用np.searchsorted创建长度为输入数组的等间隔索引的思路。

以下是具体实现方法:-

def equal_bin(N, m):
    sep = (N.size/float(m))*np.arange(1,m+1)
    idx = sep.searchsorted(np.arange(N.size))
    return idx[N.argsort().argsort()]

样例运行使用每个箱子的计数来验证结果 -

In [442]: N = np.arange(1,94)

In [443]: np.bincount(equal_bin(N, 4))
Out[443]: array([24, 23, 23, 23])

In [444]: np.bincount(equal_bin(N, 5))
Out[444]: array([19, 19, 18, 19, 18])

In [445]: np.bincount(equal_bin(N, 10))
Out[445]: array([10,  9,  9, 10,  9,  9, 10,  9,  9,  9])

这里有另一种方法,使用linspace来创建那些等间距的数字,可以用作索引,例如 -
def equal_bin_v2(N, m):
    idx = np.linspace(0,m,N.size+0.5, endpoint=0).astype(int)
    return idx[N.argsort().argsort()]  

示例运行 -

In [689]: N
Out[689]: array([ 0.2,  1.5,  0.3,  1.7,  0.5])

In [690]: equal_bin_v2(N,2)
Out[690]: array([0, 1, 0, 1, 0])

In [691]: equal_bin_v2(N,3)
Out[691]: array([0, 1, 0, 2, 1])

In [692]: equal_bin_v2(N,4)
Out[692]: array([0, 2, 0, 3, 1])

In [693]: equal_bin_v2(N,5)
Out[693]: array([0, 3, 1, 4, 2])

我针对 N = np.arange(1,94) 和 m = 10 运行了这个程序,但是分组的大小并不相等。我期望它大部分是9或10,但是有一些 bin 有6个元素,另一些则有11个元素等等。 - max_max_mir
@max_max_mir 请查看更新版本,速度应该更快! - Divakar
感谢@Divakar的快速回复。第一个还不太行(接近了)。如果您尝试equal_bin(np.arange(1,91), 10),您会在8个箱中得到9个元素,但是您有1个箱中有10个元素,另一个箱中有8个元素。在这种情况下,每个箱子应该有9个元素。 - max_max_mir

3

pandas.qcut

另一种好的选择是pandas中的pd.qcut。例如:

In [6]: import pandas as pd
In [7]: N = [0.2, 1.5, 0.3, 1.7, 0.5]
   ...: m = 2

In [8]: pd.qcut(N, m, labels=False)
Out[8]: array([0, 1, 0, 1, 0], dtype=int64)

获取直方图中间点的提示

如果您想返回箱子的边缘,请使用labels=True(默认值)。这将允许您获取箱子的中间点:

In [26]: intervals = pd.qcut(N, 2)

In [27]: [i.mid for i in intervals]
Out[27]: [0.34950000000000003, 1.1, 0.34950000000000003, 1.1, 0.34950000000000003]

如果 labels=True,则间隔是 pandas.Interval 对象数组。


另请参阅:pd.cut,如果您想要使bin的宽度(而不是bin数)相等。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接