Up

男女出生率のベイズ分析

 

ラプラスの男女の出生数データが、B. Lambert (2018), A students guide to Bayesian Statistics, SAGE Publications Ltdp. 16に、女児241,945人、男児251,527人として載っており、ラプラスはこのデータから「女子の方が男子より産まれやすい」という確率が約であると計算したと説明されている。このことを確認するPythonスクリプトを用意してみた。

確率分布として2項分布を設定する。2項分布を用いたベイズ分析については、<岡本安晴「いまさら聞けないPythonでデータ分析」丸善出版>、pp. 169-176などを参照されたい。

女児が産まれる確率をpとおくと、データ「女児241,945人、男児251,527人」に対する事後分布は、事前分布として一様分布を設定したとき

で与えられる。すなわち、ベータ分布である。この分布を描くPythonスクリプトをリスト1のように用意した。

 

リスト1

import scipy.stats as ss

import numpy as np

import matplotlib.pyplot as plt

 

dist = ss.beta(241945+1, 251527+1)

 

x = np.linspace(0, 1, 1000)

y = dist.pdf(x)

 

plt.rcParams['font.family'] = ['Yu Gothic', 'Noto Sans CJK JP', 'Hiragino Maru Gothic Pro']

 

plt.title('女子の出生確率pの分布', fontsize = 20)

plt.plot(x, y)

plt.vlines(0.5, 0, 500, color = 'r', label = 'p = 0.5')

plt.xlabel('p', fontsize = 16)

plt.yticks([])

plt.legend()

plt.savefig('Fig_1.png')

plt.show()

 

リスト1のスクリプトを実行すると、図1のグラフが表示される。

図1

 

事後分布がp=0.5の近くに集まっているので、横軸を拡大して表示するため、リスト2のスクリプトを用意した。

 

リスト2

import scipy.stats as ss

import numpy as np

import matplotlib.pyplot as plt

 

dist = ss.beta(241945+1, 251527+1)

 

x_1 = np.linspace(0.48, 0.52, 1000)

y_1 = dist.pdf(x_1)

 

plt.rcParams['font.family'] = ['Yu Gothic', 'Noto Sans CJK JP', 'Hiragino Maru Gothic Pro']

 

plt.title('女子の出生確率pの分布', fontsize = 20)

plt.plot(x_1, y_1)

plt.vlines(0.5, 0, 500, color = 'r', label = 'p = 0.5')

plt.xlabel('p', fontsize = 16)

plt.yticks([])

plt.legend()

plt.savefig('Fig_2.png')

plt.show()

 

リスト2のスクリプトを実行すると図2のグラフを得る。

図2

 

事後分布がp=0.5から明確に離れていることが分かる。図2のグラフから、「p>0.5」すなわち「女児の方が産まれやすい確率」はほぼ0であると言えるが、事後分布における「p>0.5」の確率を求めてみる。非常に小さい(小数点以下の桁数が実数の精度より小さい)値であるので、男児の産まれる確率をpとしてベータ分布の累積確率分布関数のp=0.5における値として求める。Pythonスクリプトをリスト3に示す。

 

リスト3

import scipy.stats as ss

 

dist_1 = ss.beta(251527+1, 241945+1)

print('p = ', dist_1.cdf(0.5))

 

リスト3の実行例を図3に示す。

図3

 

Laplaceの計算結果、約が確認できた。

 

ちなみに、女児の産まれる確率の95%確信区間を求めると

95% CI = [0.489, 0.492]

を得た。スクリプトをリスト4に示す。

 

リスト4

import numpy as np

import scipy.stats as ss

import scipy.optimize as so

 

class BirthRates:

    def __init__(self, ng, nb):

        self.MyBeta = ss.beta(ng + 1, nb + 1)

        self.z_v = 0.0

    def set_z_v(self, z_v):

        self.z_v = z_v

    #       累積確率分布 - z_v

    def f(self, v):

        return self.MyBeta.cdf(v) - self.z_v

    #       fの導関数

    def df(self, v):

        return self.MyBeta.pdf(v)

 

BR = BirthRates(241945, 251527)

f = BR.f

df = BR.df

 

x = np.linspace(0, 1, 1000)

y = df(x)

v_init = x[np.argmax(y)]    #   Newton法の初期値

 

BR.set_z_v(0.025)           #   累積確率が0.025になる値を求める。

v025 = so.newton(f, v_init, df)

 

BR.set_z_v(0.975)           #   累積確率が0.975になる値を求める

v975 = so.newton(f, v_init, df)

 

print('95% CI = [{0:.3f}, {1:.3f}]'.format(v025, v975))

 

 

Up