目次
サンプル・プログラムの実行例


ここで注意してほしいことがある――婚姻率や離婚率と出生率の間に相関関係があるからといっても、それは、婚姻は離婚が原因で出生率が低下しているという結果(因果関係)になっているわけではない。つまり、相関関係と因果関係は別物であるということだ。

2つのデータ集合X, Yがあり、Xが変化すればYも変化することを相関関係という。
これに対し、X(原因)→Y(結果)という一方通行の関係が因果関係である。相関関係は因果関係の必要条件になり得るが、十分条件ではないのである。
サンプル・プログラム
correlation.php | サンプル・プログラム本体 |
birthrate.csv | 統計表 |
pahooStat.php | 統計に関わるクラス pahooStat。 使い方は「PHPで相関係数と回帰直線を表示」などを参照。include_path が通ったディレクトリに配置すること。 |
解説:準備
correlation.php
35: // jqPlotのあるフォルダ
36: define('JQPLOT', '../../../../common/jqplot/');
37:
38: // 統計表
39: define('FILE_CSV', 'birthrate.csv');
40:
41: // CSVデリミタ
42: define('CSV_DELIMITER', ',');
43:
44: // グラフの幅、高さ
45: define('GRAPH_WIDTH', 600);
46: define('GRAPH_HEIGHT', 350);
47:
48: // 婚姻率、離婚率、合計特殊出生率 推移グラフの名前
49: define('GRAPH_PROGRESS', 'jqPlot_progress');
50:
51: // 合計特殊出生率=婚姻率 相関グラフの名前
52: define('GRAPH_MARRIAGE', 'jqPlot_marriage');
53:
54: // 合計特殊出生率=離婚率 相関グラフの名前
55: define('GRAPH_DIVORCE', 'jqPlot_divorce');
56:
57: // 合計特殊出生率=初婚年齢 相関グラフの名前
58: define('GRAPH_1STMARRIAGE', 'jqPlot_1stmarriage');
59:
60: // 統計に関わるクラス:include_pathが通ったディレクトリに配置
61: require_once('pahooStat.php');
jqPlot の入手方法、インストール方法については、「PHPでNHK政治意識月例調査をグラフ表示」を参照してほしい。

相関係数は PECL の統計ライブラリを導入すれば計算できるが、今回は、自前のクラスを用意することにした。
クラスについては「PHPでクラスを使ってテキストの読みやすさを調べる」で解説している。
クラスファイル "pahooStat.php" は、組み込み関数 require_once を使って読めるディレクトリに配置する。ディレクトリは、設定ファイル php.ini に記述されているオプション設定 include_path に設定しておく。
pahooStat クラスの内容については後述する。

毎年の婚姻率、離婚率、合計特殊出生率、平均初婚年齢(妻)は、厚生労働省「令和5年(2023)人口動態統計月報年計(概数)の概況」をExcelに入力し、統計表ファイル FILE_CSV(CSV形式ファイル)として保存してある。
これらのデータを参照し、各種グラフを描いてゆく。
解説:データの検査
pahooStat.php
61: /**
62: * 配列かどうか、要素が1つ以上あるかどうか検査する
63: * @param array $x データ配列
64: * @return bool TRUE:配列である/FALSE:配列でない
65: */
66: function is_data($x) {
67: $res = TRUE;
68: if (! is_array($x)) {
69: $this->error = TRUE;
70: $this->errmsg = '配列でない';
71: $res = FALSE;
72: } else if (count($x) < 1) {
73: $this->error = TRUE;
74: $this->errmsg = '配列要素がない';
75: $res = FALSE;
76: }
77: return $res;
78: }
解説:相加平均
pahooStat.php
80: /**
81: * 相加平均
82: * @param array $x データ配列
83: * @return double 平均/NULL
84: */
85: function arithmetic_mean($x) {
86: if (! $this->is_data($x)) return NULL;
87:
88: return array_sum($x) / count($x);
89: }
Excelの AVERAGE関数に相当する。
\[ \displaystyle \overline{x}\ =\ \frac{1}{n}\ \sum_{i=1}^{n}\ x_i \]
解説:分散
pahooStat.php
91: /**
92: * 分散
93: * @param array $x データ配列
94: * @return double 分散
95: */
96: function variance($x) {
97: if (! $this->is_data($x)) return NULL;
98:
99: $xm = $this->arithmetic_mean($x);
100: $xs = 0;
101: $n = count($x);
102: $xs = 0;
103: for ($i = 0; $i < $n; $i++) {
104: $xs += ($x[$i] - $xm) * ($x[$i] - $xm);
105: }
106:
107: return $xs / $n;
108: }
解説:標準偏差
pahooStat.php
130: /**
131: * 標準偏差
132: * @param array $x データ配列
133: * @return double 標準偏差
134: */
135: function standard_deviation($x) {
136: if (! $this->is_data($x)) return NULL;
137:
138: return sqrt($this->variance($x));
139: }
解説:共分散
pahooStat.php
110: /**
111: * 共分散
112: * @param array $x, $y データ配列
113: * @return double 共分散
114: */
115: function covariance($x, $y) {
116: if (! $this->is_data($x)) return NULL;
117: if (! $this->is_data($y)) return NULL;
118:
119: $xm = $this->arithmetic_mean($x);
120: $ym = $this->arithmetic_mean($y);
121: $n = min(count($x), count($y));
122: $xs = 0;
123: for ($i = 0; $i < $n; $i++) {
124: $xs += ($x[$i] - $xm) * ($y[$i] - $ym);
125: }
126:
127: return $xs / $n;
128: }
解説:相関係数
pahooStat.php
141: /**
142: * 相関係数
143: * @param array $x, $y データ配列
144: * @return double 相関係数
145: */
146: function correl($x, $y) {
147: if (! $this->is_data($x)) return NULL;
148: if (! $this->is_data($y)) return NULL;
149:
150: return $this->covariance($x, $y) / ($this->standard_deviation($x) * $this->standard_deviation($y));
151: }
Excelの CORREL関数に相当する。
\[ \displaystyle r\ =\ \frac{s_{xy}}{s_x\ s_y}\ =\ \frac{\frac{1}{n}\ \sum_{i=1}^{n}\ (x_i\ -\ \overline{x})(y_i\ -\ \overline{y})}{\sqrt{\frac{1}{n}\ \sum_{i=1}^{n}\ (x_i\ -\ \overline{x})^2}\ \sqrt{\frac{1}{n}\ \sum_{i=1}^{n}\ (y_i\ -\ \overline{y})^2}} \]
解説:相関の強さ
pahooStat.php
153: /**
154: * 相関の強さ
155: * @param double $r 相関係数
156: * @return string 相関の強さ(テキスト)
157: */
158: function correl_strength($r) {
159: $table = array(
160: array(-0.7, '強い負の相関'),
161: array(-0.4, '負の相関'),
162: array(-0.2, '弱い負の相関'),
163: array(+0.2, 'ほとんど相関がない'),
164: array(+0.4, '弱い正の相関'),
165: array(+0.7, '正の相関'),
166: array(+1.1, '強い正の相関')
167: );
168:
169: foreach ($table as $item) {
170: if ($r < $item[0]) return $item[1];
171: }
172: return FALSE;
173: }
相関係数 r | 相関の強さ |
---|---|
-1.0 ≦ r < -0.7 | 強い負の相関 |
-0.7 ≦ r < -0.4 | 負の相関 |
-0.4 ≦ r < -0.2 | 弱い負の相関 |
-0.2 ≦ r < +0.2 | ほとんど相関がない |
+0.2 ≦ r < +0.4 | 弱い正の相関 |
+0.4 ≦ r < +0.7 | 正の相関 |
+0.7 ≦ r ≦ +1.0 | 強い正の相関 |
解説:回帰直線
pahooStat.php
175: /**
176: * 回帰係数、切片
177: * @param array $x, $y データ配列
178: * @return array(回帰係数,切片)
179: */
180: function slope($x, $y) {
181: if (! $this->is_data($x)) return NULL;
182: if (! $this->is_data($y)) return NULL;
183:
184: $a = $this->covariance($x, $y) / $this->variance($x);
185: $b = $this->arithmetic_mean($y) - $a * $this->arithmetic_mean($x);
186:
187: return array($a, $b);
188: }
Excelの SLOPE関数、INTERCEPT関数に相当する。

回帰直線を \( y\ =\ ax\ +\ b \) とするとき、a, b は次の式によって求める。
\[ \displaystyle a\ =\ \frac{s_{xy}}{{s_x}^2}\ =\ \frac{\sum_{i=1}^{n}\ (x_i\ -\ \overline{x})(y_i\ -\ \overline{y})}{\sum_{i=1}^{n}\ (x_i\ -\ \overline{x})^2}
b\ =\ \overline{y}\ -\ a\overline{x} \]
解説:散布図
correlation.php
298: /**
299: * jqPlot用のスクリプト:散布図
300: * @param array $x, $y データ配列
301: * @param string $label_x, $label_y 軸ラベル
302: * @param string $name オブジェクト名
303: * @return string スクリプト/FALSE
304: */
305: function plotScatter($x, $y, $label_x, $label_y, $name) {
306: // 相関係数、回帰直線
307: $pst = new pahooStat();
308: $r = $pst->correl($x, $y); // 相関係数
309: if ($pst->iserror()) return FALSE;
310: $st = $pst->correl_strength($r);
311: $r = sprintf('%4.3f (%s)', $r, $st);
312: list($a, $b) = $pst->slope($x, $y); // 回帰係数、切片
313: if ($pst->iserror()) return FALSE;
314: $x0 = min($x);
315: $y0 = $a * $x0 + $b;
316: $x1 = max($x);
317: $y1 = $a * $x1 + $b;
318: $pst = NULL;
319:
320: // 系列の生成
321: $series = '';
322: $n = min(count($x), count($y));
323: for ($i = 0; $i < $n; $i++) {
324: $series .= sprintf('[%f, %f], ', $x[$i], $y[$i]);
325: }
326:
327: $width = GRAPH_WIDTH;
328: $height = GRAPH_HEIGHT;
329: $js =<<< EOT
330: <script>
331: jQuery(function() {
332: jQuery.jqplot('{$name}',
333: [
334: [ {$series} ]
335: ],
336: {
337: // タイトル
338: title: {
339: text: '{$label_x}={$label_y}<br /><span style="font-size:small; color:blue;">相関係数 = {$r}</span>',
340: show: true,
341: fontSize: '20px',
342: textAlign: 'center',
343: textColor: 'black'
344: },
345: // 散布図
346: seriesDefaults: {
347: showLine: false,
348: color: 'green',
349: markerOptions: { size: 4 }
350: },
351: // 回帰直線
352: canvasOverlay: {
353: show: true,
354: objects: [
355: {
356: line: {
357: lineWidth: 3,
358: color: 'blue',
359: shadow: false,
360: lineCap: 'round',
361: start: [ {$x0}, {$y0} ],
362: stop: [ {$x1}, {$y1} ]
363: }
364: }]
365: },
366: // 軸
367: axes: {
368: xaxis: {
369: label: '{$label_x}',
370: tickOptions: { formatString: '%.1f' }
371: },
372: yaxis: {
373: label: '{$label_y}',
374: tickOptions: { formatString: '%.1f' }
375: }
376: }
377: }
378: );
379: });
380: </script>
381: <div id="{$name}" style="width:{$width}px; height:{$height}px;"></div>
382:
383: EOT;
384:
385: return $js;
386: }

回帰直線は、X軸の最小値を始点とし、同じく最大値を終点とする直線で、jqPlotプラグインの「jqplot.dateAxisRenderer.js」(グラフに描き加える線)で行う。
解説:年次推移グラフ
correlation.php
197: /**
198: * jqPlot用のスクリプト:年次推移グラフ
199: * @param array $items データ配列
200: * @param array $labels ラベル配列
201: * @param string $name オブジェクト名
202: * @param string $title グラフ・タイトル
203: * @return string スクリプト
204: */
205: function plotProgress($items, $labels, $name, $title) {
206: // 系列の生成
207: $series = array();
208: $n = count($labels);
209: for ($i = 1; $i < $n; $i++) {
210: $series[$i] = '';
211: foreach ($items[$i] as $key=>$val) {
212: $series[$i] .= sprintf('[%d, %f], ', $items[0][$key], $val);
213: }
214: }
215: $s1 = '';
216: $s2 = '';
217: foreach ($series as $key=>$val) {
218: $s1 .= "[ {$val} ], ";
219: if (max($items[$key]) > 20) {
220: $s2 .=<<< EOT
221: {
222: label: '{$labels[$key]}',
223: yaxis: 'y2axis'
224: },
225:
226: EOT;
227: } else {
228: $s2 .=<<< EOT
229: {
230: label: '{$labels[$key]}'
231: },
232:
233: EOT;
234: }
235: }
236:
237: $width = GRAPH_WIDTH;
238: $height = GRAPH_HEIGHT;
239: $js =<<< EOT
240: <script>
241: jQuery(function() {
242: jQuery.jqplot('{$name}',
243: [
244: {$s1}
245: ],
246: {
247: // タイトル
248: title: {
249: text: '{$title}',
250: show: true,
251: fontSize: '20px',
252: textAlign: 'center',
253: textColor: 'black'
254: },
255: // ラベル
256: series: [
257: {$s2}
258: ],
259: legend: {
260: show: true,
261: placement: 'inside',
262: location: 'ne',
263: renderer: $.jqplot.EnhancedLegendRenderer,
264: rendererOptions: { numberRows: 1 }
265: },
266: // グラフ
267: seriesDefaults: {
268: showLine: true,
269: rendererOptions: { smooth: false },
270: markerOptions: { size: 0 },
271: },
272: // 軸
273: axes: {
274: xaxis: {
275: label: '{$labels[0]}',
276: tickOptions: { formatString: '%d' }
277: },
278: yaxis: {
279: label: '率',
280: tickOptions: { formatString: '%.1f' }
281: },
282: y2axis: {
283: label: '年齢',
284: tickOptions: { formatString: '%d' }
285: }
286: }
287: }
288: );
289: });
290: </script>
291: <div id="{$name}" style="width:{$width}px; height:{$height}px;"></div>
292:
293: EOT;
294:
295: return $js;
296: }
参考サイト
- PHPでNHK政治意識月例調査をグラフ表示:ぱふぅ家のホームページ
- jqPlot:アルファシス
- Sci-pursuit
- 「相関関係」と「因果関係」の違いを理解すれば根拠のない通説にだまされなくなる!:ダイヤモンド・オンライン
なぜ出生率は上昇しないのか――婚姻率の低下と離婚率の上昇が関係しているのではないかと考え、それを確かめるため、高校数学で習った相関係数を求めてみることにする。
あわせて、データ集合の散布図と回帰曲線を描いてみる。
(2025年1月26日)統計表を更新,PHP8.4対応