SQL查询的排名来计算子组的行列中位(SQL ranking query to compute ra

2019-08-21 18:33发布

我要计算中位数y在这个简单的子组 xy_table

  x | y --groups--> gid |   x | y --medians-->  gid |   x | y
-------             -------------               -------------
0.1 | 4             0.0 | 0.1 | 4               0.0 | 0.1 | 4
0.2 | 3             0.0 | 0.2 | 3                   |     |
0.7 | 5             1.0 | 0.7 | 5               1.0 | 0.7 | 5
1.5 | 1             2.0 | 1.5 | 1                   |     |
1.9 | 6             2.0 | 1.9 | 6                   |     |
2.1 | 5             2.0 | 2.1 | 5               2.0 | 2.1 | 5
2.7 | 1             3.0 | 2.7 | 1               3.0 | 2.7 | 1

在这个例子中每一个x是独一无二的,该表已经被排序x 。 我现在想GROUP BY round(x)并得到持有的中位数的元组y每个小组。

我已经可以计算这个排名查询整个表位数:

SELECT a.x, a.y FROM xy_table a,xy_table b
WHERE a.y >= b.y
GROUP BY a.x, a.y
HAVING count(*) = (SELECT round((count(*)+1)/2) FROM xy_table)

输出: 0.1, 4.0

但我并没有成功编写查询来计算子组的中位数。

注意:我没有median()可用聚合函数。 另外,请不要提出具有特殊的解决方案PARTITIONRANK ,或QUANTILE语句(如类似发现,但也特定于供应商做题 )。 我需要普通的SQL(即不兼容对于SQLite median()函数)

编辑:我其实是在寻找Medoid ,而不是平均 。

Answer 1:

我建议做计算在你的编程语言:

for each group:
  for each record_in_group:
    append y to array
  median of array

但是,如果你坚持使用SQLite,您可以通过命令各组y并选择在这样中间的记录http://sqlfiddle.com/#!5/d4c68/55/0 :

更新 :只有更大的“中间”值importand甚至NR。 行的,所以没有avg()需要:

select groups.gid,
  ids.y median
from (
  -- get middle row number in each group (bigger number if even nr. of rows)
  -- note the integer divisions and modulo operator
  select round(x) gid,
    count(*) / 2 + 1 mid_row_right
  from xy_table
  group by round(x)
) groups
join (
  -- for each record get equivalent of
  -- row_number() over(partition by gid order by y)
  select round(a.x) gid,
    a.x,
    a.y,
    count(*) rownr_by_y
  from xy_table a
  left join xy_table b
    on round(a.x) = round (b.x)
    and a.y >= b.y
  group by a.x
) ids on ids.gid = groups.gid
where ids.rownr_by_y = groups.mid_row_right


Answer 2:

OK,这依赖于一个临时表:

create temporary table tmp (x float, y float);

insert into tmp
  select * from xy_table order by round(x), y

但是,你可能对数据的你有兴趣在一个范围内创建此。另一种方法是,以确保xy_table过这样的排序顺序,而不是只对订购的x 。 这样做的原因是SQLite的缺乏行编号能力。

然后:

select tmp4.x as gid, t.* from (
  select tmp1.x, 
         round((tmp2.y + coalesce(tmp3.y, tmp2.y)) / 2) as y -- <- for larger of the two, change to: (case when tmp2.y > coalesce(tmp3.y, 0) then tmp2.y else tmp3.y end)
  from (
    select round(x) as x, min(rowid) + (count(*) / 2) as id1, 
           (case when count(*) % 2 = 0 then min(rowid) + (count(*) / 2) - 1 
                 else 0 end) as id2
    from (  
      select *, rowid from tmp
    ) t
    group by round(x)
  ) tmp1
  join tmp tmp2 on tmp1.id1 = tmp2.rowid
  left join tmp tmp3 on tmp1.id2 = tmp3.rowid
) tmp4
join xy_table t on tmp4.x = round(t.x) and tmp4.y = t.y

如果你想治疗位数作为两个中间值,作为@Aprillion已经指出这不符合该定义的大,那么你只会把两者中的较大y值,而不是他们的平均,对第三行查询。



文章来源: SQL ranking query to compute ranks and median in sub groups