将点分配到箱中
如何将数字值分到特定的范围里呢?比如说,我有一串数字,我想把它们分成N个区间。现在,我的做法是这样的:
from scipy import *
num_bins = 3 # number of bins to use
values = # some array of integers...
min_val = min(values) - 1
max_val = max(values) + 1
my_bins = linspace(min_val, max_val, num_bins)
# assign point to my bins
for v in values:
best_bin = min_index(abs(my_bins - v))
这里的min_index是用来找出最小值的索引。这个方法的思路是,通过找出某个点与各个区间的差值,来确定它应该属于哪个区间。
不过,我觉得这样做会有一些奇怪的边界情况。我希望能找到一种更好的区间表示方式,理想情况下是半闭合半开放的区间(这样就不会把一个点分到两个区间里),也就是说:
bin1 = [x1, x2)
bin2 = [x2, x3)
bin3 = [x3, x4)
etc...
在Python中,使用numpy或scipy,有什么好的方法来实现这个呢?我这里主要关注的是整数值的分区。
非常感谢你的帮助。
2 个回答
1
在numpy中,使用广播功能可以很简单地做到这一点——我下面的例子只用了四行代码(不算前两行用来创建区间和数据点,这些通常是提前准备好的)。
import numpy as NP
# just creating 5 bins at random, each bin expressed as (x, y, z) although, this code
# is not limited by bin number or bin dimension
bins = NP.random.random_integers(10, 99, 15).reshape(5, 3)
# creating 30 random data points
data = NP.random.random_integers(10, 99, 90).reshape(30, 3)
# for each data point i want the nearest bin, but before i can generate a distance
# matrix, i need to 'conform' the array dimensions
# 'broadcasting' is an excellent and concise way to do this
bins = bins[:, NP.newaxis, :]
data2 = data[NP.newaxis, :, :]
# now i can calculate the distance matrix
dist_matrix = NP.sqrt(NP.sum((data - bins)**2, axis=-1))
bin_assignments = NP.argmin(dist_matrix, axis=0)
'bin_assignments'是一个一维数组,里面的值是从0到4的整数,这些值对应于五个区间——也就是上面'data'矩阵中30个原始点的区间分配情况。
27
numpy.histogram()
正好能满足你的需求。
这个函数的使用方法是:
numpy.histogram(a, bins=10, range=None, normed=False, weights=None, new=None)
我们主要关注 a
和 bins
。a
是你需要分组的数据,而 bins
可以是一个数字(也就是你的 num_bins
),或者是一系列数字,这些数字表示分组的边界(半开区间)。
import numpy
values = numpy.arange(10, dtype=int)
bins = numpy.arange(-1, 11)
freq, bins = numpy.histogram(values, bins)
# freq is now [0 1 1 1 1 1 1 1 1 1 1]
# bins is unchanged
引用一下 文档:
除了最后一个(最右边的)分组外,其他的分组都是半开的。换句话说,如果
bins
是:[1, 2, 3, 4]
那么第一个分组是
[1, 2)
(包括1,但不包括2),第二个分组是[2, 3)
。不过最后一个分组是[3, 4]
,它是包括 4 的。
补充:如果你想知道每个元素在你的分组中的索引,可以使用 numpy.digitize()
。如果你的分组是整数的话,你也可以使用 numpy.bincount()
。
>>> values = numpy.random.randint(0, 20, 10)
>>> values
array([17, 14, 9, 7, 6, 9, 19, 4, 2, 19])
>>> bins = numpy.linspace(-1, 21, 23)
>>> bins
array([ -1., 0., 1., 2., 3., 4., 5., 6., 7., 8., 9.,
10., 11., 12., 13., 14., 15., 16., 17., 18., 19., 20.,
21.])
>>> pos = numpy.digitize(values, bins)
>>> pos
array([19, 16, 11, 9, 8, 11, 21, 6, 4, 21])
由于上限是开放的,所以索引是正确的:
>>> (bins[pos-1] == values).all()
True
>>> import sys
>>> for n in range(len(values)):
... sys.stdout.write("%g <= %g < %g\n"
... %(bins[pos[n]-1], values[n], bins[pos[n]]))
17 <= 17 < 18
14 <= 14 < 15
9 <= 9 < 10
7 <= 7 < 8
6 <= 6 < 7
9 <= 9 < 10
19 <= 19 < 20
4 <= 4 < 5
2 <= 2 < 3
19 <= 19 < 20