Расчет процентильных рейтингов в MS SQL - PullRequest
26 голосов
/ 17 сентября 2008

Как лучше всего рассчитать процентильные ранги (например, 90-й процентиль или медианный балл) в MSSQL 2005?

Я бы хотел иметь возможность выбрать 25-й, средний и 75-й процентили для одного столбца оценок (желательно в одной записи, чтобы я мог комбинировать их со средним, максимальным и минимальным). Так, например, вывод таблицы результатов может быть:

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

Ответы [ 8 ]

15 голосов
/ 17 сентября 2008

Я думаю, что это будет самое простое решение:

SELECT TOP N PERCENT FROM TheTable ORDER BY TheScore DESC

Где N = (100 - желаемый процентиль). Поэтому, если вы хотите, чтобы все строки в 90-м процентиле были выбраны, верхние 10%.

Я не уверен, что вы подразумеваете под "желательно в одной записи". Вы имеете в виду подсчитать, в какой процентиль попадет данная оценка для отдельной записи? например хотите ли вы иметь возможность делать заявления типа «ваш счет 83, что ставит вас в 91-й процентиль».

РЕДАКТИРОВАТЬ: ОК, я подумал еще немного о вашем вопросе и придумал эту интерпретацию. Вы спрашиваете, как рассчитать показатель отсечки для определенного процентиля? например примерно так: чтобы попасть в 90-й процентиль, у вас должно быть больше 78.

Если это так, этот запрос работает. Мне не нравятся подзапросы, поэтому, в зависимости от того, для чего это было, я, вероятно, попытался бы найти более элегантное решение. Однако он возвращает одну запись с одним счетом.

-- 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
9 голосов
/ 29 июня 2011

Проверьте команду NTILE - она ​​очень легко даст вам процентили!

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)
2 голосов
/ 06 сентября 2012

Как насчет этого:

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
1 голос
/ 28 июня 2011

50-й процентиль такой же, как медиана. При вычислении другого процентиля, скажем, 80-го, отсортируйте данные для 80 процентов данных в порядке возрастания, а остальные 20 процентов - в порядке убывания и возьмите среднее из двух средних значений.

NB. Медианный запрос существует уже давно, но я не могу вспомнить, откуда именно я его получил, я только изменил его, чтобы вычислить другие процентили.

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 голос
/ 05 декабря 2008

Я работал над этим немного больше, и вот что я придумал до сих пор:

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

Затем в другой хранимой процедуре я делаю это:

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

Это все еще не совсем то, что я ищу. Это позволит получить статистику по всем тестам; тогда как я хотел бы иметь возможность выбирать из таблицы TestScores, в которой есть несколько разных тестов, и получать одинаковую статистику для каждого отдельного теста (как у меня в моей таблице примеров в моем вопросе).

0 голосов
/ 08 марта 2019

процентиль рассчитывается как

(Rank -1) /(total_rows -1) при сортировке значений в порядке возрастания.

Приведенный ниже запрос даст вам значение процентиля от 0 до 1. У человека с наименьшими оценками будет 0 процентиль.

SELECT Name, marks, (rank_1-1)/((select count(*) as total_1 from table)-1)as percentile_rank
from
(
SELECT Name,
       Marks,
       RANK() OVER (ORDER BY Marks) AS rank_1
       from table
) as A
0 голосов
/ 17 сентября 2008

я бы сделал что-то вроде:

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

это правильно?

0 голосов
/ 17 сентября 2008

Я бы, наверное, использовал SQL Server 2005

row_number () больше (порядок за счетом) / (выберите количество (*) из оценок)

или что-то в этом роде.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...