PostgreSQL
 sql >> Base de données >  >> RDS >> PostgreSQL

Agrégation de nuages ​​​​de points de coordonnées (x, y) dans PostgreSQL

Utilisez la fonction intégrée souvent négligée width_bucket() en combinaison avec votre agrégation :

Si vos coordonnées vont, disons, de 0 à 2000 et que vous souhaitez tout regrouper dans des carrés de 5 à des points uniques, je tracerais une grille de 10 (5*2) comme ceci :

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;

Pour minimiser l'erreur vous pouvez GROUP BY la grille comme illustré, mais enregistrez les coordonnées moyennes réelles :

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 démontrant les deux à côté.

Eh bien, ce cas particulier pourrait être plus simple :

...
GROUP  BY
       device_id
     , (pos_x / 10) * 10          -- truncates last digit of an integer
     , (pos_y / 10) * 10
...

Mais c'est simplement parce que la taille de la grille de démonstration de 10 correspond commodément au système décimal. Essayez la même chose avec une taille de grille de 17 ou quelque chose ...

Étendre aux horodatages

Vous pouvez étendre cette approche pour couvrir date et timestamp valeurs en les convertissant en époque unix (nombre de secondes depuis '1970-1-1') avec extract().

SELECT extract(epoch FROM '2012-10-01 21:06:38+02'::timestamptz);

Lorsque vous avez terminé, reconvertissez le résultat en timestamp with time zone :

SELECT timestamptz 'epoch' + 1349118398 * interval '1s';

Ou simplement to_timestamp() :

SELECT to_timestamp(1349118398);