札幌医科大学入試結果分析
この記事では札幌医科大学ホームページで公表されている受験者のデータをもとに、受験者全体の平均点、標準偏差を大雑把に見積もっていきたいと思います。
1.得点分布の仮定
大学から公表されているデータでは合格者の平均点、最高点、最低点、受験者数のみしか把握できないので、とりあえず受験者の得点が正規分布に従うものと仮定して、与えられたデータを用いて正規分布のパラメータ $\mu, \sigma$ を予想してみました。
2.データ収集
まず、予想に用いたデータを表にまとめました。
ここで最下位の順位が毎年75となっているのは、札幌医科大学の一般入試における定員が75人であり、追加合格者が発生した場合も75番目の合格者の得点を最低点として公表するためです。
3.分析方法
分析方法はいたってシンプルです。以下の連立方程式を解くだけです。
\left\{
\begin{split}
合格者の割合 &= \int_{最低点}^{\infty} \frac{1}{\sqrt{2 \pi} \sigma} \exp (-\frac{(x - \mu)^2}{2 \sigma^2}) dx \\
合格者の平均点 &= \frac{\int_{最低点}^{\infty} \frac{x}{\sqrt{2 \pi} \sigma} \exp (-\frac{(x - \mu)^2}{2 \sigma^2})}{\int_{最低点}^{\infty} \frac{1}{\sqrt{2 \pi} \sigma} \exp (-\frac{(x - \mu)^2}{2 \sigma^2}) dx}
\end{split}
\right.
少し補足説明します。一つ目の式は
合格者の割合 = \frac{最下位の順位}{受験者数} = \int_{最低点}^{\infty} 正規分布 dx
という意味です。正規分布を最低点から無限大まで積分したら合格者の割合が得られますよね。
二つ目の式は
合格者の平均点 = 合格者得点の期待値 = \int_{最低点}^{\infty}規格化定数 \times x \times 正規分布 dx = \frac{\int_{最低点}^{\infty}x \times 正規分布 dx}{\int_{最低点}^{\infty} 正規分布 dx}
$ $ という意味です。規格化定数は $ C \times \int_{最低点}^{\infty} 正規分布 dx = 1$とするための定数 $C$ で、つまり $1/ \int_{最低点}^{\infty} 正規分布 dx $ のことです。
$ $ さて、ここで問題となるのが、未知数が二つの連立方程式が得られたのであとは計算するだけかと思いきや、一つ目の式の積分は$\mu, \sigma$が未知のままでは計算することができません。ですから数学的に厳密な解を求めるのはあきらめて、様々な$\mu, \sigma$の値の組を代入して最も良く近似できそうな組を探すことにしました。とはいってもこんな面倒くさい計算はやってられないので、pyhonの登場です。
4.Pyhonを用いて近似解を求める
まず必要なライブラリやモジュールをインポートします。
import numpy as np
import scipy.stats as st
import matplotlib.pyplot as plt
from scipy import integrate
import japanize_matplotlib
続いて予測に必要なデータを作成します。
mu_I = [950,1000]
sigma_I = [60,90]
year = [2018,2019,2020]
n = [321,267,281]
pass_n = 75
pass_ratio = [pass_n/i for i in n]
pass_average = [1063,1073,1072]
worst = [1023,1029,1022]
mu_points = np.linspace(mu_I[0],mu_I[1],100)
sigma_points = np.linspace(sigma_I[0],sigma_I[1],60)
pass_ratio_err = 0.005
pass_average_err = 1
さらに合格者の得点の期待値の計算に必要な関数を定義します。
def norm(x,mu, sigma):
return (x/(np.sqrt(2*np.pi)*sigma))*np.exp(-(x - mu)**2/(2*(sigma**2)))
そして、以下のコードでプロット点$(\mu,\sigma)$を
- 「合格者の割合を$\pm 0.5$ %の精度にする」が「合格者平均点を$\pm 1$点の精度にしない」(黄)
- 「合格者の割合を$\pm 0.5$ %の精度にしない」が「合格者平均点を$\pm 1$点の精度にする」(青)
- 「合格者の割合を$\pm 0.5$ %の精度にする」かつ「合格者平均点を$\pm 1$点の精度にする」(緑)
の3通りに分類しました。
ratio_average = []
ratio_only = []
average_only = []
for i in range(len(year)):
ratio_average.append([[],[]])
ratio_only.append([[],[]])
average_only.append([[],[]])
for mu_point in mu_points:
for sigma_point in sigma_points:
mu = mu_point
sigma = sigma_point
cdf = st.norm.cdf(worst[i], mu, sigma)
int_pdf = integrate.quad(norm,worst[i], np.inf, args = (mu, sigma))[0]
calculate_pass_ratio = 1 - cdf
calculate_pass_average = int_pdf / calculate_pass_ratio
if np.abs(calculate_pass_ratio - pass_ratio[i]) < pass_ratio_err:
if np.abs(calculate_pass_average - pass_average[i]) < pass_average_err:
ratio_average[i][0].append(mu)
ratio_average[i][1].append(sigma)
else:
ratio_only[i][0].append(mu)
ratio_only[i][1].append(sigma)
elif np.abs(calculate_pass_average - pass_average[i]) < pass_average_err:
average_only[i][0].append(mu)
average_only[i][1].append(sigma)
else:
pass
最後に、分類した点を色分けしてグラフにプロットしました。
fig , axes = plt.subplots(1,3,figsize = (18,5))
for i, ax in zip([0,1,2],axes):
ax.scatter(ratio_only[i][0],ratio_only[i][1],c = 'y', s = 2, label= '合格者の割合が{:.3f} $\\pm$ {}%'.format(pass_ratio[i], pass_ratio_err*100))
ax.scatter(average_only[i][0],average_only[i][1],c = 'b', s = 2,label = '合格者平均が{} $\\pm$ {}点'.format(pass_average[i], pass_average_err))
ax.scatter(ratio_average[i][0],ratio_average[i][1],c = 'g', s = 2, label = '以上の二つの条件をともに満たす')
ax.set_xlim(mu_I[0], mu_I[1])
ax.set_ylim(sigma_I[0], sigma_I[1])
ax.set_xlabel('$\\mu$')
ax.set_ylabel('$\\sigma$')
ax.legend(loc = 'best')
ax.set_title('{}年'.format(year[i]))
plt.show()
実行結果は以下のグラフのようになりました。
グラフの緑色の領域から$\mu, \sigma$のおよその値を値を読み取ると以下の表のようになりました。
5.結論
今年(2020年度)は2018年度に比べて合格者の平均点は高かったが、受験者の平均点は2018年度を下回る結果となった。また、年々標準偏差が上昇しており、受験者の間で点差が広がるような出題がなされるようになってきているといえる。
※お断り
あくまで正規分布に従うモデルを仮定したため、事実とは異なる場合がございます。