我有一个包含两列wind_speed
和wind_direction
的表。我想要一个自定义的聚集函数,该函数将返回平均值wind_speed
和wind_direction
。 wind_speed
和wind_direction
共同定义了一个向量,其中wind_speed
是向量的大小,wind_direction
是向量的方向。 avg_wind_direction
函数应返回平均值wind_speed
作为幅度,wind_direction
作为平均值矢量的方向。
SELECT avg_wind_direction(wind_speed, wind_direction)
FROM sometable
GROUP BY location;
答案 0 :(得分:1)
因此,我已经能够创建一个进行向量平均的聚集函数。假设矢量与极坐标不同,在极坐标中,角度在度中。
DROP AGGREGATE IF EXISTS vector_avg(float, float) CASCADE;
DROP TYPE IF EXISTS vector_sum CASCADE;
DROP TYPE IF EXISTS avg_vector CASCADE;
CREATE TYPE vector_sum AS (x float, y float, count int);
CREATE TYPE avg_vector AS (magnitude float, direction float);
CREATE OR REPLACE FUNCTION sum_vector (vectors vector_sum, magnitude float, direction float)
RETURNS vector_sum LANGUAGE sql STRICT AS
'SELECT vectors.x + (magnitude * cos(direction * (pi() / 180))), vectors.y + (magnitude * sin(direction * (pi() / 180))), vectors.count + 1';
CREATE OR REPLACE FUNCTION avg_vector_finalfunc(vectors vector_sum) RETURNS avg_vector AS
$$
DECLARE
x float;
y float;
BEGIN
BEGIN
IF vectors.count = 0 THEN
RETURN (NULL, NULL)::avg_vector;
END IF;
x := (vectors.x/vectors.count);
y := (vectors.y/vectors.count);
-- This means the vector is null vector
-- Please see: https://math.stackexchange.com/a/3682/10842
IF x = 0 OR y = 0 THEN
RETURN (0, 0)::avg_vector;
END IF;
RETURN (sqrt(power(x, 2) + power(y, 2)), atan(y/x) * (180 / pi()))::avg_vector;
EXCEPTION WHEN others THEN
RETURN (NULL, NULL)::avg_vector;
END;
END;
$$
LANGUAGE 'plpgsql'
RETURNS NULL ON NULL INPUT;
CREATE AGGREGATE vector_avg (float, float) (
sfunc = sum_vector
, stype = vector_sum
, finalfunc = avg_vector_finalfunc
, initcond = '(0.0, 0.0, 0)'
);
测试:
DROP TABLE t;
CREATE TEMP TABLE t(speed float, direction float);
INSERT INTO t VALUES (23, 334), (20, 3), (340, 67);
测试:
SELECT (vector_avg(speed, direction)).magnitude AS speed, (vector_avg(speed, direction)).direction AS direction FROM t;
结果:
+-----------------+-------------------+
| speed | direction |
+=================+===================+
| 108.44241888507 | 0.972468335643555 |
+-----------------+-------------------+
删除所有行:
DELETE FROM t;
SELECT (vector_avg(speed, direction)).magnitude AS speed, (vector_avg(speed, direction)).direction AS direction FROM t;
结果:
+---------+-------------+
| speed | direction |
+=========+=============+
| <null> | <null> |
+---------+-------------+
答案 1 :(得分:1)
首先抱歉,如果我违反这里的任何张贴规则,第一次张贴者以及所有这些内容。
我的diy气象站希望将以上答案与timescaledb一起添加到postgres中,但事实证明该功能不是并行安全的。同样,使用atan不会产生正确的答案。
所以这是我的修改版本,我认为应该是并行安全的,而应使用atan2。
DROP AGGREGATE IF EXISTS vector_avg(float, float) CASCADE;
DROP TYPE IF EXISTS vector_sum CASCADE;
DROP TYPE IF EXISTS avg_vector CASCADE;
CREATE TYPE vector_sum AS (x float, y float, count int);
CREATE TYPE avg_vector AS (magnitude float, direction float);
CREATE OR REPLACE FUNCTION sum_vector (vectors vector_sum, magnitude float, direction float)
RETURNS vector_sum LANGUAGE sql PARALLEL SAFE STRICT AS
'SELECT vectors.x + (magnitude * cos(radians(direction))), vectors.y + (magnitude * sin(radians(direction))), vectors.count + 1';
CREATE OR REPLACE FUNCTION combine_sum (part1 vector_sum , part2 vector_sum)
RETURNS vector_sum LANGUAGE sql PARALLEL SAFE STRICT AS
'SELECT (part1.x+part2.x)/2,(part1.y+part2.y)/2,part1.count+part2.count';
CREATE OR REPLACE FUNCTION avg_vector_finalfunc(vectors vector_sum)
RETURNS avg_vector
AS
$$
DECLARE
x float;
y float;
d float;
BEGIN
BEGIN
IF vectors.count = 0 THEN
RETURN (NULL, NULL)::avg_vector;
END IF;
x := (vectors.x/vectors.count);
y := (vectors.y/vectors.count);
-- This means the vector is null vector
-- Please see: https://math.stackexchange.com/a/3682/10842
IF x = 0 OR y = 0 THEN
RETURN (0, 0)::avg_vector;
END IF;
d:=degrees(atan2(y,x));
-- atan2 returns negative result for angles > 180
IF d < 0 THEN
d := d+360;
END IF;
RETURN (sqrt(power(x, 2) + power(y, 2)), d )::avg_vector;
EXCEPTION WHEN others THEN
RETURN (NULL, NULL)::avg_vector;
END;
END;
$$
LANGUAGE 'plpgsql'
PARALLEL SAFE
RETURNS NULL ON NULL INPUT;
CREATE AGGREGATE vector_avg (float, float) (
sfunc = sum_vector
, stype = vector_sum
, combinefunc = combine_sum
, finalfunc = avg_vector_finalfunc
, initcond = '(0.0, 0.0, 0)'
, PARALLEL = SAFE
通过一个非常小的样本进行测试:
psql -d weather -c "select * from windavgtest;"
time | direction | speed
-------------------------------+-----------+-------
2019-08-01 16:51:53.199357+00 | 170 | 1
2019-08-01 16:51:54.388392+00 | 170 | 1
2019-08-01 16:51:55.335034+00 | 170 | 1
2019-08-01 16:51:56.362812+00 | 170 | 1
2019-08-01 16:52:07.191919+00 | 190 | 1
2019-08-01 16:52:08.250756+00 | 190 | 1
2019-08-01 16:52:09.193265+00 | 190 | 1
2019-08-01 16:52:10.224283+00 | 190 | 1
(8 rows)
产量:
psql -d weather -c "select round((vector_avg(speed, direction)).direction) AS wdirection from windavgtest;
"
wdirection
------------
180
(1 row)