p値関数
あらゆる$ \theta_0 に対して
帰無仮説$ H_0: \theta = \theta_0
を検定したときのp値を計算し、それをプロットしたものをp値関数という。
軸は
横軸 $ \theta_0
縦軸 p値
見方
p値関数は、各$ \theta_0 が観測データおよび仮定したモデルとどの程度compatibleであるか(矛盾するか)を連続的に示す
p値が小さいほど、その$ \theta_0 のもとでは観測データのcompatibilityが低い(矛盾の程度が大きい)
p値は次のいずれでもない
× "$ \theta_0 が真である確率"
× "効果の大きさ"
× "結果の重要性"
信頼区間との関係
p値関数から信頼区間を作ることができる
たとえば $ 100(1-\alpha) \%信頼区間は
$ p(\theta_0) \geq \alpha
を満たす $ \theta_0 の集合として得られる
信頼区間はp値関数の要約の一つといえる
S値関数との関係
p値の代わりにS値をプロットしたものをS値関数という
$ s(\theta_0) = -\log_2 p(\theta_0)
p値関数・信頼区間が依存するもの
仮定している統計モデル
確率分布
検定統計量
検定する仮説 $ H_0: \theta = \theta_0
観察されたデータ
モデルを適用するための背後仮定
観測が独立であること
二項モデルが妥当であること
標本サイズが十分に大きいこと
データに欠測や偏りがないこと
Wald型のp値関数(compatibility curve)
Wald型では、$ \hat{\theta} を中心として滑らかな山型のp値関数が得られる
ただしこれは近似的な手法なので、標本サイズが小さい場合などには精度が悪くなることがある
詳細はWald信頼区間
クリックしてアニメーションを開く
https://gyazo.com/66835885a4dcabe151c3c848ef313d0c
code:wald.js
let n = 20;
let marginL = 70, marginR = 50, marginT = 50, marginB = 65;
function setup() {
createCanvas(720, 480);
}
function draw() {
background(255);
let plotW = width - marginL - marginR;
let plotH = height - marginT - marginB;
let pHat = 0.5;
let X = round(n * pHat);
// カーソルYからalpha
let alphaRaw = map(mouseY, marginT + plotH, marginT, 0.0, 1.0, true);
let alpha = max(0.01, round(alphaRaw / 0.01) * 0.01);
// --- 軸 ---
stroke(200);
strokeWeight(1);
line(marginL, marginT, marginL, marginT + plotH);
line(marginL, marginT + plotH, marginL + plotW, marginT + plotH);
// 目盛り
fill(100);
noStroke();
textFont('sans-serif');
textSize(10);
textAlign(CENTER, TOP);
for (let i = 0; i <= 10; i++) {
let x = marginL + (i / 10) * plotW;
stroke(230);
line(x, marginT, x, marginT + plotH);
noStroke();
text(nf(i / 10, 1, 1), x, marginT + plotH + 8);
}
textAlign(RIGHT, CENTER);
for (let i = 0; i <= 5; i++) {
let y = marginT + plotH - (i / 5) * plotH;
stroke(230);
line(marginL, y, marginL + plotW, y);
noStroke();
text(nf(i / 5, 1, 1), marginL - 8, y);
}
// --- 軸ラベル ---
fill(60);
noStroke();
textSize(13);
textAlign(CENTER, TOP);
text('θ₀', marginL + plotW / 2, marginT + plotH + 24);
push();
translate(18, marginT + plotH / 2);
rotate(-HALF_PI);
textAlign(CENTER, CENTER);
text('p-value', 0, 0);
pop();
// --- p値関数を計算 ---
let steps = 500;
let pValues = [];
for (let i = 0; i <= steps; i++) {
let p0 = i / steps;
pValues.push(waldPValue(p0, pHat, n));
}
// --- 信頼区間を解析的に計算 ---
let se = sqrt(pHat * (1 - pHat) / n);
let zCrit = qnorm(1 - alpha / 2);
let ciLow = pHat - zCrit * se;
let ciHigh = pHat + zCrit * se;
// --- alpha水平線(red, full width) ---
let yAlpha = marginT + plotH - alpha * plotH;
stroke(220, 70, 70);
strokeWeight(1);
line(marginL, yAlpha, marginL + plotW, yAlpha);
// --- p値関数の曲線 ---
noFill();
stroke(40, 100, 200);
strokeWeight(2);
beginShape();
for (let i = 0; i <= steps; i++) {
let sx = marginL + (i / steps) * plotW;
let sy = marginT + plotH - pValuesi * plotH;
vertex(sx, sy);
}
endShape();
// --- CI as green line on alpha level + dots (drawn on top of curve) ---
{
let cl = max(0, ciLow);
let ch = min(1, ciHigh);
let x1 = marginL + cl * plotW;
let x2 = marginL + ch * plotW;
// green line segment on alpha level
stroke(40, 160, 80);
strokeWeight(3);
line(x1, yAlpha, x2, yAlpha);
// dots at intersection points
noStroke();
fill(40, 160, 80);
circle(x1, yAlpha, 8);
circle(x2, yAlpha, 8);
}
// --- 点推定値の縦線 ---
let xPhat = marginL + pHat * plotW;
stroke(0, 0, 0, 40);
strokeWeight(1);
drawingContext.setLineDash(3, 3);
line(xPhat, marginT, xPhat, marginT + plotH);
drawingContext.setLineDash([]);
// 頂上の点
noStroke();
fill(40, 100, 200);
circle(xPhat, marginT, 6);
// --- alpha表示 ---
fill(220, 70, 70);
noStroke();
textFont('sans-serif');
textSize(11);
textAlign(LEFT, CENTER);
text(α = ${alpha.toFixed(2)}, marginL + plotW + 4, yAlpha);
// --- CI表示 ---
fill(40, 160, 80);
textSize(12);
textAlign(LEFT, TOP);
let pct = ((1 - alpha) * 100).toFixed(0);
text(${pct}% CI: [${ciLow.toFixed(4)}, ${ciHigh.toFixed(4)}], marginL + 8, marginT + 8);
// --- タイトル・情報 ---
noStroke();
fill(40);
textAlign(LEFT, TOP);
textSize(14);
textStyle(BOLD);
text(Wald-type p-value function, marginL, 10);
textStyle(NORMAL);
textSize(11);
fill(100);
text(n = ${n} X = ${X} θ^ = ${pHat.toFixed(1)}, marginL, 28);
// 点推定値ラベル
fill(0, 0, 0, 120);
textSize(10);
textAlign(CENTER, BOTTOM);
text(θ^ = ${pHat.toFixed(1)}, xPhat, marginT - 4);
// 操作説明
fill(160);
textSize(10);
textAlign(CENTER, BOTTOM);
text('Cursor: Change α-level / ↑↓: Change sample size', width / 2, height - 4);
}
function keyPressed() {
if (keyCode === UP_ARROW) {
if (n < 50) n += 2;
else if (n < 200) n += 10;
else if (n < 1000) n += 50;
else n += 200;
n = min(n, 10000);
}
if (keyCode === DOWN_ARROW) {
if (n <= 50) n -= 2;
else if (n <= 200) n -= 10;
else if (n <= 1000) n -= 50;
else n -= 200;
n = max(n, 2);
if (n % 2 !== 0) n++;
}
return false;
}
// --- 正規分布CDF ---
function normalCDF(x) {
if (x < -8) return 0;
if (x > 8) return 1;
let a1 = 0.254829592, a2 = -0.284496736, a3 = 1.421413741;
let a4 = -1.453152027, a5 = 1.061405429, p = 0.3275911;
let sign = x < 0 ? -1 : 1;
let u = abs(x) / sqrt(2);
let t = 1.0 / (1.0 + p * u);
let y = 1.0 - (((((a5 * t + a4) * t) + a3) * t + a2) * t + a1) * t * exp(-x * x / 2);
return 0.5 * (1.0 + sign * y);
}
// --- Wald p値 ---
function waldPValue(p0, pHat, n) {
let se = sqrt(pHat * (1 - pHat) / n);
if (se === 0) return 0;
let z = abs((pHat - p0) / se);
return 2 * (1 - normalCDF(z));
}
// --- 逆正規分布CDF (Acklam's approximation) ---
function qnorm(p) {
if (p <= 0) return -Infinity;
if (p >= 1) return Infinity;
if (p === 0.5) return 0;
let a1 = -3.969683028665376e+01, a2 = 2.209460984245205e+02;
let a3 = -2.759285104469687e+02, a4 = 1.383577518672690e+02;
let a5 = -3.066479806614716e+01, a6 = 2.506628277459239e+00;
let b1 = -5.447609879822406e+01, b2 = 1.615858368580409e+02;
let b3 = -1.556989798598866e+02, b4 = 6.680131188771972e+01;
let b5 = -1.328068155288572e+01;
let c1 = -7.784894002430293e-03, c2 = -3.223964580411365e-01;
let c3 = -2.400758277161838e+00, c4 = -2.549732539343734e+00;
let c5 = 4.374664141464968e+00, c6 = 2.938163982698783e+00;
let d1 = 7.784695709041462e-03, d2 = 3.224671290700398e-01;
let d3 = 2.445134137142996e+00, d4 = 3.754408661907416e+00;
let pLow = 0.02425, pHigh = 1 - pLow;
let q, r;
if (p < pLow) {
q = sqrt(-2 * log(p));
return (((((c1*q+c2)*q+c3)*q+c4)*q+c5)*q+c6) / ((((d1*q+d2)*q+d3)*q+d4)*q+1);
} else if (p <= pHigh) {
q = p - 0.5; r = q * q;
return (((((a1*r+a2)*r+a3)*r+a4)*r+a5)*r+a6)*q / (((((b1*r+b2)*r+b3)*r+b4)*r+b5)*r+1);
} else {
q = sqrt(-2 * log(1 - p));
return -(((((c1*q+c2)*q+c3)*q+c4)*q+c5)*q+c6) / ((((d1*q+d2)*q+d3)*q+d4)*q+1);
}
}
public