如何在AWS Redshift中计算中位数?

时间:2014-01-07 02:20:31

标签: amazon-redshift

大多数数据库都有内置函数来计算中位数但我在Amazon Redshift中没有看到任何中位数。

您可以使用nth_value()和count()分析函数的组合来计算中值,但这似乎很笨拙。如果分析数据库没有内置计算中位数的方法,我会很惊讶,所以我假设我错过了一些东西。

http://docs.aws.amazon.com/redshift/latest/dg/r_Examples_of_NTH_WF.html http://docs.aws.amazon.com/redshift/latest/dg/c_Window_functions.html

4 个答案:

答案 0 :(得分:5)

截至2014-10-17,Redshift支持MEDIAN窗口功能:

# select min(median) from (select median(num) over () from temp);
 min 
-----
 4.0

答案 1 :(得分:4)

尝试NTILE功能。

您可以将数据划分为2个排名组,并从第一组中选择最小值。这是因为在具有奇数个值的数据集中,第一个ntile将比第二个具有多1个值。这种近似应该适用于大型数据集。

create table temp (num smallint);
insert into temp values (1),(5),(10),(2),(4);

select num, ntile(2) over(order by num desc) from temp ;
 num | ntile 
-----+-------
  10 |     1
   5 |     1
   4 |     1
   2 |     2
   1 |     2

select min(num) as median from (select num, ntile(2) over(order by num desc) from temp) where ntile = 1;
 median 
--------
      4

答案 2 :(得分:1)

我也遇到了这个困难,但得到了亚马逊的一些帮助。自2014-06-30版本的Redshift以来,您可以使用PERCENTILE_CONTPERCENTILE_DISC窗口函数执行此操作。

它们使用起来有点奇怪,因为它们会将每个行的中位数(或您选择的任何百分位数)固定。你把它放在一个子查询中,然后取中间列的MIN(或其他)。

# select count(num), min(median) as median from (select num, percentile_cont (0.5) within group (order by num) over () as median from temp); count | median -------+-------- 5 | 4.0

(它之所以复杂,是因为窗口函数也可以自己进行迷你分组,并命令一次性给出许多组的中位数,以及其他技巧。)

在偶数个值的情况下,CONT(inuous)将在两个中间值之间进行插值,其中DISC(rete)将选择其中一个值。

答案 3 :(得分:0)

如果我正在寻找足够接近的答案,我通常会使用NTILE函数将数据拆分为两组。但是,如果我想要精确的中位数(例如偶数行的中点),我会使用AWS Redshift Discussion Forum上建议的技术。

此技术按升序和降序对行进行排序,然后如果有奇数行,则返回中间行的平均值(即row_num_asc = row_num_desc),这只是中间行本身

CREATE TABLE temp (num SMALLINT);

INSERT INTO temp VALUES (1),(5),(10),(2),(4);

SELECT
  AVG(num) AS median
FROM
(SELECT
  num,
  SUM(1) OVER (ORDER BY num ASC) AS row_num_asc,
  SUM(1) OVER (ORDER BY num DESC) AS row_num_desc
FROM
  temp) AS ordered
WHERE
  row_num_asc IN (row_num_desc, row_num_desc - 1, row_num_desc + 1);

 median 
--------
      4

如果有偶数行,则返回两个中间行的平均值。

INSERT INTO temp VALUES (9);

SELECT
  AVG(num) AS median
FROM
(SELECT
  num,
  SUM(1) OVER (ORDER BY num ASC) AS row_num_asc,
  SUM(1) OVER (ORDER BY num DESC) AS row_num_desc
FROM
  temp) AS ordered
WHERE
  row_num_asc IN (row_num_desc, row_num_desc - 1, row_num_desc + 1);

 median 
--------
    4.5