#13 ふれてみよう高校数学 確率・統計
相関と回帰
相関とは
「身長が高い人は体重も重い傾向がある」「勉強時間が長いほど成績が良い」——こうした「一方が大きいと他方も大きい(または小さい)」という傾向を相関関係といいます。
2つの変数 X と Y の間にこの傾向があるとき、2変数は相関関係にあるといいます。
重要:相関 ≠ 因果関係——「アイスクリームの売上と溺死者数に相関がある」というデータがあります。これは気温(第3の変数)が両方に影響しているからです。「アイスを食べると溺れやすい」わけではありません。相関はあくまで統計的な共変の傾向であり、原因と結果の関係を意味しません。
相関係数 r
「どのくらい強く相関しているか」を -1 から +1 の数値で表すのが相関係数です:
値の範囲:
| の値 | 解釈 |
|---|---|
| 強い正の相関 | |
| 相関なし | |
| 強い負の相関 | |
| 強い相関の目安 |
回帰直線(最小二乗法)
「散布図にデータ点を打ったとき、そのデータを最もよく表す一本の直線を引きたい」——これが回帰直線です。
回帰直線(regression line)とは、データ点への距離の二乗和を最小化する直線 です。「全データ点から直線までの縦方向の距離の合計が最小になる直線」——これを最小二乗法といいます:
ここで , はそれぞれの標準偏差。回帰直線は必ず (両者の平均の交点)を通ります——「平均どうしを結んだ点は必ず直線上にある」。
散布図・回帰直線デモ
マウスをグラフ内で動かすと、その位置に赤い仮想データ点が追加されて回帰直線と相関係数がリアルタイムで更新されます。端っこに外れ値を置くと直線が大きく動く「外れ値の影響力」を体感できます。
散布図と回帰直線:マウスを動かして新しい点の位置を確認、直線と相関係数への影響を観察
var seed = 77;
function rand() {
seed = (seed * 1664525 + 1013904223) & 0x7fffffff;
return seed / 0x7fffffff;
}
function rn() {
return Math.sqrt(-2 * Math.log(rand() + 0.00001)) * Math.cos(2 * Math.PI * rand());
}
var baseData = [];
for (var i = 0; i < 20; i++) {
var xv = 20 + rand() * 60;
var yv = 1.2 * xv + 10 + rn() * 12;
baseData.push({ x: xv, y: yv });
}
var chartL = 50, chartR = 530, chartT = 30, chartB = 280;
var dataMinX = 10, dataMaxX = 90, dataMinY = 10, dataMaxY = 130;
function toScreenX(v) { return chartL + (v - dataMinX) / (dataMaxX - dataMinX) * (chartR - chartL); }
function toScreenY(v) { return chartB - (v - dataMinY) / (dataMaxY - dataMinY) * (chartB - chartT); }
function fromScreenX(px) { return dataMinX + (px - chartL) / (chartR - chartL) * (dataMaxX - dataMinX); }
function fromScreenY(py) { return dataMinY + (chartB - py) / (chartB - chartT) * (dataMaxY - dataMinY); }
function computeStats(pts) {
var n = pts.length;
if (n < 2) return null;
var sx = 0, sy = 0;
for (var i = 0; i < n; i++) { sx += pts[i].x; sy += pts[i].y; }
var mx2 = sx / n, my2 = sy / n;
var sxx = 0, syy = 0, sxy = 0;
for (var i2 = 0; i2 < n; i2++) {
sxx += (pts[i2].x - mx2) * (pts[i2].x - mx2);
syy += (pts[i2].y - my2) * (pts[i2].y - my2);
sxy += (pts[i2].x - mx2) * (pts[i2].y - my2);
}
var a = sxy / sxx;
var b2 = my2 - a * mx2;
var r = sxy / Math.sqrt(sxx * syy);
return { a: a, b: b2, r: r, mx: mx2, my: my2 };
}
function loop() {
ctx.clearRect(0, 0, W, H);
var mouseDataX = fromScreenX(mx);
var mouseDataY = fromScreenY(my);
var inChart = mx >= chartL && mx <= chartR && my >= chartT && my <= chartB;
var allData = baseData.slice();
if (inChart) allData.push({ x: mouseDataX, y: mouseDataY });
ctx.strokeStyle = '#334155';
ctx.lineWidth = 1;
ctx.strokeRect(chartL, chartT, chartR - chartL, chartB - chartT);
for (var gx = dataMinX; gx <= dataMaxX; gx += 20) {
var sx = toScreenX(gx);
ctx.strokeStyle = '#1e293b';
ctx.lineWidth = 0.5;
ctx.beginPath();
ctx.moveTo(sx, chartT);
ctx.lineTo(sx, chartB);
ctx.stroke();
ctx.fillStyle = '#475569';
ctx.font = '10px monospace';
ctx.textAlign = 'center';
ctx.fillText(gx, sx, chartB + 14);
}
for (var gy = dataMinY; gy <= dataMaxY; gy += 20) {
var sy = toScreenY(gy);
ctx.strokeStyle = '#1e293b';
ctx.lineWidth = 0.5;
ctx.beginPath();
ctx.moveTo(chartL, sy);
ctx.lineTo(chartR, sy);
ctx.stroke();
ctx.fillStyle = '#475569';
ctx.font = '10px monospace';
ctx.textAlign = 'right';
ctx.fillText(gy, chartL - 4, sy + 4);
}
var stats = computeStats(allData);
if (stats) {
var lineX1 = dataMinX, lineX2 = dataMaxX;
var lineY1 = stats.a * lineX1 + stats.b;
var lineY2 = stats.a * lineX2 + stats.b;
ctx.strokeStyle = '#f59e0b';
ctx.lineWidth = 2;
ctx.beginPath();
ctx.moveTo(toScreenX(lineX1), toScreenY(lineY1));
ctx.lineTo(toScreenX(lineX2), toScreenY(lineY2));
ctx.stroke();
}
for (var i3 = 0; i3 < baseData.length; i3++) {
ctx.fillStyle = '#3b82f6';
ctx.beginPath();
ctx.arc(toScreenX(baseData[i3].x), toScreenY(baseData[i3].y), 5, 0, Math.PI * 2);
ctx.fill();
}
if (inChart) {
ctx.fillStyle = '#ef4444';
ctx.beginPath();
ctx.arc(toScreenX(mouseDataX), toScreenY(mouseDataY), 7, 0, Math.PI * 2);
ctx.fill();
ctx.strokeStyle = '#ef4444';
ctx.lineWidth = 1.5;
ctx.setLineDash([3, 3]);
ctx.beginPath();
ctx.moveTo(toScreenX(mouseDataX), toScreenY(mouseDataY));
if (stats) {
var predY = stats.a * mouseDataX + stats.b;
ctx.lineTo(toScreenX(mouseDataX), toScreenY(predY));
}
ctx.stroke();
ctx.setLineDash([]);
}
if (stats) {
var rColor = Math.abs(stats.r) > 0.7 ? '#22c55e' : Math.abs(stats.r) > 0.4 ? '#f59e0b' : '#ef4444';
ctx.fillStyle = '#e2e8f0';
ctx.font = 'bold 13px sans-serif';
ctx.textAlign = 'left';
ctx.fillText('y = ' + stats.a.toFixed(2) + 'x + ' + stats.b.toFixed(1), 20, 308);
ctx.fillStyle = rColor;
ctx.font = 'bold 14px sans-serif';
ctx.fillText('r = ' + stats.r.toFixed(3), 20, 328);
ctx.fillStyle = '#64748b';
ctx.font = '11px sans-serif';
ctx.fillText('n = ' + allData.length + '点', 20, 346);
}
ctx.fillStyle = '#94a3b8';
ctx.font = '12px sans-serif';
ctx.textAlign = 'right';
ctx.fillText('赤点: マウス位置の仮想データ点', W - 20, 362);
requestAnimationFrame(loop);
}
loop(); 回帰直線の解釈
- 傾き : が 1 増えると は平均 1.20 増える——「勉強時間が1時間増えると点数が1.2点上がる」
- 切片 : のときの予測値(注:外挿は危険——データの範囲外の予測は信頼できない)
- :強い正の相関
決定係数 R²
は「回帰直線が Y の変動をどれだけ説明できるか」の割合——「当てはまりの良さ」を0から1で表します:
- なら、Y の変動の 90% が X の変動で説明できる
- 残り 10% は説明できない変動(誤差)——「他の要因の影響や測定誤差」
相関係数の注意点
- 非線形関係: は線形関係のみを検出——U字型の関係では でも強い関係がある。「年齢と体力はU字型かもしれない」
- 外れ値の影響: は外れ値に敏感——1点が大きく を変えることがある。デモで端に赤点を置くと直線が大きく動く様子で確認できます
- 擬似相関:第3変数(交絡因子)が両方に影響している場合——「アイスと溺死」の例のように
まとめ
- 相関係数 :、線形関係の強さと方向を示す——「どれくらい直線状に並んでいるか」
- 回帰直線 :最小二乗法で求める、 を必ず通る——「全データへの距離の二乗和を最小化する直線」
- 、
- 決定係数 :回帰の説明力——「0.9なら90%説明できる」
- 相関は因果ではない、外れ値・外挿・擬似相関に注意——「数字だけ見て早合点しない」
次回は統計的推測の基本——サンプルから母集団を推定する方法と中心極限定理を学びます。