Как рассчитать квартили по группам? - PullRequest
0 голосов
/ 28 сентября 2018

Допустим, у меня есть таблица

VAL     PERSON
  1          1
  2          1
  3          1
  4          1
  2          2
  4          2
  6          2
  3          3
  6          3
  9          3
  12         3
  15         3

И я хотел бы рассчитать квартили для каждого человека.

Я понимаю, что могу легко рассчитать их для одного человека следующим образом:

SELECT 
    VAL,
    NTILE(4) OVER(ORDER BY VAL) AS QUARTILE
WHERE PERSON = 1;

Даст мне желаемые результаты:

VAL    QUARTILE
1      1
2      2
3      3
4      4

Проблема в том, что я 'Я хотел бы сделать это для каждого человека.Я знаю, что-то вроде этого сделало бы эту работу:

SELECT 
    PERSON,
    VAL,
    NTILE(4) OVER(ORDER BY VAL) AS QUARTILE
WHERE PERSON = 1
UNION
SELECT 
    PERSON,
    VAL,
    NTILE(4) OVER(ORDER BY VAL) AS QUARTILE
WHERE PERSON = 2
UNION
SELECT 
    PERSON,
    VAL,
    NTILE(4) OVER(ORDER BY VAL) AS QUARTILE
WHERE PERSON = 3
UNION
SELECT 
    PERSON,
    VAL,
    NTILE(4) OVER(ORDER BY VAL) AS QUARTILE
WHERE PERSON = 4

Но что, если на столе появился новый человек?Тогда мне придется изменить код SQL.Есть предложения?

Ответы [ 2 ]

0 голосов
/ 28 сентября 2018

ntile() не очень хорошо справляется со связями.Это можно легко увидеть на примере:

select v.x, ntile(2) over (order by x) as tile
from (values (1), (1), (1), (1)) v(x);

, который возвращает:

x tile
1   1
1   1
1   2
1   2

То же значение.Разные плитки.Это ухудшается, если вы отслеживаете, в каком тайле находится значение. Разные строки могут иметь разные тайлы в разных прогонах одного и того же запроса - даже если данные не меняются.

Обычно вам нужностроки с одинаковым значением имеют одинаковый квартиль, даже если плитки не одинакового размера.По этой причине я рекомендую вместо явного вычисления использовать rank():

select t.*,
       ((seqnum - 1) * 4 / cnt) + 1 as quartile
from (select t.*,
             rank() over (partition by person order by val) as seqnum,
             count(*) over (partition by person) as cnt
      from t
     ) t;

Если вы действительно хотите, чтобы значения были разбиты по плиткам, используйте row_number() вместо rank().

0 голосов
/ 28 сентября 2018

Почему бы вам не попробовать использовать раздел по.

SELECT 
  PERSON,
  VAL,
  NTILE(4) OVER(PARTITION BY PERSON ORDER BY VAL) AS QUARTILE;
FROM TABLE 

Привет

...