【问题标题】:Postgres aggregrate function for calculating vector average of wind speed (vector magnitude) and wind direction (vector direction)Postgres聚合函数,用于计算风速(矢量幅度)和风向(矢量方向)的矢量平均值
【发布时间】:2019-02-22 14:57:16
【问题描述】:

我有一个包含两列 wind_speedwind_direction 的表格。我想要一个自定义聚合函数,它会返回平均 wind_speedwind_directionwind_speedwind_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;

相关问题:Custom PostgreSQL aggregate for circular average

【问题讨论】:

  • 那么该聚合究竟要计算什么?请edit您的问题(通过单击其下方的edit 链接)并添加一些sample data 和基于该数据的预期输出。 Formatted textno screen shots.
  • 为什么不使用普通的avg两次?
  • 对不起,我忘了提到它是矢量平均,因为风速和风向组合是一个矢量,其中速度是矢量的大小,方向是矢量的方向,以度为单位。正常的平均是行不通的。
  • 也许看看这个扩展 - pgxn.org/dist/aggs_for_vecs - 应该计算向量上的平均值和其他一些聚合
  • 请首先提供您的 Postgres 版本、表定义、一些示例值和预期结果。它必须是 single 聚合函数吗? (为什么?)或者你可以组合多个聚合函数?

标签: sql postgresql aggregate-functions


【解决方案1】:

如果我在这里违反任何发布规则,第一次发帖等等,首先抱歉。

想将上述答案与我的 diy 气象站的 postgres 添加的 timescaledb 一起使用,但事实证明该功能不是并行安全的。同样, 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)

【讨论】:

  • 是的,你是对的。有一些错误。我已经修好了那些。请查看我的更新答案。
【解决方案2】:

所以我已经能够创建一个聚合函数来进行向量平均。它假设向量是极坐标,角度是度数,而不是弧度。

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;
        maybe_neg_angle numeric;
        angle numeric;

        v_state   TEXT;
        v_msg     TEXT;
        v_detail  TEXT;
        v_hint    TEXT;
        v_context TEXT;
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
            RAISE NOTICE 'X or Y component is 0. Returning NULL vector';
            RETURN (0.0, 0.0)::avg_vector;
        END IF;

        maybe_neg_angle := atan2(CAST(y AS NUMERIC), CAST(x AS NUMERIC)) * (180.0 / pi());
        angle := MOD(CAST((maybe_neg_angle + 360.0) AS NUMERIC), CAST(360.0 AS NUMERIC));

        RETURN (sqrt(power(x, 2) + power(y, 2)), angle)::avg_vector;

    EXCEPTION WHEN others THEN
        RAISE NOTICE 'Exception was raised. Returning just NULL';
        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>      |
+---------+-------------+

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-05-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-06-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多