Группировать по диапазону значений в BigQuery - PullRequest
0 голосов
/ 21 октября 2019

Есть ли способ в Bigquery сгруппировать не по абсолютному значению, а по диапазону значений?

У меня есть запрос, который просматривает таблицу продуктов с 4 различными числовыми группами по. То, что я ищу, - это эффективный способ группировки таким образом, как: группировка по «A ± 1000» и т. Д. Или «A ± 10% от A».

заранее спасибо,

Ответы [ 3 ]

1 голос
/ 21 октября 2019

Вы можете создать столбец как «именованный диапазон», а затем сгруппировать по столбцу. В качестве примера для вашего A+-1000 случая:

with data as ( 
select 100 as v union all
select 200 union all
select 2000 union all
select 2100 union all
select 2200 union all
select 4100 union all
select 8000 union all
select 8000
)
select count(v), ARRAY_AGG(v), ranges
FROM data, unnest([0, 2000, 4000, 6000, 8000]) ranges
WHERE data.v >= ranges - 1000 AND data.v < ranges + 1000
GROUP BY ranges

Вывод:

+-----+------------------------+--------+
| f0_ |          f1_           | ranges |
+-----+------------------------+--------+
|   2 |          ["100","200"] |      0 |
|   3 | ["2000","2100","2200"] |   2000 |
|   1 |               ["4100"] |   4000 |
|   2 |        ["8000","8000"] |   8000 |
+-----+------------------------+--------+
0 голосов
/ 21 октября 2019

Вы можете выполнять математические операции на GROUP BY, создавая группы по любым произвольным критериям.

Например:

WITH data AS (
  SELECT repo.name, COUNT(*) price
  FROM `githubarchive.month.201909` 
  GROUP BY 1
  HAVING price>100
)


SELECT FORMAT('range %i-%i', MIN(price), MAX(price)) price_range,  COUNT(*) c
FROM data
GROUP BY CAST(LOG(price) AS INT64)
ORDER BY MIN(price)

enter image description here

0 голосов
/ 21 октября 2019

Ниже приведен пример для BigQuery Standard SQL

#standardSQL
WITH `project.dataset.example` AS (
  SELECT * FROM 
  UNNEST([STRUCT<id INT64, price FLOAT64>
    (1, 15), (2, 50), (3, 125), (4, 150), (5, 175), (6, 250)
  ])
)
SELECT 
  CASE
    WHEN price > 0   AND price <= 100 THEN '  0 - 100'
    WHEN price > 100 AND price <= 200 THEN '100 - 200'
    ELSE '200+'
  END AS range_group, 
  COUNT(1) AS cnt 
FROM `project.dataset.example`
GROUP BY range_group
-- ORDER BY range_group

с результатом

Row range_group cnt  
1   0 - 100     2    
2   100 - 200   3    
3   200+        1    

Как вы можете видеть, в приведенном выше решении вам нужно создать оператор CASE для отражения ваших диапазонов - если выиметь несколько - это может быть довольно скучно - поэтому ниже приведено более общее (но более подробное) решение - и в нем используется недавно представленная RANGE_BUCKET функция

#standardSQL
WITH `project.dataset.example` AS (
  SELECT * FROM 
  UNNEST([STRUCT<id INT64, price FLOAT64>
    (1, 15), (2, 50), (3, 125), (4, 150), (5, 175), (6, 250)
  ])
), ranges AS (
  SELECT [100.0, 200.0] ranges_array
), temp AS (
  SELECT OFFSET, IF(prev_val = val, CONCAT(prev_val, ' - '), CONCAT(prev_val, ' - ', val)) rng FROM (
    SELECT OFFSET, IFNULL(CAST(LAG(val) OVER(ORDER BY OFFSET) AS STRING), '') prev_val, CAST(val AS STRING) AS val
    FROM ranges, UNNEST(ARRAY_CONCAT(ranges_array, [ARRAY_REVERSE(ranges_array)[OFFSET(0)]])) val WITH OFFSET
  )
)
SELECT 
  RANGE_BUCKET(price, ranges_array) range_group, 
  rng, 
  COUNT(1) AS cnt 
FROM `project.dataset.example`, ranges
JOIN temp ON RANGE_BUCKET(price, ranges_array) = OFFSET
GROUP BY range_group, rng
-- ORDER BY range_group

с результатом

Row range_group rng         cnt  
1   0               - 100   2    
2   1           100 - 200   3    
3   2           200 -       1    

Как вы можете видеть, во втором решении вам нужно определить свои диапазоны в ranges как простой массив, включающий ваши границы как SELECT [100.0, 200.0] ranges_array
Тогда temp выполнит все необходимые вычисления

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...