チャネル別相関分析 — CORR + GROUP BY でセグメント間の相関強度を比較・ランキングする
CORR(Y, X) は通常の集計関数なので GROUP BY と組み合わせてグループごとの相関係数を一括算出できます。ウィンドウ関数としては使えない点が RANK 等との重要な違いです。
-- GROUP BY + CORR でグループ別相関を一括算出 SELECT channel, ROUND(CORR(revenue, ad_cost)::numeric, 4) AS corr_coef, REGR_COUNT(revenue, ad_cost) AS n -- NULL除外の有効ペア数 FROM channel_sales GROUP BY channel; -- CORR を行レベルで付与したい場合は OVER() 不可 → CTE GROUP BY + JOIN が必要 -- ABS(CORR) で正負の相関を同一軸で強度比較できる
REGR_COUNT が期待する n より小さい場合、NULL が多くモデルの信頼性が低い可能性があります。channel_sales テーブルを使い、チャネル(channel)ごとに広告費(ad_cost)と売上(revenue)のピアソン相関係数(小数第4位まで)・有効ペア数(n)・相関強度ラベル(strength)を算出してください。ABS(CORR) >= 0.9 → '強い相関'、>= 0.7 → '中程度の相関'、それ以外は '弱い相関'。corr_coef の降順で返してください。
| week | channel | ad_cost | revenue |
|---|---|---|---|
| 1 | Online | 100 | 280 |
| 2 | Online | 200 | 650 |
| 3 | Online | 300 | 780 |
| 4 | Online | 400 | 1100 |
| 5 | Online | 500 | 1250 |
| 6 | Online | 600 | 1640 |
| 1 | Offline | 100 | 500 |
| 2 | Offline | 200 | 420 |
| 3 | Offline | 300 | 680 |
| 4 | Offline | 400 | 550 |
| 5 | Offline | 500 | 820 |
| 6 | Offline | 600 | 700 |
| channel | corr_coef | n | strength |
|---|---|---|---|
| Online | 0.9914 | 6 | 強い相関 |
| Offline | 0.7498 | 6 | 中程度の相関 |
残差分析 — REGR_SLOPE + CROSS JOIN で全行に予測値・残差を付与し外れ値を特定する
回帰モデルの残差(residual)= 実測値 − 予測値を各行に付与することで、モデルから大きく外れた行(外れ値・異常値候補)を特定できます。SQL では CTE で集計(1行)→ CROSS JOIN で全行に展開 → 予測値・残差を計算という3段階のパターンが定番です。
-- ① CTE でモデルパラメータを1行に集約 WITH model AS ( SELECT REGR_SLOPE(Y, X) AS slope, REGR_INTERCEPT(Y, X) AS intercept FROM data_table ), -- ② CROSS JOIN で全行にパラメータを展開 → 予測値・残差を計算 with_pred AS ( SELECT d.*, ROUND((m.slope * d.X + m.intercept)::numeric, 0) AS predicted, ROUND((d.Y - (m.slope * d.X + m.intercept))::numeric, 0) AS residual FROM data_table d CROSS JOIN model m -- 1行×N行 = N行に展開 ) SELECT *, ABS(residual) AS abs_residual FROM with_pred ORDER BY abs_residual DESC;
ABS(residual) が大きい行が外れ値候補です。R²(決定係数)が外れ値の影響で低下していないか REGR_R2 も同時に確認しましょう。store_data テーブルで床面積(floor_sqm)→ 日次売上(daily_sales)の線形回帰モデルをCTEで構築し、各店舗の予測売上(predicted)・残差(residual)・残差絶対値(abs_residual)を算出してください。abs_residual の降順で返し、外れ値候補の店舗を特定してください。
| store_id | floor_sqm | daily_sales |
|---|---|---|
| S1 | 100 | 500 |
| S2 | 200 | 950 |
| S3 | 300 | 1400 |
| S4 | 400 | 1850 |
| S5 | 500 | 2300 |
| S6 | 600 | 2200 |
| S7 | 700 | 3200 |
| S8 | 800 | 3700 |
| store_id | floor_sqm | daily_sales | predicted | residual | abs_residual |
|---|---|---|---|---|---|
| S6 | 600 | 2200 | 2664 | -464 | 464 |
| S8 | 800 | 3700 | 3533 | 167 | 167 |
| S7 | 700 | 3200 | 3099 | 101 | 101 |
| S5 | 500 | 2300 | 2230 | 70 | 70 |
| S4 | 400 | 1850 | 1795 | 55 | 55 |
| S3 | 300 | 1400 | 1361 | 39 | 39 |
| S2 | 200 | 950 | 926 | 24 | 24 |
| S1 | 100 | 500 | 492 | 8 | 8 |
カテゴリ別回帰モデル + バッチ予測 — GROUP BY REGR でモデルを構築し新データに一括適用する
GROUP BY と REGR_SLOPE / REGR_INTERCEPT を組み合わせると、カテゴリごとに独立した回帰モデルを1クエリで構築できます。さらに JOIN で新データテーブルと結合することで、複数シナリオの予測を一括バッチ処理できます。
-- GROUP BY でカテゴリ別モデルを並列構築 WITH models AS ( SELECT category, REGR_SLOPE(sales, ad_cost) AS slope, REGR_INTERCEPT(sales, ad_cost) AS intercept, REGR_R2(sales, ad_cost) AS r2 FROM product_data GROUP BY category ) -- JOIN で新データに各カテゴリのモデルを適用してバッチ予測 SELECT f.category, f.future_ad, ROUND((m.slope * f.future_ad + m.intercept)::numeric, 0) AS predicted_sales FROM forecast_input f JOIN models m USING (category);
product_data テーブルを使い、カテゴリ(category)ごとに広告費(ad_cost)→ 売上(sales)の回帰モデルを構築してください。次に forecast_input の予測広告費(future_ad)に各カテゴリのモデルを適用し、予測売上(predicted_sales)・傾き(slope)・切片(intercept)・決定係数(r2)を出力してください。category・future_ad 昇順で返してください。
| category | ad_cost | sales |
|---|---|---|
| Electronics | 100 | 300 |
| Electronics | 200 | 720 |
| Electronics | 300 | 1000 |
| Electronics | 400 | 1420 |
| Electronics | 500 | 1660 |
| Fashion | 100 | 220 |
| Fashion | 200 | 300 |
| Fashion | 300 | 480 |
| Fashion | 400 | 490 |
| Fashion | 500 | 680 |
| category | future_ad |
|---|---|
| Electronics | 400 |
| Electronics | 600 |
| Fashion | 400 |
| Fashion | 600 |
| category | future_ad | predicted_sales | slope | intercept | r2 |
|---|---|---|---|---|---|
| Electronics | 400 | 1362 | 3.4200 | -6.00 | 0.9926 |
| Electronics | 600 | 2046 | 3.4200 | -6.00 | 0.9926 |
| Fashion | 400 | 545 | 1.1100 | 101.00 | 0.9513 |
| Fashion | 600 | 767 | 1.1100 | 101.00 | 0.9513 |
順序統計量と外れ値検出 — PERCENTILE_CONT と Tukey フェンスで IQR ベース外れ値を特定する
PERCENTILE_CONT(p) WITHIN GROUP (ORDER BY col) はデータを昇順ソートして位置 p(0〜1)に相当する値を線形補間で返す順序集合集計関数です。p=0.25・0.5・0.75 で 第1四分位数・中央値・第3四分位数が得られます。
-- 書式: PERCENTILE_CONT(分位値) WITHIN GROUP (ORDER BY 列) -- 線形補間: 行位置 = 1 + p×(N−1) 例: N=12, p=0.25 → 行位置=3.75 PERCENTILE_CONT(0.25) WITHIN GROUP (ORDER BY score) AS q1 PERCENTILE_CONT(0.50) WITHIN GROUP (ORDER BY score) AS median PERCENTILE_CONT(0.75) WITHIN GROUP (ORDER BY score) AS q3 -- PERCENTILE_DISC: 補間せず最近傍の実際値を返す(離散版) -- Tukey フェンス(箱ひげ図の外れ値基準): -- lower_fence = Q1 − 1.5×IQR / upper_fence = Q3 + 1.5×IQR -- fence の外側を外れ値(outlier)とみなす
GROUP BY category と組み合わせてカテゴリ別の中央値を一括算出できます。また OVER() ウィンドウ関数としては使えません。行レベルで各行の分位数を付与したい場合は NTILE が代替候補ですが、NTILE は件数ベースの等分、PERCENTILE_CONT は値ベースの補間という違いを意識してください。exam_results テーブルを使い、CTE で Q1・中央値・Q3 から IQR・下限フェンス・上限フェンスを算出し、各学生のスコアが Tukey フェンス(lower=Q1−1.5×IQR, upper=Q3+1.5×IQR)の範囲内か否かを outlier_flag('外れ値' / '正常') でラベリングしてください。score 昇順で返してください。
| student_id | score |
|---|---|
| S01 | 67 |
| S02 | 82 |
| S03 | 55 |
| S04 | 138 |
| S05 | 63 |
| S06 | 80 |
| S07 | 42 |
| S08 | 91 |
| S09 | 71 |
| S10 | 75 |
| S11 | 85 |
| S12 | 58 |
| student_id | score | lower_fence | upper_fence | outlier_flag |
|---|---|---|---|---|
| S07 | 42 | 30.25 | 114.25 | 正常 |
| S03 | 55 | 30.25 | 114.25 | 正常 |
| S12 | 58 | 30.25 | 114.25 | 正常 |
| S05 | 63 | 30.25 | 114.25 | 正常 |
| S01 | 67 | 30.25 | 114.25 | 正常 |
| S09 | 71 | 30.25 | 114.25 | 正常 |
| S10 | 75 | 30.25 | 114.25 | 正常 |
| S06 | 80 | 30.25 | 114.25 | 正常 |
| S02 | 82 | 30.25 | 114.25 | 正常 |
| S11 | 85 | 30.25 | 114.25 | 正常 |
| S08 | 91 | 30.25 | 114.25 | 正常 |
| S04 | 138 | 30.25 | 114.25 | 外れ値 |
移動平均 — ROWS BETWEEN ウィンドウフレームで3ヶ月移動平均と前月比を同時算出する
ウィンドウ関数の ROWS BETWEEN n PRECEDING AND CURRENT ROW でウィンドウフレームを明示指定すると、現在行を含む直近 n+1 行の移動平均(Moving Average)を計算できます。
-- ROWS BETWEEN で移動平均ウィンドウフレームを明示指定 AVG(revenue) OVER ( ORDER BY month ROWS BETWEEN 2 PRECEDING AND CURRENT ROW ) AS ma3 -- 3ヶ月移動平均(当月含む直近3行) -- デフォルトフレームとの違い: -- ORDER BY のみ指定: RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW -- → 先頭行から当月まで全累積(累積平均) -- ROWS BETWEEN 2 PRECEDING AND CURRENT ROW -- → 物理的に直近3行のみ(移動平均)← こちらが正しい -- NULLIF で 0 除算を防ぐ前月比パターン(LAG の応用) -- (revenue - prev_revenue)::numeric / NULLIF(prev_revenue, 0) * 100
RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW(値が同じ行も含む累積)です。移動平均のように「物理的に直近 N 行」を指定したい場合は必ず ROWS BETWEEN を明示してください。ORDER BY 列に重複値がある場合、RANGE と ROWS では結果が異なります。sales_monthly テーブルを使い、CTE で前月売上(prev_revenue)を LAG で付与し、外側クエリで3ヶ月移動平均(ma3: 直近3行 ROUND 整数)と前月比成長率(mom_pct: 小数第2位、NULLIF でゼロ除算防止)を同時算出してください。month 昇順で返してください。
| month | revenue |
|---|---|
| 2024-01 | 1200000 |
| 2024-02 | 1350000 |
| 2024-03 | 1180000 |
| 2024-04 | 1520000 |
| 2024-05 | 1680000 |
| 2024-06 | 1450000 |
| 2024-07 | 1820000 |
| 2024-08 | 1960000 |
| month | revenue | ma3 | mom_pct |
|---|---|---|---|
| 2024-01 | 1200000 | 1200000 | NULL |
| 2024-02 | 1350000 | 1275000 | 12.50 |
| 2024-03 | 1180000 | 1243333 | -12.59 |
| 2024-04 | 1520000 | 1350000 | 28.81 |
| 2024-05 | 1680000 | 1460000 | 10.53 |
| 2024-06 | 1450000 | 1550000 | -13.69 |
| 2024-07 | 1820000 | 1650000 | 25.52 |
| 2024-08 | 1960000 | 1743333 | 7.69 |