「基礎からのベイズ統計学」の入社試験問題で考えたこと

# <i class='fa fa-child' /> お題「入社試験問題」
[基礎からのベイズ統計学](http://www.amazon.co.jp/dp/4254122128/)を読んで考えたことです。
59ページより引用

> 入社試験問題：ある企業の入社試験では、毎年、同じ難しさの問題を7問出題します。
X大学のxさんは3問正解、4問不正解でした。正解率を$\theta_x$とします。
Y大学のyさんは4問正解、3問不正解でした。正解率を$\theta_y$とします。
X大学とY大学からは、毎年たくさんの受験者がいます。調べてみると、
X大学の受験者の正解率は平均0.8、分散0.04のベータ分布で近似され、
Y大学の受験者の正解率は平均0.4、分散0.04のベータ分布で近似されることがわかりました。
$\theta_x$と$\theta_y$を推定し、母数の値の大きな受験者を1人だけ入社させるとしたら、
xさんとyさんのどちらでしょう。

**「試験の結果だけでなく、その人の所属するグループの能力も併せて評価すると、より精度のよい推定ができる」**ことの是非に関する考察になります。

ここでは、この問題をそのまま解くのではなく、以下のように条件を変えて検討します。

- 学生の能力は1人1つの値とし、その能力値の高い方を選択できるか どうかを目的とします。
- X大学出身の受験者の能力は、平均105、標準偏差10の正規分布に従うとします。
- Y大学出身の受験者の能力は、平均100、標準偏差10の正規分布に従うとします。
- 入社試験の成績である観測値は、平均が自分の能力値、標準偏差10の正規分布に従うとします。
- **試験成績方式**では、観測値のみで評価します。
- **グループ併用方式**では、$補正値 \equiv \frac{グループの平均値＋観測値}{2}$ で評価することにします。

# <i class='fa fa-child' /> Pythonで確認してみる
各値を計算します。能力値でXが高いのはは63.8%、観測値でXが高いのは59.9%です。

```py3:python3
import numpy as np, pandas as pd
np.random.seed(1)
n = 1000000 # 評価回数
xave, yave, std = 105, 100, 10 # Xの平均、Yの平均、標準偏差
gx = np.random.normal(xave, std, (n)) # グループXの能力値
gy = np.random.normal(yave, std, (n)) # グループYの能力値
ox = np.random.normal(gx, std) # グループXの観測値
oy = np.random.normal(gy, std) # グループYの観測値
ax = (xave + ox) / 2 # グループXの補正値
ay = (yave + oy) / 2  # グループXの補正値

gf = gx > gy # 能力値でXが高い
of = ox > oy # 観測値でXが高い
af = ax > ay # 補正値でXが高い

print(gf.sum() / n, of.sum() / n)
>>>
0.638318 0.598666
```

## 試験成績方式
観測値による評価では、75.9%の正解率です。

```py3:python3
print(pd.DataFrame([[(gf&of).sum(), ((~gf)&of).sum()],
                    [(gf&(~of)).sum(), ((~gf)&(~of)).sum()]],
                  columns=['能力X', '能力Y'], index=['観測X', '観測Y']) / n)
print('正解率 = ', (gf==of).sum() / n)
>>>
          能力X       能力Y
観測X  0.498147  0.100519
観測Y  0.140171  0.261163
正解率 =  0.75931
```

## グループ併用方式
補正値による評価では、76.8%の正解率です。

```py3:python3
print(pd.DataFrame([[(gf&af).sum(), ((~gf)&af).sum()],
                    [(gf&(~af)).sum(), ((~gf)&(~af)).sum()]],
                  columns=['能力X', '能力Y'], index=['補正X', '補正Y']) / n)
print('正解率 = ', (gf==af).sum() / n)
>>>
          能力X       能力Y
補正X  0.549088  0.142374
補正Y  0.089230  0.219308
正解率 =  0.768396
```

## 考察
確かに、自分の能力だけでなく所属グループの能力を使うと、精度良く評価できました。
しかし、このような方法が本当に良いのでしょうか？
例えば、大学ではなく性別を使うのは、どうでしょうか？
おそらく男女差で合格可否を変えるのは、問題があるでしょう。

## 提案方式1
そこで、新たな方法を提案します。その方法は、以下のようなものです。

- 事前に別途試験を受けます。その結果を事前観測値とします。
- その試験の成績が気に入らない場合(事前観測値が自分の能力値以下の場合)は、何もしないとします。
- 気に入った場合は、自ら登録処理をします。登録されている場合、入社試験では、観測値と事前観測値の両方を使わなければいけないこととします。(登録されていなければ、観測値のみで構いません。)
- 全員が別途試験を受けられない可能性も考慮して、登録制を取り入れています。

```py3:python3
bx = np.random.normal(gx, std) # グループXの事前観測値
by = np.random.normal(gy, std) # グループYの事前観測値
# 事前観測値が自分の能力値以下であれば、元々の観測値とする
px = ox*(bx < gx) + (ox+bx)/2*(bx >= gx) # グループXの提案値1
py = oy*(by < gy) + (oy+by)/2*(by >= gy) # グループYの提案値1

pf = px > py # 提案値1でXが高い

print(pd.DataFrame([[(gf&pf).sum(), ((~gf)&pf).sum()],
                    [(gf&(~pf)).sum(), ((~gf)&(~pf)).sum()]],
                  columns=['提案1X', '提案1Y'], index=['観測X', '観測Y']) / n)
print('正解率 = ', (gf==pf).sum() / n)
>>>
         提案1X      提案1Y
観測X  0.518089  0.088829
観測Y  0.120229  0.272853
正解率 =  0.790942
```

79.1%の正解率となり、精度がよくなりました。
ただし、別途試験を受けるためコストがかかります。

## 提案方式2
別途試験しない方法も考えてみます。

- 試験の成績が所属グループの平均以上の場合: 観測値を使ってもらう。
- 試験の成績が所属グループの平均未満の場合: 補正値を使ってもらう。

```py3:python3
qx = ox*(ox >= xave) + ax*(ox < xave) # グループXの提案値2
qy = oy*(oy >= yave) + ay*(oy < yave) # グループYの提案値2

qf = qx > qy # 提案値2でXが高い

print(pd.DataFrame([[(gf&qf).sum(), ((~gf)&qf).sum()],
                    [(gf&(~qf)).sum(), ((~gf)&(~qf)).sum()]],
                  columns=['提案2X', '提案2Y'], index=['観測X', '観測Y']) / n)
print('正解率 = ', (gf==qf).sum() / n)
>>>
         提案2X      提案2Y
観測X  0.521504  0.119074
観測Y  0.116814  0.242608
正解率 =  0.764112
```

観測値だけの推定より、多少よくなりました。

## 提案方式3
提案方式2でも、所属グループの上位能力者は不満かもしれません。次の方法は、どうでしょうか。

- ユーザごとに自ら登録するかどうかを試験時に決めてもらう。
- 能力値が所属グループの平均以上の場合: 登録しない。→ 観測値を使ってもらう。
- 能力値が所属グループの平均未満の場合: 登録する。→ 補正値を使ってもらう。

```py3:python3
rx = ox*(gx >= xave) + ax*(gx < xave) # グループXの提案値3
ry = oy*(gy >= yave) + ay*(gy < yave) # グループYの提案値3

rf = rx > ry # 提案値3でXが高い

print(pd.DataFrame([[(gf&rf).sum(), ((~gf)&rf).sum()],
                    [(gf&(~rf)).sum(), ((~gf)&(~rf)).sum()]],
                  columns=['提案3X', '提案3Y'], index=['観測X', '観測Y']) / n)
print('正解率 = ', (gf==rf).sum() / n)
>>>
         提案3X      提案3Y
観測X  0.518967  0.119357
観測Y  0.119351  0.242325
正解率 =  0.761292
```

- 観測値だけの推定より、多少よくなる。
- 受験者が主体的に関われるので、差別されているわけではない。
- コストもほとんどかからない。

提案3(あるいは、提案1と提案3のハイブリッド)が、よいように思われます。

以上