Postgres中的向量(数组)添加

时间:2014-10-15 17:37:44

标签: sql arrays postgresql vectorization

我有一个numeric[]个值的列,它们都具有相同的大小。我想采取他们的元素平均值。我的意思是平均值

{1, 2, 3}, {-1, -2, -3}, and {3, 3, 3}

应为{1, 1, 1}。同样令人感兴趣的是如何对这些元素进行求和,尽管我希望任何一个解决方案都能成为另一个解决方案。

(注意:数组的长度在一个表中是固定的,但在表之间可能会有所不同。所以我需要一个不假定一定长度的解决方案。)

我最初的猜测是我应该以某种方式使用unnest,因为应用于unnest列的numeric[]会使所有数组变平。因此,我想认为使用某种窗口函数+ group by可以很好地使用它来挑选每个数组的各个组件并将它们相加。

-- EXAMPLE DATA
CREATE TABLE A
  (vector numeric[])
;

INSERT INTO A
  VALUES
    ('{1, 2, 3}'::numeric[])
    ,('{-1, -2, -3}'::numeric[])
    ,('{3, 3, 3}'::numeric[])
;

3 个答案:

答案 0 :(得分:4)

我自己发现了一个解决方案,可能就是我将使用的解决方案。

首先,我们可以定义一个添加两个向量的函数:

CREATE OR REPLACE FUNCTION vec_add(arr1 numeric[], arr2 numeric[])
RETURNS numeric[] AS
$$
SELECT array_agg(result)
FROM (SELECT tuple.val1 + tuple.val2 AS result
      FROM (SELECT UNNEST($1) AS val1
                   ,UNNEST($2) AS val2
                   ,generate_subscripts($1, 1) AS ix) tuple
      ORDER BY ix) inn;
$$ LANGUAGE SQL IMMUTABLE STRICT;

和乘以常数的函数:

CREATE OR REPLACE FUNCTION vec_mult(arr numeric[], mul numeric)
RETURNS numeric[] AS
$$
SELECT array_agg(result)
FROM (SELECT val * $2 AS result
      FROM (SELECT UNNEST($1) AS val
                   ,generate_subscripts($1, 1) as ix) t
      ORDER BY ix) inn;
$$ LANGUAGE SQL IMMUTABLE STRICT;

然后我们可以使用PostgreSQL语句CREATE AGGREGATE直接创建vec_sum函数:

CREATE AGGREGATE vec_sum(numeric[]) (
    SFUNC = vec_add
    ,STYPE = numeric[]
);

最后,我们可以找到平均值:

SELECT vec_mult(vec_sum(vector), 1 / count(vector)) FROM A;

答案 1 :(得分:2)

我已经写了一个扩展来使用快速C函数进行向量加法(和减法,乘法,除法和幂)。您可以找到它on GithubPGXN

鉴于两个数组ab,您可以说vec_add(a, b)。您还可以将任一侧添加到标量,例如vec_add(a, 5)

如果您想要SUM聚合函数,则可以在aggs_for_vecs中找到该函数,也可以在PGXN上找到。

最后,如果您想总结单个数组的所有元素,可以使用aggs_for_arraysPGXN)。

答案 2 :(得分:1)

来自http://www.postgresql.org/message-id/4C2504A3.4090502@wp.pl

select avg(unnested) from (select unnest(vector) as unnested from A) temp;


编辑:我想我现在更好地理解了这个问题。

以下是一个可能的解决方案:https://stackoverflow.com/a/8767450/3430807我不认为它优雅,也不确定它会表现良好:

测试数据:

CREATE TABLE A
  (vector numeric[], id serial)
;

INSERT INTO A
  VALUES
    ('{1, 2, 3}'::numeric[])
    ,('{4, 5, 6}'::numeric[])
    ,('{7, 8, 9}'::numeric[])
;

查询:

select  avg(vector[temp.index])
from    A as a
join
    (select generate_subscripts(vector, 1) as index
              , id
        from    A) as temp on temp.id = a.id
group by temp.index