PHPで標本誤差を求める

(1/1)
世論調査のように、全有権者という母集団から1千人程度の標本を無作為抽出し、その調査結果が発表される。だが、完全に無作為抽出された標本であっても、調査結果が母集団のそれと完全一致するものではないことが、統計学的に示されている。これを標本誤差と呼ぶ。
今回は、PHPを使って標本誤差を計算するプログラムを作ってみる。

目次

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

PHPで標本誤差を求める
初期値として、2019年(令和元年)11月16、17日に実施した朝日新聞の世論調査における内閣支持率の値を調査結果として用意した。内閣支持率は44%だが、信頼区間95%としたときの標本誤差は41.0~47.0%の範囲だ。
同じ時期(11月9日、10日)に実施した JNNの世論調査では、標本数は1,139人とほぼ同じだが、内閣支持率は54.3%と、朝日新聞の調査より10%も多い。
本プログラムで信頼区間95%としたときの標本誤差を計算すると、51.4~57.2% となり、朝日新聞の標本誤差と重ならない。標本は母集団(有権者数)を代表しているものであるから、標本誤差の範囲は重ならなければいけない。
つまり、朝日新聞とJNNの世論調査のいずれか、または双方において、下記の事態が発生していると推測できる。
  1. 信頼区間外(5%の確率)で、たまたま違う調査結果となった。
  2. 標本が無作為抽出になっていない。
  3. 有効回答にバイアスがかかっている。


時間がある方は、両社の過去の内閣支持率の標本誤差を計算してみてほしい。ほぼ毎回のように重ならないだろう。
ここからは個人的観測になるが、1の可能性は棄却できるだろう。かりにも大手マスコミによる世論調査だから、2ということはあるまい。となると、3の可能性が残る――電話による世論調査で、「朝日新聞です」と名乗った場合と、「JNNです」と名乗った場合では、回答を拒否する層が異なるのではないだろうか。

サンプル・プログラムのダウンロード

圧縮ファイルの内容
SamplingError.phpサンプル・プログラム本体

全数調査と標本調査

中学校で生徒の平均身長を求めようとする場合、その学校の全生徒の健診データから身長を抜き出して平均値を計算すればいい。これを全数調査と呼ぶ。
ここで、調査の範囲を市町村、都道府県、全国と広げていった場合、どうなるだろう。健診の時期が異なっていたり、健診そのものが行われていなかったりと、全数調査を行うには莫大の費用と時間がかかるだろう。
そこで、母集団から一部の人を抽出して、その身長の平均値を計算する標本調査を行い、母集団の平均身長を推測する。

抽出された標本が母集団の性質を反映するためには、無作為抽出法が用いられる。無作為抽出法にはいくつかの手段があるが、詳しくは「標本の抽出方法」(統計WEB)をご覧いただきたい。

標本誤差

標本調査は統計学的処理である以上、全数調査を行ったときに得られる真の値からの誤差を避けることはできない。これを標本誤差と呼び、計算で求めることができる。
\[ \displaystyle ta(m,x) \quad \times \quad \sqrt{\frac{N-n}{N-1} \quad \times \quad \frac{p(1-p)}{n}} \]
ここで \( ta(m,x) \)‥‥t分布の確率密度関数.\( n \)‥‥自由度,\( x \)‥‥確率変数
\( N \)‥‥母集団の数
\(n \)‥‥標本の数
\( p \)‥‥調査結果

標本誤差によって、調査結果は範囲のある数字となる。これを区間推定と呼ぶ

信頼区間

区間推定を行う場合、その区間の信頼性、すなわち精度が問われる。これを信頼区間と呼ぶ。つまり、区間推定を行う場合、標本の調査結果に加え、信頼区間を指定してやらなければならない。

標本誤差を求める際に用いたt分布の確率密度関数 \( ta(m,x) \) において、m:自由度は標本数を、x:確率変数は信頼区間をあらわす。

解説:t分布の確率密度

t分布の確率密度を求める関数としては、PECL::stats_dens_t が用意されているが、PECL環境が用意されていない処理系のために、ユーザー関数 dens_t を用意した。自由度と確率変数を渡すと、t分布表を参照し、確率密度を返す。
自由度と確率変数が合致しない場合、計算を簡単にするため、補間せず、次の確率密度を返すようにしている。実用上差し支えないだろう。

 184: /**
 185:  * t分布の確率密度関数
 186:  * @param   float $m 自由度
 187:  * @param   float $x 確率変数
 188:  * @return  float 確率密度/FALSE:引数が不正
 189: */
 190: function dens_t($m, $x) {
 191: //t分布表
 192: static $table_pa = array(0.25, 0.1, 0.05, 0.025, 0.01, 0.005);
 193: //自由度/片側確率 0.25  0.1  0.05  0.025   0.01   0.005
 194: static $table_ta = array(
 195:   1 => array(1.000, 3.078, 6.314, 12.706, 31.821, 63.657),
 196:   2 => array(0.816, 1.886, 2.920, 4.303, 6.965, 9.925),
 197:   3 => array(0.765, 1.638, 2.353, 3.182, 4.541, 5.841),
 198:   4 => array(0.741, 1.533, 2.132, 2.776, 3.747, 4.604),
 199:   5 => array(0.727, 1.476, 2.015, 2.571, 3.365, 4.032),
 200:   6 => array(0.718, 1.440, 1.943, 2.447, 3.143, 3.707),
 201:   7 => array(0.711, 1.415, 1.895, 2.365, 2.998, 3.499),
 202:   8 => array(0.706, 1.397, 1.860, 2.306, 2.896, 3.355),
 203:   9 => array(0.703, 1.383, 1.833, 2.262, 2.821, 3.250),
 204:  10 => array(0.700, 1.372, 1.812, 2.228, 2.764, 3.169),
 205:  11 => array(0.697, 1.363, 1.796, 2.201, 2.718, 3.106),
 206:  12 => array(0.695, 1.356, 1.782, 2.179, 2.681, 3.055),
 207:  13 => array(0.694, 1.350, 1.771, 2.160, 2.650, 3.012),
 208:  14 => array(0.692, 1.345, 1.761, 2.145, 2.624, 2.977),
 209:  15 => array(0.691, 1.341, 1.753, 2.131, 2.602, 2.947),
 210:  16 => array(0.690, 1.337, 1.746, 2.120, 2.583, 2.921),
 211:  17 => array(0.689, 1.333, 1.740, 2.110, 2.567, 2.898),
 212:  18 => array(0.688, 1.330, 1.734, 2.101, 2.552, 2.878),
 213:  19 => array(0.688, 1.328, 1.729, 2.093, 2.539, 2.861),
 214:  20 => array(0.687, 1.325, 1.725, 2.086, 2.528, 2.845),
 215:  21 => array(0.686, 1.323, 1.721, 2.080, 2.518, 2.831),
 216:  22 => array(0.686, 1.321, 1.717, 2.074, 2.508, 2.819),
 217:  23 => array(0.685, 1.319, 1.714, 2.069, 2.500, 2.807),
 218:  24 => array(0.685, 1.318, 1.711, 2.064, 2.492, 2.797),
 219:  25 => array(0.684, 1.316, 1.708, 2.060, 2.485, 2.787),
 220:  26 => array(0.684, 1.315, 1.706, 2.056, 2.479, 2.779),
 221:  27 => array(0.684, 1.314, 1.703, 2.052, 2.473, 2.771),
 222:  28 => array(0.683, 1.313, 1.701, 2.048, 2.467, 2.763),
 223:  29 => array(0.683, 1.311, 1.699, 2.045, 2.462, 2.756),
 224:  30 => array(0.683, 1.310, 1.697, 2.042, 2.457, 2.750),
 225:  31 => array(0.682, 1.309, 1.696, 2.040, 2.453, 2.744),
 226:  32 => array(0.682, 1.309, 1.694, 2.037, 2.449, 2.738),
 227:  33 => array(0.682, 1.308, 1.692, 2.035, 2.445, 2.733),
 228:  34 => array(0.682, 1.307, 1.691, 2.032, 2.441, 2.728),
 229:  35 => array(0.682, 1.306, 1.690, 2.030, 2.438, 2.724),
 230:  36 => array(0.681, 1.306, 1.688, 2.028, 2.434, 2.719),
 231:  37 => array(0.681, 1.305, 1.687, 2.026, 2.431, 2.715),
 232:  38 => array(0.681, 1.304, 1.686, 2.024, 2.429, 2.712),
 233:  39 => array(0.681, 1.304, 1.685, 2.023, 2.426, 2.708),
 234:  40 => array(0.681, 1.303, 1.684, 2.021, 2.423, 2.704),
 235:  41 => array(0.681, 1.303, 1.683, 2.020, 2.421, 2.701),
 236:  42 => array(0.680, 1.302, 1.682, 2.018, 2.418, 2.698),
 237:  43 => array(0.680, 1.302, 1.681, 2.017, 2.416, 2.695),
 238:  44 => array(0.680, 1.301, 1.680, 2.015, 2.414, 2.692),
 239:  45 => array(0.680, 1.301, 1.679, 2.014, 2.412, 2.690),
 240:  50 => array(0.679, 1.299, 1.676, 2.009, 2.403, 2.678),
 241:  60 => array(0.679, 1.296, 1.671, 2.000, 2.390, 2.660),
 242:  80 => array(0.678, 1.292, 1.664, 1.990, 2.374, 2.639),
 243: 120 => array(0.677, 1.289, 1.658, 1.980, 2.358, 2.617),
 244: 999 => array(0.674, 1.282, 1.645, 1.960, 2.326, 2.576)      //m=∞
 245: );
 246: 
 247:     //引数チェック
 248:     if ($m < 1)      return FALSE;
 249:     if ($m > 999)   $m = 999;
 250:     if ($x < 0)      return FALSE;
 251:     if ($x > 0.5)   return FALSE;
 252:     $x = $x / 2;
 253:     $res = FALSE;
 254: 
 255:     //t分布表の探索
 256:     foreach ($table_ta as $key=>$ta) {
 257:         $n = count($ta);
 258:         for ($i = 0$i < $n$i++) {
 259:             if ($x >$table_pa[$i]) {
 260:                 if ($m <$key)      return $ta[$i];
 261:                 else                break;
 262:             }
 263:         }
 264:         if ($m <$key)      return $ta[$i - 1];
 265:     }
 266:     return $res;
 267: }

解説:標本誤差

 269: /**
 270:  * 標本誤差
 271:  * @param   int   $n 母集団の数
 272:  * @param   int   $m 標本数
 273:  * @param   float $p 調査結果(0.0≦$p≦1.0)
 274:  * @param   float $x 確率変数
 275:  * @return  float 標本誤差/FALSE:引数が不正
 276: */
 277: function sampling_error($n, $m, $p, $x) {
 278:     //エラーチェック
 279:     if ($n <0return FALSE;
 280:     if ($m <0return FALSE;
 281:     if (($p < 0|| ($p > 1))    return FALSE;
 282: 
 283:     $pt = dens_t($n, $x);
 284:     if ($pt == FALSE)       return FALSE;
 285: 
 286:     return $pt * sqrt(($n - $m) / ($n - 1* ($p * (1 - $p) / $m));
 287: }

ユーザー関数 sampling_error は、母集団の数、標本数、調査結果(0.0≦$p≦1.0)、確率変数を与え、標本誤差を返す。
まず、引数が定義域内にあるかどうかチェックする。
つづいて、dens_t 関数でt分布の確率密度を求め、公式通りに標本誤差を計算する。

参考サイト

(この項おわり)
header