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
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
パーセンタイルまでのすべての行を占めます...