我想以这样的方式对数字列(var
)进行分区,使得每个bin中的行数大致相同。我的附加要求是该列中的一个(唯一)值不能分配给多于一个bin。例如,如果将列var
中的值1分配给bin 1,则不允许将值1分配给bin 2。
我知道函数ntile()
或percent_rank()
,但我不知道这些函数如何用于手头的任务。
drop table if exists binme;
create table binme (var numeric);
insert into binme values
(0), (0), (0),
(1), (1), (1.5), (1.5),
(2), (2), (2), (2.5),
(3), (3), (3.5), (4.5),
(5), (6), (7), (10), (11);
select (var * 100)::int, ntile(5) over(order by var), percent_rank() over(order by var)
from binme;
对于我的例子和5个箱子,所需的结果是:
var ntile required_bin
0 1 1
0 1 1
0 1 1
1 1 1
1 2 1 Has to be in bin 1
1.5 2 2
1.5 2 2
2 2 2
2 3 2
2 3 2 Has to be in bin 2
2.5 3 3
3 3 3
3 4 3
3.5 4 3
4.5 4 4
5 4 4
6 5 4
7 5 4
10 5 5
11 5 5
我在某种程度上直觉地认为可能需要先按var
分组,获取每个值的行数并使用一些递归查询将bin分配给原始数据。应该可以从以下方面弄清楚:
select
var,
cnt,
sum(cnt) over(order by var) as nrows
from
(select var, count(*) cnt from binme group by var) a;
答案 0 :(得分:1)
如果您只是寻找近似值(确保将相同的值放在同一个存储桶中),那么您确实可以使用@greg所提到的width_bucket
,但要平衡数字对于每个桶的项目,它必须应用于累积的总和而不是var
值本身。这是一个演示(SQL fiddle,下面改进的解决方案):
SELECT
o.var,
WIDTH_BUCKET(o.cumSum, 1, o.cnt + 1, 5) bucket
FROM
(SELECT
b.var,
(SELECT COUNT(*) FROM binme t) AS cnt,
(SELECT COUNT(*) FROM binme t WHERE t.var <= b.var) AS cumSum
FROM
binme b
) o
;
累计和(或累计数可能更精确)至少为1
(包括最小值)和最大(不包括)cnt + 1
,第3个参数指定桶数。第一个存储桶为1
(不是0
,对于基于0的存储桶编号减去一个)。
或者,您可以<
代替<=
并将范围设置为[0,cnt)
,这可能是更好的解决方案 :SQL fiddle。