product_name | price |
---|---|
商品A | 1.5 |
商品A | 1.0 |
商品A | 2.0 |
商品A | 2.1 |
商品A | 1.6 |
商品B | 3.0 |
商品B | 3.0 |
商品B | 3.1 |
商品B | 3.5 |
商品B | 3.5 |
商品B | 3.6 |
商品B | 3.9 |
#建表语句
drop table if exists table_name;
create table table_name
(
id bigint comment 'id'
,product_name string comment '商品名称'
,price decimal(18,2) comment '价格'
)
;
insert overwrite table table_name
values
(1,"商品A",1.5)
,(2,"商品A",1)
,(3,"商品A",2)
,(4,"商品A",2.1)
,(5,"商品A",1.6)
,(6,"商品B",3)
,(7,"商品B",3)
,(8,"商品B",3.1)
,(9,"商品B",3.5)
,(10,"商品B",3.5)
,(11,"商品B",3.6)
,(12,"商品B",3.9)
;
需求:求不同商品售价的中位数,以及所有商品售价的中位数
法一:percentile_approx()
函数求中位数
##分组后求中位数
select product_name
,percentile_approx(price,0.5,10000000) as med_price
from
table_name
group by product_name
;
输出不同商品售价的中位数结果:
product_name | med_price |
---|---|
商品A | 1.55 |
商品B | 3.2 |
##不分组直接求中位数
select
percentile_approx(price,0.5,10000000) as med_price
from
table_name;
输出所有商品售价的中位数结果:
med_price |
---|
2.55 |
percentile_approx(col, p,B)
p的取值为0-1,若为0.2,则结果为二分位数。参数B控制内存消耗的近似精度,B越大,结果的准确度越高。默认为10,000。当col字段中的distinct值的个数小于B时,结果就为准确的百分位数。
在不分组的情况下,也可一次性求多个分位数,percentile_approx(col,array(0.05,0.5,0.95),9999),结果返回一个分位数列表。
percentile(col, p,B)
col是要计算的列(值必须为int类型)
法二:排序后打分位点
##分组排序后计算中位数
#排序
with tmp1 as
(
select id
,product_name
,price
,row_number() over (partition by product_name order by price asc) as rn
from
table_name)
#打分位数标签
,tmp2 as
(
select a.*
,b.cnt
,case when a.rn=ceil(b.cnt*0.5) then 'qt50'
else null
end as quantile_flag
from
tmp1 a
left join
(select product_name,count(*) as cnt from tmp1 group by product_name) b
on a.product_name=b.product_name
)
#计算分位数标签对应的分为数值
select product_name
,max(if(quantile_flag='qt50', price, null)) as qt50
from
tmp2
group by product_name
;
输出不同商品售价的中位数结果:
product_name | med_price |
---|---|
商品A | 1.60 |
商品B | 3.5 |
##不分组排序直接求中位数
with tmp1 as
(
select id
,product_name
,price
,row_number() over (order by price asc) as rn
from
hvprd_dmcp_udl.ptt_temp_1101
)
select
price
from
tmp1 a
left join
(select count(*) as cnt from tmp1) b
on 1=1
where a.rn=ceil(b.cnt*0.5)
;
med_price |
---|
3.00 |
两种方式计算出来的结果出现了差异,这可能是由于法一percentile_approx计算百分数的方式与法二不一致导致。计算百分位数的时候主要有这样几种方法:
(1)最近序数方法(The Nearest Rank method)
(2)在最近序数间线性插值的方法(The Linear Interpolation Between Closest Ranks method)
(3)权重百分位数方法(The Weighted Percentile method)
(4)微软excel表格算法(Microsoft Excel method)
(5)NIST方法(NIST method)
具体差异以后有机会再研究吧。
关于百分位的含义,可参考这篇文章