在 SQL 中分析并形成分位数并计算落在各个分位数中的值的百分比

Posted

技术标签:

【中文标题】在 SQL 中分析并形成分位数并计算落在各个分位数中的值的百分比【英文标题】:Analyse and form quantiles in SQL and count the percentage of values that fall in respective quantiles 【发布时间】:2021-01-27 15:07:26 【问题描述】:

您好,我在 mysql 数据库中有以下数据

txnid amount type
    1    500 A
    2    600 B
    3    700 A
    4    800 C
    5    900 D
    6   1000 A
    7   1100 B
    8   1200 B

我想在第一个 bin 中构建四个包含 0-25 分位数的 bin,在第二个 bin 中构建 25-50 个分位数,依此类推。接下来我要计算落在各个 bin 中的值的百分比。

【问题讨论】:

删除图片链接,而是直接在您的问题中包含示例数据,作为文本 您共享的输出中的所有类型是什么? @AnkitJindal:所有类型都是包括类型,即 A、B、C 和 D 欢迎来到 SO。请勿发文字图片,见Why should I provide an MCRE for what seems to me to be a very simple SQL query 【参考方案1】:

如果我正确地遵循了这一点,您可以使用ntile() 和算术来构建存储桶,然后使用条件聚合来透视:

select min_amount, max_amount, 
    sum(type = 'A') / total_cnt a,
    sum(type = 'B') / total_cnt b,
    sum(type = 'C') / total_cnt c,
    sum(type = 'D') / total_cnt d,
    count(*) / total_cnt all_types
from (
    select t.*, 
        min(amount) over() + (ntile(4) over(order by amount) - 1) / 4 * (max(amount) over() - min(amount) over()) min_amount,
        min(amount) over() +  ntile(4) over(order by amount)      / 4 * (max(amount) over() - min(amount) over()) max_amount,
        count(*) over() total_cnt
    from mytable t
) t
group by min_amount, max_amount

我将存储桶边界放在两个不同的列中,因为这对我来说更有意义 - 如果您愿意,可以将它们连接起来。比率表示为01 之间的值,如果需要百分比,可以乘以100

Demo on DB Fiddle

最小金额 |最大金额 |一个 |乙 | c | d |所有类型 ---------: | ---------: | -----: | -----: | -----: | -----: | --------: 500.0000 | 675.0000 | 0.1250 | 0.1250 | 0.0000 | 0.0000 | 0.2500 675.0000 | 850.0000 | 0.1250 | 0.0000 | 0.1250 | 0.0000 | 0.2500 850.0000 | 1025.0000 | 0.1250 | 0.0000 | 0.0000 | 0.1250 | 0.2500 1025.0000 | 1200.0000 | 0.0000 | 0.2500 | 0.0000 | 0.0000 | 0.2500

【讨论】:

嗨,我不能在查询中使用 over()。我正在使用一个名为元数据库的可视化应用程序,并且在使用 over() 时它给出了 SQL 语法错误【参考方案2】:

需要首先确定结果中每一行的间隔。由于表中没有这样的值。可能会形成一个包含UNION ALL 运算符的子查询。然后应该构造一个动态查询,考虑到type 列的不同值可能会更改。因此,您可以使用以下语句:

SET SESSION group_concat_max_len = 4096;
SET @sql = NULL;

SELECT GROUP_CONCAT(
       CONCAT('CONCAT(ROUND( 100 * SUM( CASE WHEN amount BETWEEN THEN type = ''',type,''' END)/COUNT(*), 2),"%") AS ',type )
       )
  INTO @sql
  FROM ( SELECT DISTINCT type FROM t) tt;

SELECT GROUP_CONCAT(
       CONCAT(
               'SELECT "',REPLACE(intrval,' AND ','-'),'" AS "Amount Bins", ',
                          REPLACE( @sql, 'BETWEEN THEN', 
                                  CONCAT('BETWEEN ',intrval,' THEN' )),
                      ', CONCAT(ROUND( 100 * SUM( CASE WHEN amount BETWEEN ',intrval,' THEN 1 END)/COUNT(*), 2),"%") AS "All Types"',         
               ' FROM t '                    
              )
       SEPARATOR ' UNION ALL '       
       )
  INTO @sql
  FROM (SELECT @i := @i + 1 AS i, 
               CONCAT(@i * ((max_amount - min_amount) / 4) + min_amount,' AND ',
                     (@i + 1) * ((max_amount - min_amount) / 4) + min_amount ) AS intrval
          FROM information_schema.tables i
          JOIN (SELECT @i := -1) AS ii
          JOIN (SELECT MIN(amount) AS min_amount, MAX(amount) AS max_amount
                  FROM t ) AS t_agg  
         WHERE @i < 3) i;

PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;

结果是

Amount Bins A       B       C       D       All Types
----------- ------  ------  ------  ------  ---------
500-675     12.50%  12.50%  0.00%   0.00%   25.00%
675-850     12.50%  0.00%   12.50%  0.00%   25.00%
850-1025    12.50%  0.00%   0.00%   12.50%  25.00%
1025-1200   0.00%   25.00%  0.00%   0.00%   25.00%

Demo

【讨论】:

这行不通,因为我们需要动态确定间隔,而不是在查询中对其进行硬编码 但在问题@NikhilKumarSingh 中没有区间值的来源(例如表格)或确定逻辑 我们需要分配分位数,比如第一个范围是 0%-25% 分位数,第二个是 25%-50% 分位数,第三个是 50%-75% 分位数,最后一个将是 75%-100%(最大)分位数值。 好吧,但是如何确定这些区间值 500-674、675-849 .. 等?我们如何知道这些价值观?它们是预定的吗?是否有规则让他们保持在每个分位数内? @NikhilKumarSingh 这就是我们的问题所在。我们需要编写一个查询来确定这些值。如果您查看@GMB 的答案。他使用每个分位数的最小和最大数量找到分位数

以上是关于在 SQL 中分析并形成分位数并计算落在各个分位数中的值的百分比的主要内容,如果未能解决你的问题,请参考以下文章

为大量数据计算分位数的增量方法

什么是分位数,如何计算分位数?

分位数如何计算?

R语言构建分位数回归(Quantile Regression)并计算R方指标实战

Pandas .. 分位数函数是不是需要排序数据来计算百分位数?

聊聊python的分位数