将数组拆分为数量相等的 bin
Split an array into bins of equal numbers
我有一个包含 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,但它没有给我不等距的垃圾箱。
在此 post 中列出的是一种基于 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()]
采样 运行s 对每个 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])
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 中间点的提示
如果要 return 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 计数)相等
我有一个包含 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,但它没有给我不等距的垃圾箱。
在此 post 中列出的是一种基于 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()]
采样 运行s 对每个 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])
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 中间点的提示
如果要 return 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 计数)相等