在每个bin中创建具有唯一值的bin

时间:2015-08-16 20:36:20

标签: sql postgresql

我想以这样的方式对数字列(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;

1 个答案:

答案 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