web-dev-qa-db-ja.com

加重百分位数を取得するクエリ

SQLを生成して、特定のパーセンタイル値のセットで加重連続値を計算します(以下では25%、50%、75%のレベルを使用しますが、ソリューションでは任意のパラメーターレベルを使用できます)。言い換えると、下の「ソース」テーブルのテストデータの25%、50%、75%の累積パーセンタイルごとに、「cnt」で重み付けされた補間された「未加工」値を見つけたいと考えています。

NB:cntは、サンプリング期間中にraw値が発生した回数と予想される出力を表しますパーセンタイルに到達するためにraw値をcntで重み付けします(変位値/中央値および類似の統計に類似)

テストデータ:(テーブル:ソース)

_|  site  |  dateval   |  raw  |   cnt   |
+--------+------------+-------+---------+
|   A    | 2019-01-05 |   45  |      14 |
|   A    | 2019-01-05 |   52  |     178 |
|   A    | 2019-01-05 |   45  |       9 |
|   A    | 2019-01-05 |   37  |      75 |
|   A    | 2019-01-05 |   23  |      98 |
|   A    | 2019-01-05 |   78  |     102 |
|   A    | 2019-01-05 |   56  |       9 |
|   A    | 2019-01-05 |   17  |      54 |
|   A    | 2019-01-05 |   56  |       8 |
|   A    | 2019-01-06 |   33  |      35 |
|   A    | 2019-01-06 |   67  |      45 |
|   A    | 2019-01-06 |   65  |      93 |
|   A    | 2019-01-06 |   89  |     113 |
|   A    | 2019-01-06 |   52  |      64 |
|   A    | 2019-01-06 |  101  |      12 |
|   B    | 2019-01-05 |    5  |      25 |
|   B    | 2019-01-05 |   16  |      48 |
|   B    | 2019-01-05 |   12  |     107 |
|   B    | 2019-01-05 |   25  |      78 |
|   B    | 2019-01-05 |   44  |      53 |
|   B    | 2019-01-05 |    8  |      12 |
|   B    | 2019-01-05 |   31  |      32 |
|   B    | 2019-01-06 |   34  |      87 |
|   B    | 2019-01-06 |   18  |      35 |
|   B    | 2019-01-06 |   51  |      17 |
|   B    | 2019-01-06 |   22  |      23 |
|   B    | 2019-01-06 |   14  |      52 |
|   B    | 2019-01-06 |    6  |      34 |
+--------+------------+-------+---------+
_

期待される出力(最も近い1/100に四捨五入):

_|  site  |   dateval  |   p00   |   p25   |   p50   |   p75   |   p100  |
+--------+------------+---------+---------+---------+---------+---------+
|   A    | 2019-01-05 |   17.00 |   22.07 |   45.92 |   51.30 |   78.00 |
|   A    | 2019-01-06 |   33.00 |   49.48 |   63.46 |   73.72 |  101.00 |
|   B    | 2019-01-05 |    5.00 |    9.93 |   14.79 |   24.57 |   44.00 |
|   B    | 2019-01-06 |    6.00 |   10.31 |   18.52 |   27.79 |   51.00 |
+--------+------------+---------+---------+---------+---------+---------+
_

[〜#〜] nb [〜#〜]:上記の結果は、raw値間の線形平滑化を想定しています。たとえば、_p25_ value of _22.07_ _=_ [ (25.00% - 54/547) / ((98+54)/547 - 54/547) ] * (23-17) + 17、ここで547 = sum(cnt) | site='A' & dateval='2019-01-05'

現在のSQL

以下は、テーブル「ソース」にある「生」の値に基づいて、目立たないポイントでのパーセンタイル値を計算します。ただし、必要な出力は、継続的に特定のパーセンタイルに対応する「生」値です(簡単にするために、目立たない「生」レベル間の補間は、スプライン/その他ではなく線形です)。率直に言って、次のアプローチが最も適切な方法かどうかはわかりません。

_WITH raw_lvl AS (
  SELECT "site", "dateval", "raw", sum("cnt") AS "sumcnt"
  FROM   source
  GROUP BY "site", "dateval", "raw"
), cum_raw AS (
  SELECT tlr.*, sum(tlr."sumcnt") OVER "win_cr" AS "cumsumcnt"
  FROM raw_lvl AS "tlr"
  WINDOW "win_cr" AS (PARTITION BY tlr."site", tlr."dateval" ORDER BY tlr."raw" ASC)
)
SELECT cr.*, cr."cumsumcnt"/(sum(cr."sumcnt") OVER "win_pr") AS "percentile" 
FROM cum_raw AS cr
WINDOW "win_pr" AS (PARTITION BY cr."site", cr."dateval");
_

Postgresバージョン10.3

3
Whee

Postgresには、目的に応じて Ordered-Set Aggregate Functions があります。

特別な困難:行をcntで「重み付け」する必要があります。それが各行がcnt同一の行を表すことを意味すると想定されている場合、generate_series(1, cnt)に結合することで入力行を乗算できます。

SELECT site, dateval
     , percentile_cont('{0,.25,.5,.75,1}'::float8[]) WITHIN GROUP (ORDER BY raw)
FROM   source s, generate_series(1, s.cnt)
GROUP  BY 1, 2;

db <> fiddle ここ

しかし、結果は期待される出力とは異なります(0パーセンタイルと100パーセンタイルを除く)。だからあなたは「重み付け」が異なっています...

さて、元のクエリは次のように簡略化できます。

SELECT site, dateval, raw, sum(cnt) AS sumcnt
     , sum(sum(cnt)) OVER w AS cumsumcnt
     , sum(sum(cnt)) OVER w / sum(sum(cnt)) OVER (PARTITION BY site, dateval) AS percentile 
FROM   source
GROUP  BY site, dateval, raw
WINDOW w AS (PARTITION BY site, dateval ORDER BY raw);

同じSELECT内の集約関数の結果に対してウィンドウ関数を実行できます(その逆はできません)。見る:

上のフィドルにデモを追加しました。

しかし、どちらも「期待される結果」の奇数を説明しません。それらは、あなたがどのように補間しても、私を正しくないと見なします。例:22.07の最初の行のp25は意味をなさないようです-値23は、独自のクエリに従ってcntを考慮に入れた後、27.7879パーセンタイルまでのすべての行を占めます...

2