これまでPythonの基礎を学びながら、テストの項目分析に関して、項目困難度、項目間相関、G-P分析による識別力を学びました。
項目分析の目的は、実施したテストの項目を分析して、学習者の傾向を知り、次回以降のテスト作成に有用な情報を得ることです。
ここではこれらに加えて、識別力、信頼性係数について学びます。
In [1]:
# pandasのimport
import pandas as pd
# 今回使うデータ
# 92人の受験者が10問の問題に回答した結果
# 1が正解、0が不正解を示す
data = pd.read_csv("../DATA01/IEDA06_01.csv",index_col=0)
data.head()
Out[1]:
Q01 | Q02 | Q03 | Q04 | Q05 | Q06 | Q07 | Q08 | Q09 | Q10 | |
---|---|---|---|---|---|---|---|---|---|---|
S001 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
S002 | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
S003 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | 1 | 1 | 1 |
S004 | 1 | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 1 | 1 |
S005 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
6.1 項目困難度¶
- 項目得点の平均値
- 各受験者の項目に対する反応(1か0)の総和$\div$受験者の人数
- 値が小さいほどその項目は難しい項目
- 困難度の解釈はその項目のスケール(正解、不正解なのか1から5の5段階なのか)に依存する。
- 正答が1点、誤答が0点の場合、正答した受験者の割合
In [2]:
# 項目困難度の算出(最も難しい項目はQ03)
item_diff = data.mean()
item_diff
Out[2]:
Q01 0.771739 Q02 0.923913 Q03 0.706522 Q04 0.891304 Q05 0.967391 Q06 0.891304 Q07 0.869565 Q08 0.815217 Q09 0.923913 Q10 0.913043 dtype: float64
6.2 識別力¶
- 知りたいことは、ある項目に正解している人がテストの合計点においても高い点数を得ているかどうか。
- あるいは、ある項目に不正解している人がテストの合計点においても低い点数を得ているかどうか。
- つまり、識別力が低い項目とは、その項目に正解であっても不正解であってもテストの合計点とは関係がない。
- テストの合計点が高い人が正解している項目は識別力が高い。
- 項目の正解/不正解とテストの合計点という2つの変数の関係を表す統計量が欲しい。
点双列相関係数¶
- 識別力のひとつの指標となり得るもの。
- 変数の一方が2値(0か1など)で他方が間隔尺度の場合に使用する相関係数
- 相関係数と同様の解釈ができる。
- 1に近ければ、その項目に正答した人はテスト得点の点数が高い。
以下の式で定義される $$ r_{pbi} = \frac{\sqrt{pq}(\overline{X_p} - \overline{X_q})}{SD} $$
p: その項目に正答した人の割合
q: その項目に誤答した人の割合
$ \overline{X_p} $: その項目に正答した人のテストの平均点
$ \overline{X_q} $: その項目に誤答した人のテストの平均点
SD: テストの標準偏差
In [3]:
import numpy as np
# それではQ01を例に点双列相関係数を求めてみましょう。
# Q01に正答/誤答した人の割合は
# 以下のように条件指定をしてその行数をデータ全体の行数で割る
p = len(data[data["Q01"]==1])/len(data)
q = len(data[data["Q01"]==0])/len(data)
# Q01に正答/誤答した人のテストの平均点は
# 条件指定して合計を算出し、その平均を算出する
X_p = data[data["Q01"]==1].sum(axis=1).mean()
X_q = data[data["Q01"]==0].sum(axis=1).mean()
# SDはテスト得点の標準偏差
SD = data.sum(axis=1).std()
個々の値を求めたので、以下の式に代入してみましょう。
$ r_{pbi} = \frac{\sqrt{pq}(\overline{X_p} - \overline{X_q})}{SD} $
In [4]:
r_pbi = (np.sqrt(p*q) * (X_p- X_q) / SD)
r_pbi
Out[4]:
0.5797567974416593
- 点双列相関係数も絶対的な解釈はない。
- 複数の項目を比較する際に使用される。
In [5]:
import numpy as np
# forを使って全項目の点双列相関係数を求める
# 算出した値を保存するリスト
R_pbi = []
# コラム名を取得して全項目に関して同様の処理をする
# 前のコードと違う部分は"Q01"がiになっているだけ
for i in data.columns:
X_p = data[data[i]==1].sum(axis=1).mean()
X_q = data[data[i]==0].sum(axis=1).mean()
SD = data.sum(axis=1).std()
p = len(data[data[i]==1])/len(data)
q = len(data[data[i]==0])/len(data)
r_pbi = ((X_p - X_q) / SD) * np.sqrt(p*q)
R_pbi.append(r_pbi)
R_pbi
Out[5]:
[0.5797567974416593, 0.19789666579271892, 0.39789120504323705, 0.42007059274596065, 0.4407413274125061, 0.4953559717056259, 0.3267216171199718, 0.4921935415353277, 0.5219604615568473, 0.34350154895027657]
6.3 測定具の信頼性¶
- 身長は成長期であったとしても1日でその値が大きく変わるとは考えられません。
- 今あなたが身長を測って、明日の同じ時間に身長を測ってもその値はほとんど変わりません。
- あるいは、2つの身長計を使ってあなたの身長を測った場合、2つの身長計から得られる値はほとんど同じはずです。
- これを測定具の「信頼性」と言います。
- つまり、「複数回測定しても値が変わらない」ということです。
- 身長計はあなたの身長を測定する測定具であると同じように、英語のテストもあなたの英語力を測る測定具と考えることができます。
- しかしながら、英語力は身長と異なり複数回測定してその信頼性を確認することはできません。
- まったく同じ英語のテストを今日と明日受験してもらってもその間に多少勉強するかもしれません。
- また、続けて2回同じテストをすることは、受験者にとって何の利益もありません。
6.3.1 折半法¶
- 上記のような問題を解決する方法のひとつとして折半法があります。
- 折半法とは、テストの項目を半分に分け(これを部分テストA、部分テストBと呼びます)、これらの部分テストAとBの総点の相関係数を求めます。この相関係数は以下のSpearman-Brownの公式を用いて補正することにより(深入りしません)、テストの信頼性係数と考えます。
$$ \rho_x = \frac{2r}{1+r} $$
$r$: 2つの部分テストの相関係数
- テストを半分に分ける方法は項目数により何通りにもなります。
- 折半法を全通り行ったときの平均値はクロンバックのαとほぼ同じであることが分かっています。
- 折半法によって得られる信頼性係数$\rho$がなぜこのような式なのはイマイチ分かりませんが、この式の相関係数$r$と信頼性係数$\rho$は以下のような関係で、ちょっとだけ信頼性係数の値の方が大きくなります。
In [6]:
import matplotlib.pyplot as plt
%matplotlib inline
import japanize_matplotlib
C = [0,0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,1]
S = []
for i in C:
j = 2 * i / (1+i)
S.append(j)
plt.plot(C,S)
plt.xlabel("相関係数")
plt.ylabel("信頼性係数")
Out[6]:
Text(0, 0.5, '信頼性係数')
6.3.2 クロンバックのα¶
- クロンバックのαは以下の式で定義されます。
$$ \alpha = \frac{m}{m-1}(1 - \frac{\sum_{1 = 1}^{m}\sigma_{i}^{2}}{\sigma_{x}^{2}} )$$
- $m$は項目数、$\sigma_{i}^{2}$は各項目の分散、$\sigma_{x}^{2}$はテストの総点の分散である。
- テストにおける解答の安定性を数値化した指標
- 「テスト得点の分散から誤差の分散を除いた部分の割合」
- 以下で上の式の項を順番に求めていきましょう。
In [7]:
# 項目数
n_item = len(data.columns)
n_item
Out[7]:
10
In [8]:
# 各項目の分散の合計
item_var = sum(data.var())
item_var
Out[8]:
1.104873387482083
In [9]:
# 合計得点の分散
total_var = data.sum(axis=1).var()
total_var
Out[9]:
1.936454849498328
- 求めた値を計算式に代入
In [10]:
# 信頼性係数
alpha = n_item / (n_item - 1) * (1 - (item_var / total_var))
alpha
Out[10]:
0.4771499849220057
- テストの性質にもよるが、クロンバックのアルファは0.8以上が望ましい。
6.3.3 ある項目を抜いた場合のクロンバックのα¶
- ひとつの項目を削除してテスト全体の信頼性係数を計算した時、その値が上がれば抜いた項目が信頼性係数を下げている原因と考えることができる。
In [11]:
# alphaを保存しておくリスト
A = []
# 項目数は一定なのであらかじめ代入しておく
n_item = 9
# drop()を用いてひとつずつ順番に項目を削除し、
# 残った項目で信頼性係数を計算する。
for i in data.columns:
data_x = data.drop(i,axis=1)
item_var = sum(data.var())
total_var = data_x.sum(axis=1).var()
a = n_item / (n_item -1) * (1 - (item_var / total_var))
A.append(a)
A
Out[11]:
[0.2557117199899759, 0.4566903217519748, 0.36536426016497575, 0.37913919151376163, 0.41394772804919716, 0.3484141791044777, 0.4112654320987653, 0.32371794871794884, 0.3568409980069388, 0.41243837304848]
6.4 項目に関する情報をまとめる¶
これまで算出した困難度、識別力、項目を削除した場合の$\alpha$のDataFrameを作ってみましょう。
In [12]:
item_property = pd.DataFrame({"difficulty":item_diff,
"discrimination":R_pbi,
"alpha_if_dropped":A},
index = data.columns)
item_property
Out[12]:
difficulty | discrimination | alpha_if_dropped | |
---|---|---|---|
Q01 | 0.771739 | 0.579757 | 0.255712 |
Q02 | 0.923913 | 0.197897 | 0.456690 |
Q03 | 0.706522 | 0.397891 | 0.365364 |
Q04 | 0.891304 | 0.420071 | 0.379139 |
Q05 | 0.967391 | 0.440741 | 0.413948 |
Q06 | 0.891304 | 0.495356 | 0.348414 |
Q07 | 0.869565 | 0.326722 | 0.411265 |
Q08 | 0.815217 | 0.492194 | 0.323718 |
Q09 | 0.923913 | 0.521960 | 0.356841 |
Q10 | 0.913043 | 0.343502 | 0.412438 |
6.5 項目に関する情報の可視化¶
In [13]:
# 項目困難度と識別力の散布図
item_property.plot(kind="scatter",x="difficulty",y="discrimination")
Out[13]:
<AxesSubplot: xlabel='difficulty', ylabel='discrimination'>
In [14]:
# ある項目を抜いた場合のクロンバックのαと識別力の散布図
item_property.plot(kind="scatter",x="alpha_if_dropped",y="discrimination")
Out[14]:
<AxesSubplot: xlabel='alpha_if_dropped', ylabel='discrimination'>
In [15]:
# 項目困難度とある項目を抜いた場合のクロンバックのα
item_property.plot(kind="scatter",x="difficulty",y="alpha_if_dropped")
Out[15]:
<AxesSubplot: xlabel='difficulty', ylabel='alpha_if_dropped'>
演習問題¶
../DATA01/IEDA06_02.csvを読み込んで、項目困難度、識別力、その項目を削除した場合の信頼性係数を算出し、DataFrameで表示しなさい。