ITパスポートの正解選択肢はランダムに決められているのか、Pythonでカイ2乗検定して確かめる part1

## この記事の内容

ITパスポートの試験問題はア〜エの4択問題ですが、それって偏りがあったら良くないですよね。  
じゃあ本当に偏ってないかどうか、統計的に確かめましょうという話。  
今回は各選択肢の出現回数の観点から確かめてみます。

## データの確認

ITパスポートの過去問の正答を、[こちらの記事](https://qiita.com/canid_56/items/e3676a53340154cba817)の手順で構造化します。  
出来上がったデータはこんな感じです。

```python
# モジュールのインポート
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from scipy.stats import chisquare
plt.rcParams['font.sans-serif'] = ['Hiragino Maru Gothic Pro', 'Yu Gothic']

%matplotlib inline

# 生データ
df.head()
```

<table border="1" class="dataframe">
  <thead>
    <tr style="text-align: right;">
      <th></th>
      <th>令和4年度分</th>
      <th>令和3年度分</th>
      <th>令和2年度 10月分</th>
      <th>令和元年度 秋期分</th>
      <th>平成31年度 春期分</th>
      <th>平成30年度 秋期分</th>
      <th>平成30年度 春期分</th>
      <th>平成29年度 秋期分</th>
      <th>平成29年度 春期分</th>
      <th>平成28年度 秋期分</th>
      <th>平成28年度 春期分</th>
      <th>平成27年度 秋期分</th>
      <th>平成27年度 春期分</th>
      <th>平成26年度 秋期分</th>
      <th>平成26年度 春期分</th>
      <th>平成25年度 秋期分</th>
      <th>平成25年度 春期分</th>
      <th>平成24年度 秋期分</th>
      <th>平成24年度 春期分</th>
      <th>平成23年度 秋期分</th>
      <th>平成23年度 特別分</th>
      <th>平成22年度 秋期分</th>
      <th>平成22年度 春期分</th>
      <th>平成21年度 春期分</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>0</th>
      <td>エ</td>
      <td>ウ</td>
      <td>ウ</td>
      <td>エ</td>
      <td>エ</td>
      <td>ア</td>
      <td>エ</td>
      <td>ウ</td>
      <td>ア</td>
      <td>エ</td>
      <td>ア</td>
      <td>ア</td>
      <td>ウ</td>
      <td>ウ</td>
      <td>ア</td>
      <td>エ</td>
      <td>ア</td>
      <td>エ</td>
      <td>イ</td>
      <td>ア</td>
      <td>ア</td>
      <td>エ</td>
      <td>ア</td>
      <td>ウ</td>
    </tr>
    <tr>
      <th>1</th>
      <td>イ</td>
      <td>エ</td>
      <td>イ</td>
      <td>ア</td>
      <td>ウ</td>
      <td>ウ</td>
      <td>ウ</td>
      <td>エ</td>
      <td>イ</td>
      <td>ア</td>
      <td>ア</td>
      <td>ア</td>
      <td>ア</td>
      <td>ウ</td>
      <td>エ</td>
      <td>ア</td>
      <td>ウ</td>
      <td>イ</td>
      <td>ア</td>
      <td>イ</td>
      <td>イ</td>
      <td>ア</td>
      <td>イ</td>
      <td>ウ</td>
    </tr>
    <tr>
      <th>2</th>
      <td>ウ</td>
      <td>ウ</td>
      <td>イ</td>
      <td>ウ</td>
      <td>ア</td>
      <td>ウ</td>
      <td>イ</td>
      <td>ア</td>
      <td>ア</td>
      <td>ウ</td>
      <td>エ</td>
      <td>エ</td>
      <td>エ</td>
      <td>ア</td>
      <td>エ</td>
      <td>エ</td>
      <td>イ</td>
      <td>ア</td>
      <td>イ</td>
      <td>エ</td>
      <td>イ</td>
      <td>イ</td>
      <td>エ</td>
      <td>イ</td>
    </tr>
    <tr>
      <th>3</th>
      <td>ア</td>
      <td>ア</td>
      <td>ウ</td>
      <td>エ</td>
      <td>イ</td>
      <td>ウ</td>
      <td>ア</td>
      <td>イ</td>
      <td>イ</td>
      <td>エ</td>
      <td>ウ</td>
      <td>ア</td>
      <td>ウ</td>
      <td>イ</td>
      <td>イ</td>
      <td>ウ</td>
      <td>ア</td>
      <td>イ</td>
      <td>ア</td>
      <td>エ</td>
      <td>イ</td>
      <td>ア</td>
      <td>ウ</td>
      <td>ア</td>
    </tr>
    <tr>
      <th>4</th>
      <td>イ</td>
      <td>ウ</td>
      <td>イ</td>
      <td>エ</td>
      <td>ア</td>
      <td>ウ</td>
      <td>イ</td>
      <td>ア</td>
      <td>イ</td>
      <td>イ</td>
      <td>ウ</td>
      <td>ア</td>
      <td>ア</td>
      <td>ウ</td>
      <td>エ</td>
      <td>ウ</td>
      <td>エ</td>
      <td>イ</td>
      <td>ウ</td>
      <td>イ</td>
      <td>ウ</td>
      <td>ウ</td>
      <td>イ</td>
      <td>イ</td>
    </tr>
  </tbody>
</table>

## 仮説 : ア〜エの選択肢の出現回数はランダムだと言えるのか？

特定の選択肢が正解になりやすいという事があれば試験として問題ですね。  
逆に全ての選択肢が同じ数だけ出現するというのも、メタ読みで正解が分かってしまうので良くないです。  
なので理想は、サイコロで決めるように全ての選択肢が正解になる確率が等しい事です。  
つまり、**各選択肢の出現回数は一様分布に従う**べきです。
データの出現回数が特定の分布に従うことを検証するためには、カイ2乗検定を行います。  

それでは、ITパスポートの過去の試験が一様分布に従うかどうか、カイ2乗検定で確かめてみます。  
有意水準は5%で。

## データの変換

まずは集計しやすいようにデータを縦持ちに変換します。

```python
df_long = df.unstack()
df_long.name = "value"
df_long = df_long.reset_index()
df_long.head()
```

<table border="1" class="dataframe">
  <thead>
    <tr style="text-align: right;">
      <th></th>
      <th>level_0</th>
      <th>level_1</th>
      <th>value</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>0</th>
      <td>令和4年度分</td>
      <td>0</td>
      <td>エ</td>
    </tr>
    <tr>
      <th>1</th>
      <td>令和4年度分</td>
      <td>1</td>
      <td>イ</td>
    </tr>
    <tr>
      <th>2</th>
      <td>令和4年度分</td>
      <td>2</td>
      <td>ウ</td>
    </tr>
    <tr>
      <th>3</th>
      <td>令和4年度分</td>
      <td>3</td>
      <td>ア</td>
    </tr>
    <tr>
      <th>4</th>
      <td>令和4年度分</td>
      <td>4</td>
      <td>イ</td>
    </tr>
  </tbody>
</table>

続いて各試験ごとに4つの選択肢それぞれが正解となった回数を集計します。

```python
count = df_long.groupby("level_0")["value"].value_counts().unstack().loc[df.columns]
count.head()
```

<table border="1" class="dataframe">
  <thead>
    <tr style="text-align: right;">
      <th>value</th>
      <th>ア</th>
      <th>イ</th>
      <th>ウ</th>
      <th>エ</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <th>令和4年度分</th>
      <td>32</td>
      <td>27</td>
      <td>16</td>
      <td>25</td>
    </tr>
    <tr>
      <th>令和3年度分</th>
      <td>29</td>
      <td>20</td>
      <td>25</td>
      <td>26</td>
    </tr>
    <tr>
      <th>令和2年度 10月分</th>
      <td>27</td>
      <td>25</td>
      <td>23</td>
      <td>25</td>
    </tr>
    <tr>
      <th>令和元年度 秋期分</th>
      <td>21</td>
      <td>20</td>
      <td>23</td>
      <td>36</td>
    </tr>
    <tr>
      <th>平成31年度 春期分</th>
      <td>26</td>
      <td>22</td>
      <td>26</td>
      <td>26</td>
    </tr>
  </tbody>
</table>

例えば令和4年度分の試験の選択肢はこんな分布です。

```python
count.loc["令和4年度分"].plot.bar()
plt.show()
```

![r4_count.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/315348/63501919-5ec4-b1ca-8061-457fb2db8611.png)

## 比較対象データの作成

次に、比較対象となる期待される出現回数を用意します。  
選択肢が4つ、問題が100問なので全ての選択肢が25回出現することが期待されます。

```python
f_exp_count = np.ones(4, dtype=int) * 25
plt.bar(count.columns, f_exp_count)
plt.xlabel("count")
plt.show()
```

![exp_count.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/315348/6ef3fb74-c809-cdc2-ce5e-12478ab95876.png)

## カイ2乗検定の実行
あとは各行に一様分布とのカイ2乗検定を行い、p値を取り出せば分析完了です。

```python
count.apply(lambda x:chisquare(x, f_exp_count).pvalue, axis=1)
```

```
令和4年度分        0.147256
令和3年度分        0.641389
令和2年度 10月分    0.956224
令和元年度 秋期分     0.084302
平成31年度 春期分    0.923263
平成30年度 秋期分    0.539870
平成30年度 春期分    0.539870
平成29年度 秋期分    0.221385
平成29年度 春期分    0.004551
平成28年度 秋期分    0.714937
平成28年度 春期分    0.397834
平成27年度 秋期分    0.288573
平成27年度 春期分    0.361805
平成26年度 秋期分    0.180798
平成26年度 春期分    0.849467
平成25年度 秋期分    0.696186
平成25年度 春期分    0.868490
平成24年度 秋期分    0.270233
平成24年度 春期分    0.641389
平成23年度 秋期分    0.009222
平成23年度 特別分    0.606269
平成22年度 秋期分    0.200137
平成22年度 春期分    0.288573
平成21年度 春期分    0.119659
dtype: float64
```

## 結論

カイ2乗検定の帰無仮説は、2つの分布が同じであることなので、p値が有意水準を上回っていれば選択肢がランダムに決められていると考えても差し支えないということになります。  
結果を見ると、H23年秋とH29年春はp値が有意水準5%を下回っているので怪しいですね。

## 予告

次の記事では、同じ選択肢が連続で正解になることの調整がされているか、確認していきます。