2013-04-11 2 views
8

Я хочу, чтобы вычислить Median из y в подгруппах этого простого xy_table:SQL ранжирования запроса для вычисления рангов и медианы в подгруппах

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(). Также не предлагайте решения со специальными PARTITION, RANK или QUANTILE заявлениями (как указано в аналогичных, но также и с конкретными поставщиками SO questions). Мне нужен простой SQL (то есть, совместимый с SQLite без median() функции)

Edit: Я был на самом деле ищу Medoid и не Median.

+0

Какова связь между 2-й и 3-й таблицами в вашем примере? 1-й «gid» изменен на 0.1, а значения 'x' или' y' не являются медианами для группы 0 – Aprillion

+0

Извините, у меня была опечатка. Третья таблица должна содержать медианы групп 'gid' (группы ID). И я полагаю, что медиана [4,3] равна 4 (большее значение) в таких случаях. – Juve

+0

медиана [4,3] обычно равна 3,5, как указано в первом абзаце вашей ссылки на Википедию, вы явно хотите получить большую ценность? – Aprillion

ответ

3

Я предлагаю делать вычисления на языке программирования:

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 for even 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 
+0

Thx, ваш ответ является основой для моего решения. Почему бы просто не использовать 'SELECT round (x) gid, 1+ (count (*))/2 mid_row', чтобы получить« среднюю »строку? Это работает для меня, по крайней мере. – Juve

+0

yep это осталось от вычисления средней точки левой стороны :) – Aprillion

0

КИ, это зависит от временной таблицы:.

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 вместо их среднего значения в третьей строке запроса.

Смежные вопросы