百分位数的另一种方法?
Posted
技术标签:
【中文标题】百分位数的另一种方法?【英文标题】:Another approach to percentiles? 【发布时间】:2013-05-08 21:48:33 【问题描述】:我有一个数据集,它基本上由作业批次列表、每个批次中包含的作业数量以及每个作业批次的持续时间组成。这是一个示例数据集:
CREATE TABLE test_data
(
batch_id NUMBER,
job_count NUMBER,
duration NUMBER
);
INSERT INTO test_data VALUES (1, 37, 9);
INSERT INTO test_data VALUES (2, 47, 4);
INSERT INTO test_data VALUES (3, 66, 6);
INSERT INTO test_data VALUES (4, 46, 6);
INSERT INTO test_data VALUES (5, 54, 1);
INSERT INTO test_data VALUES (6, 35, 1);
INSERT INTO test_data VALUES (7, 55, 9);
INSERT INTO test_data VALUES (8, 82, 7);
INSERT INTO test_data VALUES (9, 12, 9);
INSERT INTO test_data VALUES (10, 52, 4);
INSERT INTO test_data VALUES (11, 3, 9);
INSERT INTO test_data VALUES (12, 90, 2);
现在,我想计算持续时间字段的一些百分位数。通常,这是通过以下方式完成的:
SELECT
PERCENTILE_DISC( 0.75 )
WITHIN GROUP (ORDER BY duration ASC)
AS third_quartile
FROM
test_data;
(给出 9 的结果)
我的问题是我们不想根据批次获得百分位数,我想根据个人工作获得百分比。通过生成 job_count 的运行总数,我可以很容易地手动计算出来:
SELECT
batch_id,
job_count,
SUM(
job_count
)
OVER (
ORDER BY duration
ROWS UNBOUNDED PRECEDING
)
AS total_jobs,
duration
FROM
test_data
ORDER BY
duration ASC;
BATCH_ID JOB_COUNT TOTAL_JOBS DURATION
6 35 35 1
5 54 89 1
12 90 179 2
2 47 226 4
10 52 278 4
3 66 344 6
4 46 390 6
8 82 472 7
9 12 484 9
1 37 521 9
11 3 524 9
7 55 579 9
因为我有 579 个工作,所以第 75 个百分位将是工作 434。查看上面的结果集,对应的持续时间为 7,与标准函数的作用不同。
基本上,我想将批次中的每个作业视为单独的观察,并根据这些而不是批次确定百分位数。
有没有相对简单的方法来完成这个?
【问题讨论】:
您的意思是您正在寻找“per job
”持续时间?如果是这样,可以使用duration/job_count
作为衡量标准吗?请说明您的要求。您的第二种方法没有多大意义(至少在数学上)。
虽然正确,但问题仍然存在。 (为了简单起见,我在模拟数据中省略了这一点)如果我这样做,那么上述数据集中报告的第 75 个百分位是 0.16,但所需的第 75 个百分位应该是 0.13,因为它仍然根据批次而不是作业来确定第 75 个百分位。
另外值得注意的是,从功能上讲,在整个批次完成之前,批次中的任何作业都不会被视为已完成。因此,从最终用户的角度来看,一个批次中的所有作业都需要相同的时间。
【参考方案1】:
我认为这是“加权”百分位数。我不知道Oracle中是否有内置的分析函数,但计算起来很容易。而你正在路上。
另外的思路是计算jobs的总数,然后用算术来选择你想要的值。对于第 75 个百分位,该值是最小持续时间,使得累积作业数大于作业总数的 0.75 倍。
这是 SQL 中的示例:
select pcs.percentile, min(case when cumjobs >= totjobs * percentile then duration end)
from (SELECT batch_id, job_count,
SUM(job_count) OVER (ORDER BY duration) as cumjobs,
sum(job_count) over () as totjobs,
duration
FROM test_data
) t cross join
(select 0.25 as percentile from dual union all
select 0.5 from dual union all
select 0.75 from dual
) pcs
group by pcs.percentile;
此示例为您提供百分位值(作为额外奖励,对于三个不同的百分位),每个值位于其自己的行中。如果您想要每一行的值,则需要连接回原始表。
【讨论】:
这两个答案都让我得到了我想要的,但我接受了这个答案,因为它在更大的数据集上运行得更快。 (针对 600k 批次进行测试,每批次最多 1,800 个作业)我会赞成两者,但我还不能。谢谢两位的回答! 以防万一其他人对此答案中使用的表 dual 感到困惑,它是默认的 Oracle 系统表,通常用于选择常量(因为 SELECT 语句需要 FROM )。请参阅Wikipedia article。【参考方案2】:好的。我想我有你的答案。想法是我的。实现借鉴自this Ask Tom article
SELECT PERCENTILE_DISC( 0.75 )
WITHIN GROUP (ORDER BY duration ASC)
AS third_quartile
FROM(
with data as
(select level l
from dual, (select max(job_count) max_jobs from test_data)
connect by level <= max_jobs
)
select *
from test_data, data
where l <= job_count
--ORDER BY duration, batch_id
) inner
;
这里是SQL Fiddle。
【讨论】:
以上是关于百分位数的另一种方法?的主要内容,如果未能解决你的问题,请参考以下文章
JavaScript中的分位数/百分点/百分位数/逆累积分布函数
Pandas .. 分位数函数是不是需要排序数据来计算百分位数?