Custom PostgreSQL aggregate for circular average

99封情书 提交于 2019-12-01 23:36:55

You can make use of an ARRAY type internally. Argument type can still be any numeric type. Demonstrating with float (= double precision):

CREATE OR REPLACE FUNCTION f_circavg (float[], float)
  RETURNS float[] LANGUAGE sql STRICT AS
'SELECT ARRAY[$1[1] + sin($2), $1[2] + cos($2), 1]';

CREATE OR REPLACE FUNCTION f_circavg_final (float[])
  RETURNS float  LANGUAGE sql AS
'SELECT CASE WHEN $1[3] > 0 THEN atan2($1[1], $1[2]) END';

CREATE AGGREGATE circavg (float) (
   sfunc     = f_circavg
 , stype     = float[]
 , finalfunc = f_circavg_final
 , initcond  = '{0,0,0}'
);

The transition function f_circavg() is defined STRICT, so it ignores rows with NULL input. It also sets a third array element to identify sets with one or more input rows - else the CASE the final function returns NULL.

Temporary table for testing:

CREATE TEMP TABLE t (x float);
INSERT INTO t VALUES (2), (NULL), (3), (4), (5);

I threw in a NULL value to also test the STRICT magic. Call:

SELECT circavg(x) FROM t;

       circavg
-------------------
 -2.78318530717959

Cross check:

SELECT atan2(sum(sin(x)), sum(cos(x))) FROM t;

       atan2
-------------------
 -2.78318530717959

Returns the same. Seems to work. In test with a bigger table the last expression with regular aggregate functions was 4x faster than the custom aggregate.

Test for zero input rows / only NULL input:

SELECT circavg(x) FROM t WHERE false;     -- no input rows
SELECT circavg(x) FROM t WHERE x IS NULL; -- only NULL input

Returns NULL in both cases.

PostgreSQL provides you with a set of geometry types, POINT being the fundamental one.
Use this type as the input parameter for your function.

You can create your custom type as an alternative if you prefer.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!