Создать столбец для номера квантиля значения в BigQuery - PullRequest
0 голосов
/ 30 августа 2018

У меня есть таблица с двумя столбцами: id и score. Я хотел бы создать третий столбец, равный квантилю, в который попадает score индивидуума. Я хотел бы сделать это в стандартной SQL BigQuery.

Вот my_table:

+----+--------+
| id | score  |
+----+--------+
|  1 |      2 |
|  2 |     13 |
|  3 |     -2 |
|  4 |      7 |
+----+--------+

и после этого я хотел бы иметь следующую таблицу (пример показан с квартилями, но меня интересуют квартили / квинтили / децили)

+----+--------+----------+
| id | score  | quaRtile |
+----+--------+----------+
|  1 |      2 |        2 |
|  2 |     13 |        4 |
|  3 |     -2 |        1 |
|  4 |      7 |        3 |
+----+--------+----------+

Было бы замечательно, если бы это работало на 100 миллионов строк. Я посмотрел вокруг и увидел пару решений, которые, кажется, используют устаревшие sql , а решения , использующие функции RANK(), не работают для действительно больших наборов данных. Спасибо!

Ответы [ 2 ]

0 голосов
/ 30 августа 2018

Вывод подсказывает мне rank():

SELECT *, RANK() OVER (ORDER BY score) as quantile 
FROM table t
ORDER BY id;
0 голосов
/ 30 августа 2018

Если я правильно понимаю, вы можете использовать ntile(). Например, если вы хотите значение от 1 до 4, вы можете сделать:

select t.*, ntile(4) over (order by score) as tile
from t;

Если вы хотите перечислить значения, используйте rank() или dense_rank():

select t.*, rank() over (order by score) as tile
from t;

Я вижу, ваша проблема заключается в том, чтобы заставить код работать, потому что BigQuery имеет тенденцию исчерпывать ресурсы без partition by. Один из способов - разбить счет на разные группы. Я думаю, что эта логика делает то, что вы хотите:

select *, 
       ( (count(*) over (partition by cast(score / 1000 as int64) order by cast(score / 1000 as int64)) -
          count(*) over (partition by cast(score / 1000 as int64))
         ) +
         rank() over (partition by cast(score / 1000 as int64) order by regi_id)
      ) as therank,
      -- rank() over (order by score) as therank
from t;

Это разбивает счет на 1000 групп (возможно, это слишком много для целого числа). А затем восстанавливает рейтинг.

Если ваша оценка имеет относительно низкое количество элементов, то join с агрегацией работает:

select t.*, (running_cnt - cnt + 1) as therank
from t join
     (select score, count(*) as cnt, sum(count(*)) over (order by score) as running_cnt
      from t
      group by score
     ) s
     on t.score = s.score;

Когда у вас есть rank() (или row_number()), вы можете легко вычислить плитки самостоятельно (подсказка: деление).

...