it-swarm-pt.tech

Calculando rankings de percentil no MS SQL

Qual é a melhor maneira de calcular as classificações percentuais (por exemplo, o percentil 90 ou a pontuação mediana) no MSSQL 2005?

Gostaria de poder selecionar os percentis 25, mediano e 75 para uma única coluna de pontuações (de preferência em um único registro para que eu possa combinar com média, máximo e mínimo). Por exemplo, a saída da tabela dos resultados pode ser:

Group  MinScore  MaxScore  AvgScore  pct25  median  pct75
-----  --------  --------  --------  -----  ------  -----
T1     52        96        74        68     76      84
T2     48        98        74        68     75      85
25
Soldarnal

Eu pensaria que esta seria a solução mais simples:

SELECT TOP N PERCENT FROM TheTable ORDER BY TheScore DESC

Onde N = (100 - percentil desejado). Então, se você quisesse todas as linhas no 90º percentil, você selecionaria os 10% melhores.

Eu não tenho certeza do que você quer dizer com "de preferência em um único registro". Quer dizer calcular qual percentil uma determinada pontuação para um único registro cairia? por exemplo. Você quer ser capaz de fazer declarações como "sua pontuação é 83, o que o coloca no 91º percentil". ?

EDIT: OK, eu pensei um pouco mais sobre a sua pergunta e surgiu com essa interpretação. Você está perguntando como calcular a pontuação de corte para um percentil específico? por exemplo. algo assim: para estar no 90º percentil você deve ter uma pontuação maior que 78.

Em caso afirmativo, esta consulta funciona. Eu não gosto de subconsultas, então, dependendo do que era, eu provavelmente tentaria encontrar uma solução mais elegante. No entanto, ele retorna um único registro com uma única pontuação.

-- Find the minimum score for all scores in the 90th percentile
SELECT Min(subq.TheScore) FROM
(SELECT TOP 10 PERCENT TheScore FROM TheTable
ORDER BY TheScore DESC) AS subq
14
Matt

Confira o comando NTILE - ele lhe dará percentuais com facilidade!

SELECT  SalesOrderID, 
    OrderQty,
    RowNum = Row_Number() OVER(Order By OrderQty),
    Rnk = RANK() OVER(ORDER BY OrderQty),
    DenseRnk = DENSE_RANK() OVER(ORDER BY OrderQty),
    NTile4  = NTILE(4) OVER(ORDER BY OrderQty)
FROM Sales.SalesOrderDetail 
WHERE SalesOrderID IN (43689, 63181)
9
Elizabeth

Que tal agora:

SELECT
  Group,
  75_percentile =  MAX(case when NTILE(4) OVER(ORDER BY score ASC) = 3 then score  else 0 end),
  90_percentile =  MAX(case when NTILE(10) OVER(ORDER BY score  ASC) = 9 then score  else 0 end)     
FROM TheScore
GROUP BY Group
2
Paul

O percentil 50 é o mesmo que a mediana. Ao computar outro percentil, digamos o 80, classifique os dados para os 80% dos dados em ordem crescente e os outros 20% em ordem decrescente, e calcule a média dos dois valores intermediários.

NB: A consulta mediana existe há muito tempo, mas não se lembra exatamente de onde eu a obtive, apenas a alterei para calcular outros percentis.

DECLARE @Temp TABLE(Id INT IDENTITY(1,1), DATA DECIMAL(10,5))

INSERT INTO @Temp VALUES(0)
INSERT INTO @Temp VALUES(2)
INSERT INTO @Temp VALUES(8)
INSERT INTO @Temp VALUES(4)
INSERT INTO @Temp VALUES(3)
INSERT INTO @Temp VALUES(6)
INSERT INTO @Temp VALUES(6)
INSERT INTO @Temp VALUES(6) 
INSERT INTO @Temp VALUES(7)
INSERT INTO @Temp VALUES(0)
INSERT INTO @Temp VALUES(1)
INSERT INTO @Temp VALUES(NULL)


--50th percentile or median
SELECT ((
        SELECT TOP 1 DATA
        FROM   (
                SELECT  TOP 50 PERCENT DATA
                FROM    @Temp
                WHERE   DATA IS NOT NULL
                ORDER BY DATA
                ) AS A
        ORDER BY DATA DESC) + 
        (
        SELECT TOP 1 DATA
        FROM   (
                SELECT  TOP 50 PERCENT DATA
                FROM    @Temp
                WHERE   DATA IS NOT NULL
                ORDER BY DATA DESC
                ) AS A
        ORDER BY DATA ASC)) / 2.0


--90th percentile 
SELECT ((
        SELECT TOP 1 DATA
        FROM   (
                SELECT  TOP 90 PERCENT DATA
                FROM    @Temp
                WHERE   DATA IS NOT NULL
                ORDER BY DATA
                ) AS A
        ORDER BY DATA DESC) + 
        (
        SELECT TOP 1 DATA
        FROM   (
                SELECT  TOP 10 PERCENT DATA
                FROM    @Temp
                WHERE   DATA IS NOT NULL
                ORDER BY DATA DESC
                ) AS A
        ORDER BY DATA ASC)) / 2.0


--75th percentile
SELECT ((
        SELECT TOP 1 DATA
        FROM   (
                SELECT  TOP 75 PERCENT DATA
                FROM    @Temp
                WHERE   DATA IS NOT NULL
                ORDER BY DATA
                ) AS A
        ORDER BY DATA DESC) + 
        (
        SELECT TOP 1 DATA
        FROM   (
                SELECT  TOP 25 PERCENT DATA
                FROM    @Temp
                WHERE   DATA IS NOT NULL
                ORDER BY DATA DESC
                ) AS A
        ORDER BY DATA ASC)) / 2.0
1
Kay Aliu

Eu tenho trabalhado nisso um pouco mais, e aqui está o que eu fiz até agora:

CREATE PROCEDURE [dbo].[TestGetPercentile]

@percentile as float,
@resultval as float output

AS

BEGIN

WITH scores(score, prev_rank, curr_rank, next_rank) AS (
    SELECT dblScore,
        (ROW_NUMBER() OVER ( ORDER BY dblScore ) - 1.0) / ((SELECT COUNT(*) FROM TestScores) + 1)  [prev_rank],
        (ROW_NUMBER() OVER ( ORDER BY dblScore ) + 0.0) / ((SELECT COUNT(*) FROM TestScores) + 1)  [curr_rank],
        (ROW_NUMBER() OVER ( ORDER BY dblScore ) + 1.0) / ((SELECT COUNT(*) FROM TestScores) + 1)  [next_rank]
    FROM TestScores
)

SELECT @resultval = (
    SELECT TOP 1 
    CASE WHEN t1.score = t2.score
        THEN t1.score
    ELSE
        t1.score + (t2.score - t1.score) * ((@percentile - t1.curr_rank) / (t2.curr_rank - t1.curr_rank))
    END
    FROM scores t1, scores t2
    WHERE (t1.curr_rank = @percentile OR (t1.curr_rank < @percentile AND t1.next_rank > @percentile))
        AND (t2.curr_rank = @percentile OR (t2.curr_rank > @percentile AND t2.prev_rank < @percentile))
)

END

Em seguida, em outro procedimento armazenado, faço isso:

DECLARE @pct25 float;
DECLARE @pct50 float;
DECLARE @pct75 float;

exec SurveyGetPercentile .25, @pct25 output
exec SurveyGetPercentile .50, @pct50 output
exec SurveyGetPercentile .75, @pct75 output

Select
    min(dblScore) as minScore,
    max(dblScore) as maxScore,
    avg(dblScore) as avgScore,
    @pct25 as percentile25,
    @pct50 as percentile50,
    @pct75 as percentile75
From TestScores

Ainda não faz bem o que estou procurando. Isto irá obter as estatísticas para todos os testes; Considerando que eu gostaria de poder selecionar de uma tabela TestScores que tem vários testes diferentes e obter as mesmas estatísticas para cada teste diferente (como eu tenho na minha tabela de exemplo na minha pergunta).

1
Soldarnal

eu provavelmente usaria o SQL Server 2005 

row_number () over (ordem pela pontuação)/(selecione count (*) das pontuações)

ou algo nesse sentido. 

0
karl prosser

eu faria algo como:

select @n = count(*) from tbl1
select @median = @n / 2
select @p75 = @n * 3 / 4
select @p90 = @n * 9 / 10

select top 1 score from (select top @median score from tbl1 order by score asc) order by score desc

isto está certo?

0
syap