SQL 統計分析 — 残差分析・PERCENTILE_CONTの応用

応用統計分析CORR + GROUP BY残差分析PERCENTILE_CONT移動平均PostgreSQL対応全5問
1 / 5 · 学習中 0 / 5 問完了
QUESTION 6

チャネル別相関分析 — CORR + GROUP BY でセグメント間の相関強度を比較・ランキングする

CORRGROUP BY相関分析セグメント比較ABS / CASE WHEN
前提知識

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) で正負の相関を同一軸で強度比較できる
GROUP BY の集計粒度に注意:GROUP BY channel の場合、各チャネル内の全行データで CORR が計算されます。週ごとにも CORR を見たい場合は GROUP BY channel, week_group のように粒度を調整してください。また REGR_COUNT が期待する n より小さい場合、NULL が多くモデルの信頼性が低い可能性があります。
問題

channel_sales テーブルを使い、チャネル(channel)ごとに広告費(ad_cost)と売上(revenue)のピアソン相関係数(小数第4位まで)有効ペア数(n)相関強度ラベル(strength)を算出してください。ABS(CORR) >= 0.9 → '強い相関'>= 0.7 → '中程度の相関'、それ以外は '弱い相関'corr_coef の降順で返してください。

使用テーブル
► channel_sales(12行)
weekchannelad_costrevenue
1Online100280
2Online200650
3Online300780
4Online4001100
5Online5001250
6Online6001640
1Offline100500
2Offline200420
3Offline300680
4Offline400550
5Offline500820
6Offline600700
期待出力
channelcorr_coefnstrength
Online0.99146強い相関
Offline0.74986中程度の相関
QUESTION 7

残差分析 — REGR_SLOPE + CROSS JOIN で全行に予測値・残差を付与し外れ値を特定する

REGR_SLOPECROSS JOIN残差分析外れ値検出CTE多段
前提知識

回帰モデルの残差(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_data(8行)
store_idfloor_sqmdaily_sales
S1100500
S2200950
S33001400
S44001850
S55002300
S66002200
S77003200
S88003700
期待出力
store_idfloor_sqmdaily_salespredictedresidualabs_residual
S660022002664-464464
S880037003533167167
S770032003099101101
S5500230022307070
S4400185017955555
S3300140013613939
S22009509262424
S110050049288
QUESTION 8

カテゴリ別回帰モデル + バッチ予測 — GROUP BY REGR でモデルを構築し新データに一括適用する

REGR_SLOPEREGR_R2カテゴリ別回帰バッチ予測GROUP BY + JOIN
前提知識

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);
単回帰の slope はカテゴリ別 ROI として解釈できる:slope = Δ売上 / Δ広告費 は「広告費1円あたりの売上増分」=広告 ROI の代理指標です。slope が高いカテゴリほど広告投資対効果が高いため、予算配分の根拠として使えます。R² を見ながら「モデルの信頼性が高いカテゴリ」のみで意思決定する習慣をつけましょう。
問題

product_data テーブルを使い、カテゴリ(category)ごとに広告費(ad_cost)→ 売上(sales)の回帰モデルを構築してください。次に forecast_input の予測広告費(future_ad)に各カテゴリのモデルを適用し、予測売上(predicted_sales)・傾き(slope)・切片(intercept)・決定係数(r2)を出力してください。category・future_ad 昇順で返してください。

使用テーブル
► product_data(10行)
categoryad_costsales
Electronics100300
Electronics200720
Electronics3001000
Electronics4001420
Electronics5001660
Fashion100220
Fashion200300
Fashion300480
Fashion400490
Fashion500680
► forecast_input(4行)
categoryfuture_ad
Electronics400
Electronics600
Fashion400
Fashion600
期待出力
categoryfuture_adpredicted_salesslopeinterceptr2
Electronics40013623.4200-6.000.9926
Electronics60020463.4200-6.000.9926
Fashion4005451.1100101.000.9513
Fashion6007671.1100101.000.9513
QUESTION 9

順序統計量と外れ値検出 — PERCENTILE_CONT と Tukey フェンスで IQR ベース外れ値を特定する

PERCENTILE_CONTWITHIN GROUP四分位数外れ値検出Tukeyフェンス
前提知識

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 可:PERCENTILE_CONT は 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 昇順で返してください。

使用テーブル
► exam_results(12行)
student_idscore
S0167
S0282
S0355
S04138
S0563
S0680
S0742
S0891
S0971
S1075
S1185
S1258
期待出力
student_idscorelower_fenceupper_fenceoutlier_flag
S074230.25114.25正常
S035530.25114.25正常
S125830.25114.25正常
S056330.25114.25正常
S016730.25114.25正常
S097130.25114.25正常
S107530.25114.25正常
S068030.25114.25正常
S028230.25114.25正常
S118530.25114.25正常
S089130.25114.25正常
S0413830.25114.25外れ値
QUESTION 10

移動平均 — ROWS BETWEEN ウィンドウフレームで3ヶ月移動平均と前月比を同時算出する

ROWS BETWEENAVG OVER移動平均時系列分析LAG + NULLIF
前提知識

ウィンドウ関数の 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
ROWS vs RANGE の違い:ORDER BY を含むウィンドウ関数のデフォルトフレームは 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 昇順で返してください。

使用テーブル
► sales_monthly(8行)
monthrevenue
2024-011200000
2024-021350000
2024-031180000
2024-041520000
2024-051680000
2024-061450000
2024-071820000
2024-081960000
期待出力
monthrevenuema3mom_pct
2024-0112000001200000NULL
2024-021350000127500012.50
2024-0311800001243333-12.59
2024-041520000135000028.81
2024-051680000146000010.53
2024-0614500001550000-13.69
2024-071820000165000025.52
2024-08196000017433337.69