聚合中位数

来自 PostgreSQL wiki
跳转到导航跳转到搜索

注意:此页面已过时。

要在 PostgreSQL 中获取中位数,请使用percentile_cont(0.5) WITHIN GROUP (ORDER BY num_value). 查看文档了解详细信息。但是,有序集聚合不能用作窗口函数,而下面给出的替代方法可以。


统计学中的中位数是将数据样本、总体或概率分布的上半部分与下半部分分隔开来的数值。中位数仅在有序的一维数据上定义,并且与任何距离度量无关。

可以使用quantile 扩展(使用 quantile(some_column,0.5))获得中位数的更高效实现。

median(numeric)

片段

聚合中位数

适用于 PostgreSQL

8.4

编写语言

SQL

依赖于


此代码段也是 ulib_agg 用户定义库 的一部分。

CREATE OR REPLACE FUNCTION _final_median(numeric[])
   RETURNS numeric AS
$$
   SELECT AVG(val)
   FROM (
     SELECT val
     FROM unnest($1) val
     ORDER BY 1
     LIMIT  2 - MOD(array_upper($1, 1), 2)
     OFFSET CEIL(array_upper($1, 1) / 2.0) - 1
   ) sub;
$$
LANGUAGE 'sql' IMMUTABLE;

CREATE AGGREGATE median(numeric) (
  SFUNC=array_append,
  STYPE=numeric[],
  FINALFUNC=_final_median,
  INITCOND='{}'
);

用法

SELECT median(num_value) AS median_value FROM t;

注意

mode()以及range()不同,由于动态限制和偏移,此代码段仅适用于 PostgreSQL 8.4 及更高版本。

注意:此版本不考虑 NULL 值,例如,median('{1,NULL,NULL}') 为 NULL。

此实现也相当慢。对于大约 500K 个元素的表,orafce 实现的速度快约 400 倍。

median(anyelement)

片段

聚合中位数

适用于 PostgreSQL

8.4

编写语言

SQL

依赖于


这是对上述代码的改进,有两个创新之处:它通过 anyelement 接受任何数值。它还会剔除空值,这需要额外的处理,因此速度会稍慢。此外,我将其改为返回 float8,这主要是一种审美上的偏好。此代码段也是 ulib_agg 用户定义库 的一部分。

CREATE FUNCTION _final_median(anyarray) RETURNS float8 AS $$ 
  WITH q AS
  (
     SELECT val
     FROM unnest($1) val
     WHERE VAL IS NOT NULL
     ORDER BY 1
  ),
  cnt AS
  (
    SELECT COUNT(*) as c FROM q
  )
  SELECT AVG(val)::float8
  FROM 
  (
    SELECT val FROM q
    LIMIT  2 - MOD((SELECT c FROM cnt), 2)
    OFFSET GREATEST(CEIL((SELECT c FROM cnt) / 2.0) - 1,0)  
  ) q2;
$$ LANGUAGE sql IMMUTABLE;

CREATE AGGREGATE median(anyelement) (
  SFUNC=array_append,
  STYPE=anyarray,
  FINALFUNC=_final_median,
  INITCOND='{}'
);

用法

SELECT median(value) AS median_value FROM t;

注意

如果你仍然使用 PostgreSQL 8.3 或更低版本,以下是对上述中位数聚合的修改将起作用(注意,首先必须定义 unnest 并创建 PL/pgsql 语言(CREATE LANGUAGE plpgsql;)如果你还没有。另请注意,在未排序的数组上可能存在更高效的中位数函数,例如,使用 选择算法 的 O(N) 与此先进行比较排序的数组的 O(N lg N)。

CREATE OR REPLACE FUNCTION final_median(anyarray) RETURNS float8 AS
$$ 
DECLARE
  cnt INTEGER;
BEGIN
  cnt := (SELECT count(*) FROM unnest($1) val WHERE val IS NOT NULL);
  RETURN (SELECT avg(tmp.val)::float8 
            FROM (SELECT val FROM unnest($1) val
                    WHERE val IS NOT NULL 
                    ORDER BY 1 
                    LIMIT 2 - MOD(cnt, 2) 
                    OFFSET CEIL(cnt/ 2.0) - 1
                  ) AS tmp
         );
END
$$ LANGUAGE plpgsql;

CREATE AGGREGATE median(anyelement) (
  SFUNC=array_append,
  STYPE=anyarray,
  FINALFUNC=final_median,
  INITCOND='{}'
);

外部实现

对于“外部”中位数计算,有很多编译版本

另请参阅

聚合众数 聚合中位数