Sql 有没有更好的方法来计算中位数(而不是平均值)
假设我有以下表格定义:Sql 有没有更好的方法来计算中位数(而不是平均值),sql,postgresql,aggregate-functions,Sql,Postgresql,Aggregate Functions,假设我有以下表格定义: CREATE TABLE x (i serial primary key, value integer not null); 我想计算值的中值(不是平均值)。中位数是将集合划分为两个包含相同元素数的子集的值。如果元素数为偶数,则中位数是最低段的最大值和最大段的最小值的平均值。(详见维基百科。) 以下是我如何计算中位数的方法,但我想一定有更好的方法: SELECT AVG(values_around_median) AS median FROM ( SELEC
CREATE TABLE x (i serial primary key, value integer not null);
我想计算值的中值
(不是平均值)。中位数是将集合划分为两个包含相同元素数的子集的值。如果元素数为偶数,则中位数是最低段的最大值和最大段的最小值的平均值。(详见维基百科。)
以下是我如何计算中位数的方法,但我想一定有更好的方法:
SELECT AVG(values_around_median) AS median
FROM (
SELECT
DISTINCT(CASE WHEN FIRST_VALUE(above) OVER w2 THEN MIN(value) OVER w3 ELSE MAX(value) OVER w2 END)
AS values_around_median
FROM (
SELECT LAST_VALUE(value) OVER w AS value,
SUM(COUNT(*)) OVER w > (SELECT count(*)/2 FROM x) AS above
FROM x
GROUP BY value
WINDOW w AS (ORDER BY value)
ORDER BY value
) AS find_if_values_are_above_or_below_median
WINDOW w2 AS (PARTITION BY above ORDER BY value DESC),
w3 AS (PARTITION BY above ORDER BY value ASC)
) AS find_values_around_median
有什么想法吗?确实有一个更简单的方法。在Postgres中,您可以定义自己的聚合函数。不久前,我向PostgreSQL代码段库发布了用于执行中间值、模式和范围的函数
一个更简单的查询:
WITH y AS (
SELECT value, row_number() OVER (ORDER BY value) AS rn
FROM x
WHERE value IS NOT NULL
)
, c AS (SELECT count(*) AS ct FROM y)
SELECT CASE WHEN c.ct%2 = 0 THEN
round((SELECT avg(value) FROM y WHERE y.rn IN (c.ct/2, c.ct/2+1)), 3)
ELSE
(SELECT value FROM y WHERE y.rn = (c.ct+1)/2)
END AS median
FROM c;
要点
- 忽略空值
- 核心特性是,自版本8.4以来就一直存在
- 最后一个选择为不均匀数获取一行,为偶数获取两行的
avg()。结果为数字,四舍五入至小数点后3位
CREATE TEMP TABLE x (value int);
INSERT INTO x SELECT generate_series(1,10000);
INSERT INTO x VALUES (NULL),(NULL),(NULL),(3);
是的,在PostgreSQL 9.4中,您可以使用新引入的反向分布函数,这也是SQL标准中指定的有序集聚合函数
WITH t(value) AS (
SELECT 1 UNION ALL
SELECT 2 UNION ALL
SELECT 100
)
SELECT
percentile_cont(0.5) WITHIN GROUP (ORDER BY value)
FROM
t;
.对于谷歌来说:还有
安装此扩展后,可以在一行中计算中值。仅使用本机postgres函数的简单sql:
select
case count(*)%2
when 1 then (array_agg(num order by num))[count(*)/2+1]
else ((array_agg(num order by num))[count(*)/2]::double precision + (array_agg(num order by num))[count(*)/2+1])/2
end as median
from unnest(array[5,17,83,27,28]) num;
如果要处理空值,当然可以添加coalesce()或其他内容
CREATE TABLE array_table (id integer, values integer[]) ;
INSERT INTO array_table VALUES ( 1,'{1,2,3}');
INSERT INTO array_table VALUES ( 2,'{4,5,6,7}');
select id, values, cardinality(values) as array_length,
(case when cardinality(values)%2=0 and cardinality(values)>1 then (values[(cardinality(values)/2)]+ values[((cardinality(values)/2)+1)])/2::float
else values[(cardinality(values)+1)/2]::float end) as median
from array_table
或者,您可以创建一个函数,并在进一步查询中的任何位置使用它
CREATE OR REPLACE FUNCTION median (a integer[])
RETURNS float AS $median$
Declare
abc float;
BEGIN
SELECT (case when cardinality(a)%2=0 and cardinality(a)>1 then
(a[(cardinality(a)/2)] + a[((cardinality(a)/2)+1)])/2::float
else a[(cardinality(a)+1)/2]::float end) into abc;
RETURN abc;
END;
$median$
LANGUAGE plpgsql;
select id,values,median(values) from array_table
使用下面的函数查找第n个百分位
CREATE or REPLACE FUNCTION nth_percentil(anyarray, int)
RETURNS
anyelement as
$$
SELECT $1[$2/100.0 * array_upper($1,1) + 1] ;
$$
LANGUAGE SQL IMMUTABLE STRICT;
你的情况是第50百分位
使用下面的查询获取中值
SELECT nth_percentil(ARRAY (SELECT Field_name FROM table_name ORDER BY 1),50)
这将给你第50个百分位,基本上是中位数
希望这有帮助。平均值和平均值是同义词。你要问的是中位数:平均数就是数字之和除以计数。确实如此。有了这些信息,谷歌可能会证明@ChrisF的可能复制品——而不是复制品。不同的关系数据库管理系统。postgressql可能比mysql更好,因为它支持分析函数和用户定义的聚合