MySQL でのパーセンタイル ランクの計算
-
20-08-2019 - |
質問
MySQL に非常に大きな測定データのテーブルがあり、これらの値のそれぞれについてパーセンタイル ランクを計算する必要があります。Oracle には、percent_rank という関数があるようですが、MySQL には同様の関数が見つかりません。確かに、テーブルにデータを入力するためにとにかく使用する Python でブルートフォースすることもできますが、1 つのサンプルに 200,000 の観測値がある可能性があるため、それは非常に非効率的であると思います。
解決
このは比較的醜い答えである、と私はそれを言って罪悪感を感じます。それはそれはあなたの問題であなたを助けるかもしれない、と述べています。
の割合を決定する1つの方法は、すべての行をカウントし、あなたが提供する数より多い行数をカウントすることです。あなたはより大きいか小さいのどちらかを計算し、必要に応じて逆を取ることができます。
あなたの番号にインデックスを作成します。 合計= SELECT COUNT(の); less_equal =選択数()値> indexed_number;
( - less_equal合計)/総 less_equal /総または:の割合は次のようなものになるだろう
それらの両方は、あなたが作成したインデックスを使用していることを確認してください。そうでない場合は、彼らがされるまで、それらを微調整。説明クエリは、右側の欄に「インデックスを使用して、」持っている必要があります。 SELECT COUNT(*)の場合には、InnoDBのためのインデックスとMyISAMのためのconstのようなものを使用する必要があります。 MyISAMテーブルには、それを計算することなく、いつでもこの値を知ることができます。
は、データベースに格納された割合を有することが必要であれば、パフォーマンスのために上からセットアップを使用し、内側の選択として第2のクエリを使用して各行の値を計算することができます。最初のクエリの値が定数として設定することができます。
このヘルプをしていますか?
ヤコブ
他のヒント
ここでは、結合を必要としない別のアプローチを示します。私の場合 (15,000 以上の行があるテーブル)、約 3 秒で実行されます。(JOIN メソッドは一桁時間がかかります)。
サンプルでは次のように仮定します。 測定 パーセントランクを計算する列です。 ID は単なる行識別子です (必須ではありません)。
SELECT
id,
@prev := @curr as prev,
@curr := measure as curr,
@rank := IF(@prev > @curr, @rank+@ties, @rank) AS rank,
@ties := IF(@prev = @curr, @ties+1, 1) AS ties,
(1-@rank/@total) as percentrank
FROM
mytable,
(SELECT
@curr := null,
@prev := null,
@rank := 0,
@ties := 1,
@total := count(*) from mytable where measure is not null
) b
WHERE
measure is not null
ORDER BY
measure DESC
この方法の功績は Shlomi Noach に帰します。彼はそれについてここで詳しく書いています:
http://code.openark.org/blog/mysql/sql-ranking-without-self-join
これを MySQL でテストしましたが、うまく機能しました。Oracle、SQLServerなどについてはわかりません。
これを行うための簡単な方法はありません。 http://rpbouman.blogspot.com/2008見ます/07/calculating-nth-percentile-in-mysql.htmlする
SELECT
c.id, c.score, ROUND(((@rank - rank) / @rank) * 100, 2) AS percentile_rank
FROM
(SELECT
*,
@prev:=@curr,
@curr:=a.score,
@rank:=IF(@prev = @curr, @rank, @rank + 1) AS rank
FROM
(SELECT id, score FROM mytable) AS a,
(SELECT @curr:= null, @prev:= null, @rank:= 0) AS b
ORDER BY score DESC) AS c;
は、次の操作を行うことができます。この例では、彼らのパーセンタイルに、空港に過剰飛行ブロック回を分解します。 LIMITのX、ORDER BY
との組み合わせでのMySQLのy句を使用します。 (申し訳ありませんフォーマットに苦労し)非常にきれいな、しかし仕事をしていませんません:
$startDt = "2011-01-01";
$endDt = "2011-02-28";
$arrPort= 'JFK';
$strSQL = "SELECT COUNT(*) as TotFlights FROM FIDS where depdt >= '$startDt' And depdt <= '$endDt' and ArrPort='$arrPort'";
if (!($queryResult = mysql_query($strSQL, $con)) ) {
echo $strSQL . " FAILED\n"; echo mysql_error();
exit(0);
}
$totFlights=0;
while($fltRow=mysql_fetch_array($queryResult)) {
echo "Total Flights into " . $arrPort . " = " . $fltRow['TotFlights'];
$totFlights = $fltRow['TotFlights'];
/* 1906 flights. Percentile 90 = int(0.9 * 1906). */
for ($x = 1; $x<=10; $x++) {
$pctlPosn = $totFlights - intval( ($x/10) * $totFlights);
echo "PCTL POSN for " . $x * 10 . " IS " . $pctlPosn . "\t";
$pctlSQL = "SELECT (ablk-sblk) as ExcessBlk from FIDS where ArrPort='" . $arrPort . "' order by ExcessBlk DESC limit " . $pctlPosn . ",1;";
if (!($query2Result = mysql_query($pctlSQL, $con)) ) {
echo $pctlSQL . " FAILED\n";
echo mysql_error();
exit(0);
}
while ($pctlRow = mysql_fetch_array($query2Result)) {
echo "Excess Block is :" . $pctlRow['ExcessBlk'] . "\n";
}
}
}
MySQL 8 ではついにウィンドウ関数が導入されました。その中には、 PERCENT_RANK()
探していた機能。したがって、次のように書きます。
SELECT col, percent_rank() OVER (ORDER BY col)
FROM t
ORDER BY col
あなたの質問では「パーセンタイル」について言及していますが、これは少し異なります。完全を期すために、次のものがあります。 PERCENTILE_DISC
そして PERCENTILE_CONT
逆分布関数は SQL 標準および一部の RBDMS (Oracle、PostgreSQL、SQL Server、Teradata) では使用できますが、MySQL では使用できません。MySQL 8 とウィンドウ関数を使用すると、 エミュレートできます PERCENTILE_DISC
, ただし、もう一度使用すると、 PERCENT_RANK
そして FIRST_VALUE
ウィンドウ関数.
ランクを取得するには、私はあなたが(左)、外側に必要自体に何かをテーブルに参加言うと思います:
select t1.name, t1.value, count(distinct isnull(t2.value,0))
from table t1
left join table t2
on t1.value>t2.value
group by t1.name, t1.value
各行について、あなたは同じテーブルの行が劣る値を持っているどのように多くの(もしあれば)カウントされます。
構文は右ではないかもしれないので、私はSQLServerのをよりよく知っていることに注意してください。また、個別のは、あなたが達成したい何のために右の振る舞いを持っていないかもしれません。しかし、それは一般的な考えです。
そして、実際のパーセンタイルランクを取得するには、まず(あなたが取るしたい慣習に応じて、または別個の値)の変数の値の数を取得する必要があり、上記の実際のランクを使用してパーセンタイルランクを計算します。
私たちのような売上テーブルがあるとします:
USER_ID、単位
次のクエリは、各ユーザーのパーセンタイルを与えるます:
select a.user_id,a.units,
(sum(case when a.units >= b.units then 1 else 0 end )*100)/count(1) percentile
from sales a join sales b ;
これはクロスのために行くことに注意してください参加するので、O(N2)複雑になるので、最適化されていない解決策として考えられたが、我々はMySQLのバージョンのいずれかの機能を持っていない与えられた簡単なようでできます。
わからない何opが「パーセンタイルランク」によって意味が、値のセットに指定されたパーセンタイルを取得するには、<のhref = "http://rpbouman.blogspot.com/2008/07/calculating-nth-を参照してくださいパーセンタイル・イン・mysql.html」のrel = "nofollowをnoreferrer"> http://rpbouman.blogspot.com/2008/07/calculating-nth-percentile-in-mysql.htmlする SQL計算を簡単に別のまたは複数のパーセンタイルを生成するために変更することができた。
一つ注:私は、例えば、わずかに90パーセンタイル計算を変更しなければならなかった - "90/100 * COUNT(*)+ 0.5" の代わりに "90/100 * COUNT(*)+ 1" のを。時にはそれは、順序付けられたリストのパーセンタイル点を過ぎて2つの値を飛ばし、代わりにパーセンタイルのための次に高い値を摘みました。整数丸めは、MySQLで動作たぶん方法。
すなわち
... SUBSTRING_INDEX(SUBSTRING_INDEX(GROUP_CONCAT(fieldValueのORDER BY fieldValueのSEPARATOR ' ')、'、 ' 90/100 * COUNT(*)+の 0.5 の)、'、'、 -1)90thPercentileとして....