PostgreSQL
 sql >> Base de Dados >  >> RDS >> PostgreSQL

Agregando nuvens de pontos de coordenadas (x,y) no PostgreSQL


Use a função interna muitas vezes esquecida width_bucket() em combinação com sua agregação:

Se suas coordenadas vão de, digamos, 0 a 2000 e você deseja consolidar tudo dentro de quadrados de 5 para pontos únicos, eu colocaria uma grade de 10 (5*2) assim:
SELECT device_id
     , width_bucket(pos_x, 0, 2000, 2000/10) * 10 AS pos_x
     , width_bucket(pos_y, 0, 2000, 2000/10) * 10 AS pos_y
     , count(*) AS ct -- or any other aggregate
FROM   tbl
GROUP  BY 1,2,3
ORDER  BY 1,2,3;

Para minimizar o erro você pode GROUP BY a grade conforme demonstrado, mas salve as coordenadas médias reais:
SELECT device_id
     , avg(pos_x)::int AS pos_x   -- save actual averages to minimize error
     , avg(pos_y)::int AS pos_y   -- cast if you need to
     , count(*)        AS ct      -- or any other aggregate
FROM   tbl
GROUP  BY
       device_id
     , width_bucket(pos_x, 0, 2000, 2000/10) * 10  -- aggregate by grid
     , width_bucket(pos_y, 0, 2000, 2000/10) * 10
ORDER  BY 1,2,3;

sqlfiddle demonstrando ambos ao lado.

Bem, este caso em particular poderia ser mais simples:
...
GROUP  BY
       device_id
     , (pos_x / 10) * 10          -- truncates last digit of an integer
     , (pos_y / 10) * 10
...

Mas isso é apenas porque o tamanho da grade de demonstração de 10 corresponde convenientemente ao sistema decimal. Tente o mesmo com um tamanho de grade de 17 ou alguma coisa ...

Expandir para carimbos de data/hora


Você pode expandir essa abordagem para cobrir date e timestamp valores convertendo-os para unix epoch (número de segundos desde '1970-1-1') com extract().
SELECT extract(epoch FROM '2012-10-01 21:06:38+02'::timestamptz);

Quando terminar, converta o resultado de volta para timestamp with time zone :
SELECT timestamptz 'epoch' + 1349118398 * interval '1s';

Ou simplesmente to_timestamp() :
SELECT to_timestamp(1349118398);