【问题标题】:Split an array into bins of equal numbers将数组拆分为相等数量的 bin
【发布时间】:2017-04-15 02:47:46
【问题描述】:

我有一个包含 N 个元素的数组(未排序)。我想保留 N 的原始顺序,但不是实际元素,而是希望它们有自己的 bin 编号,其中 N 被分成 m 个相等的 bin(如果 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,但它并没有给我不等间距的 bin。

【问题讨论】:

    标签: arrays numpy scipy vectorization binning


    【解决方案1】:

    本文中列出的是基于 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()]
    

    对每个 bin 进行 bin 计数以验证结果的样本运行 -

    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,但有 6 个元素的垃圾箱,有些有 11 个元素,依此类推。
    • @max_max_mir 查看更新后的版本,应该也快了很多!
    • 感谢@Divakar 的快速回复。第一个还没有出现(已经很接近了)。如果您尝试 equal_bin(np.arange(1,91), 10),您会在 8 个 bin 中获得 9 个元素,但在 1 个 bin 中有 10 个元素,在另一个 bin 中有 8 个元素。在这种情况下,每个元素应该是 9 个。
    【解决方案2】:

    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)
    

    获取 bin 中间点的提示

    如果要返回 bin 边缘,请使用 labels=True(默认)。这将允许您通过以下方式获得 bin 中间点:

    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]
    

    间隔是pandas.Interval 对象的数组(当labels=True 时)。


    另请参阅:pd.cut,如果您想让 bin width(不是 bin 计数)相等

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-09-13
      • 1970-01-01
      • 1970-01-01
      • 2012-04-12
      • 1970-01-01
      • 1970-01-01
      • 2018-08-01
      • 2017-09-04
      相关资源
      最近更新 更多