- 前々回の授業では、アンケートのデータを読み込み、Pandasを用いて初歩的な統計分析を行いました。
- また、その中で項目困難度 、項目間相関という考え方を学びました。
- ここでは、Pandasを用いた可視化の方法と少し高度なPandasの使用方法を学び、Good-Poor分析というテストやアンケートの項目を分析する方法を学びます。
5.1 Pandasを用いた可視化¶
5.1.1 準備¶
In [1]:
# 今回使用するパッケージ
import pandas as pd
# 以下は可視化(グラフの描画)に使用するmatplotlibというパッケージをimportしてます。
import matplotlib.pyplot as plt
# 以下はグラフをjupyter notebook上に表示するためのマジックコマンドと呼ばれるコマンドです。
%matplotlib inline
In [3]:
# データの読み込み
# 約200人の受験者が英語のReading(R)、Listening(L)、Writing(W)のテストを受験した結果です。
data = pd.read_csv("../DATA01/IEDA05_01.csv",index_col=0)
data.head()
Out[3]:
R | L | W | |
---|---|---|---|
S001 | 156 | 151 | 100 |
S002 | 166 | 145 | 89 |
S003 | 175 | 176 | 108 |
S004 | 235 | 193 | 110 |
S005 | 136 | 172 | 106 |
5.1.2 ヒストグラム¶
- 度数分布を表すグラフ。横軸に階級、縦軸に度数を示す。
- 階級とは、ここではテストの点数のような量的なデータを扱ってるため、「50点から59点まで」のような点数の範囲のことを示す。
- 度数とはその階級に含まれるデータの数、ここではその階級が示す点数を得た受験者の人数。
- 度数分布とは設定した階級に含まれる受験者がどのようにばらついているかを示すもの。
In [4]:
# 引数でグラフの種類(kind="hist")とグラフにしたい列名を指定する(y=["R"])
data.plot(y=["R","L"],kind="hist",alpha=0.5,bins=25)
Out[4]:
<AxesSubplot: ylabel='Frequency'>
In [5]:
# 列名を指定しないとすべての列が描画される。
data.plot(kind="hist")
Out[5]:
<AxesSubplot: ylabel='Frequency'>
In [6]:
# 上のグラフは少し見にくいので
# binsで階級数、alphaでグラフの透明度を設定する。
data.plot(y=["R","L"],kind="hist",bins=22,alpha=0.5)
Out[6]:
<AxesSubplot: ylabel='Frequency'>
- このグラフで分かることは、
- Reading(青)とListening(オレンジ)のテストは250点満点で、Writing(緑)は150点満点。
- Reading(青)とListening(オレンジ)の点数は広くばらついているが、Writing(緑)は100-130点の間に半数以上の学生がいる。
- 受験者を点数によって分けるというのがテストのひとつである場合、Writing(緑)のテストはあまり良いテストとは言えない。
- このように複数のテストの結果をヒストグラムで表すことによって、個々のテストのばらつきの傾向を見ることができる。
5.1.3 散布図¶
- 縦軸と横軸に2つの量を対応させてデータをプロットしたもの。
- 以下の例では受験者個人が得たListeningとReadingをプロットしており、ひとつのドットは1人の受験者である。
In [7]:
# xで横軸の値、yで縦軸の値を指定する。
data.plot(x="L",y="R",kind="scatter")
Out[7]:
<AxesSubplot: xlabel='L', ylabel='R'>
- ばらつきはあるが、全体的な傾向としてListeningの点数が高い人はReadingの点数も高いという傾向が見られる。
- このような傾向を見ることが散布図のひとつの目的である。
In [8]:
# cで3番目の値を指定するとドットの色の濃さが3番目で指定した
data.plot(x="L",y="R",kind="scatter",c="W")
Out[8]:
<AxesSubplot: xlabel='L', ylabel='R'>
5.1.4 散布図行列¶
ヒストグラムと散布図を同時に描画する方法
In [9]:
from pandas.plotting import scatter_matrix
scatter_matrix(data,alpha=0.5)
Out[9]:
array([[<AxesSubplot: xlabel='R', ylabel='R'>, <AxesSubplot: xlabel='L', ylabel='R'>, <AxesSubplot: xlabel='W', ylabel='R'>], [<AxesSubplot: xlabel='R', ylabel='L'>, <AxesSubplot: xlabel='L', ylabel='L'>, <AxesSubplot: xlabel='W', ylabel='L'>], [<AxesSubplot: xlabel='R', ylabel='W'>, <AxesSubplot: xlabel='L', ylabel='W'>, <AxesSubplot: xlabel='W', ylabel='W'>]], dtype=object)
5.2 Groupbyを用いたグルーピング¶
5.2.1準備¶
In [10]:
# 使用するデータ
data2 = pd.read_csv("../DATA01/IEDA05_02.csv",index_col=0)
data2.head()
Out[10]:
item1 | item2 | item3 | item4 | item5 | item6 | item7 | item8 | Class | |
---|---|---|---|---|---|---|---|---|---|
s01 | 5 | 4 | 4 | 5 | 4 | 4 | 6 | 5 | A |
s02 | 4 | 3 | 1 | 3 | 3 | 3 | 2 | 4 | A |
s03 | 4 | 4 | 3 | 3 | 3 | 3 | 3 | 3 | A |
s04 | 5 | 3 | 3 | 3 | 3 | 4 | 3 | 3 | A |
s05 | 3 | 2 | 3 | 3 | 3 | 3 | 3 | 3 | A |
5.2.2 Groupby¶
- 条件に基づいてデータを集約するメソッド
In [11]:
# 以下では列名"Class"でデータを分割してクラスごとの平均値を算出している。
# group()の引数に列名を与えることでデータを分割する
data2.groupby("Class").mean()
Out[11]:
item1 | item2 | item3 | item4 | item5 | item6 | item7 | item8 | |
---|---|---|---|---|---|---|---|---|
Class | ||||||||
A | 3.7 | 3.1 | 2.8 | 2.9 | 2.7 | 3.0 | 2.9 | 3.0 |
B | 4.6 | 4.2 | 4.2 | 4.2 | 4.1 | 4.1 | 4.3 | 4.1 |
In [12]:
# boxplot()の引数columnに列名を与え、Class AとClass Bの分布を箱ひげ図で確認することができる。
data2.groupby("Class").boxplot(column="item2")
Out[12]:
A AxesSubplot(0.1,0.15;0.363636x0.75) B AxesSubplot(0.536364,0.15;0.363636x0.75) dtype: object
In [13]:
# 特定の列を指定することも可能(出力に警告が含まれますが気にしないでください)
data2.groupby("Class")["item2","item3"].mean()
/var/folders/rt/wv_48tq925z8xx5ffnwfq01r0000gn/T/ipykernel_44782/732549167.py:2: FutureWarning: Indexing with multiple keys (implicitly converted to a tuple of keys) will be deprecated, use a list instead. data2.groupby("Class")["item2","item3"].mean()
Out[13]:
item2 | item3 | |
---|---|---|
Class | ||
A | 3.1 | 2.8 |
B | 4.2 | 4.2 |
5.3 Good-Poor分析¶
- ここまで学んできたことを使って、Good-Poor分析をしてみましょう。
- Good-Poor分析とは
- 合計点を基準として、受験者を上位群、中位群、下位などに分けて、
- 群ごとに項目への回答の平均値を求めます。
- 理想的な項目は上位群の平均値が下位群の平均値よりも高い。
- 合計点が高い受験者はその項目でも高い値を付けているという前提があります。
- テストで考えると、合計得点が高い受験者たちの多くは、いかなる項目を選んで見てみても正解しているという前提です。
In [15]:
data2.head()
Out[15]:
item1 | item2 | item3 | item4 | item5 | item6 | item7 | item8 | Class | |
---|---|---|---|---|---|---|---|---|---|
s01 | 5 | 4 | 4 | 5 | 4 | 4 | 6 | 5 | A |
s02 | 4 | 3 | 1 | 3 | 3 | 3 | 2 | 4 | A |
s03 | 4 | 4 | 3 | 3 | 3 | 3 | 3 | 3 | A |
s04 | 5 | 3 | 3 | 3 | 3 | 4 | 3 | 3 | A |
s05 | 3 | 2 | 3 | 3 | 3 | 3 | 3 | 3 | A |
In [17]:
# 合計得点のSeriesを作ります。
s_total = data2.sum(numeric_only=True,axis=1)
s_total
Out[17]:
s01 37 s02 23 s03 26 s04 27 s05 23 s06 14 s07 30 s08 19 s09 13 s10 29 s11 20 s12 48 s13 30 s14 39 s15 48 s16 35 s17 28 s18 18 s19 24 s20 48 dtype: int64
In [18]:
# 合計得点が上位25%だったら3、下位25%だったら1、
# その間だったら2というリストGを作ります。
G = []
upper = s_total.quantile(.75)
lower = s_total.quantile(.25)
for i in s_total:
if i > upper:
G.append(3)
elif i < lower:
G.append(1)
else:
G.append(2)
G
Out[18]:
[3, 2, 2, 2, 2, 1, 2, 1, 1, 2, 1, 3, 2, 3, 3, 2, 2, 1, 2, 3]
In [19]:
# data2にGを入れる
data2["G_P"] = G
- "G_P"は合計点で回答者を上位(3)、中位(2)、下位(1)に分けたもので、
- 以下のように"G_Pで回答者を分けて、それぞれの項目(item)で平均値を算出すると
- 上位(1)、中位(2)、下位(3)のそれぞれ群の回答者がここの項目で平均的にどのような回答をしているかが分かります。
In [21]:
data2.groupby("G_P").mean(numeric_only=True)
Out[21]:
item1 | item2 | item3 | item4 | item5 | item6 | item7 | item8 | |
---|---|---|---|---|---|---|---|---|
G_P | ||||||||
1 | 2.8 | 2.2 | 1.8 | 1.8 | 1.4 | 1.8 | 2.4 | 2.6 |
2 | 4.1 | 3.5 | 3.4 | 3.4 | 3.4 | 3.5 | 3.1 | 3.1 |
3 | 5.6 | 5.4 | 5.4 | 5.6 | 5.4 | 5.4 | 5.8 | 5.4 |
In [22]:
# グループ分けした平均値を折れ線グラフで示すと以下のようになります。
# 上の表と下のグラフを見比べてみてください。
data2.groupby("G_P").mean(numeric_only=True).plot(kind="line")
Out[22]:
<AxesSubplot: xlabel='G_P'>
In [27]:
# リストで列名を指定すると指定した列のみのグラフが出力されます。
data2.groupby("G_P")["item1","item2"].mean(numeric_only=True).plot(kind="line")
/var/folders/rt/wv_48tq925z8xx5ffnwfq01r0000gn/T/ipykernel_44782/3173011769.py:2: FutureWarning: Indexing with multiple keys (implicitly converted to a tuple of keys) will be deprecated, use a list instead. data2.groupby("G_P")["item1","item2"].mean(numeric_only=True).plot(kind="line")
Out[27]:
<AxesSubplot: xlabel='G_P'>
- このグラフを項目特性曲線と呼びます。
- このグラフから得られる情報を項目の識別力と考えることができます。
- 「1と2の平均値に比べて2と3の平均値の差の方が大きい」
- これは、この項目が
- 「能力が低い受験者と能力が中位の受験者を識別するより、中位と上位の受験者を識別するのに役立っていることを示している」
- と解釈できます。
練習問題¶
../DATA01/IEDA05_03.csvには92人が60問の問題に回答し、正解は1、不正解は0が入力されています。受験者を上位群、中位群、下位群に分け、項目ごとに群ごとの正解率を算出し、Q1、Q5、Q15の項目特性曲線を描画しなさい。
In [28]:
# パッケージのimport
import pandas as pd
import matplotlib.pyplot as plt
%matplotlib inline
In [29]:
# csvの読み込み
data = pd.read_csv("../DATA01/IEDA05_03.csv",index_col=0)
data.head()
Out[29]:
Q1 | Q2 | Q3 | Q4 | Q5 | Q6 | Q7 | Q8 | Q9 | Q10 | ... | Q51 | Q52 | Q53 | Q54 | Q55 | Q56 | Q57 | Q58 | Q59 | Q60 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
S001 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | ... | 0 | 1 | 0 | 0 | 1 | 1 | 1 | 0 | 1 | 1 |
S002 | 1 | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | ... | 0 | 1 | 1 | 0 | 1 | 0 | 0 | 0 | 0 | 0 |
S003 | 0 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | 1 | 1 | ... | 1 | 1 | 0 | 0 | 1 | 1 | 1 | 0 | 0 | 0 |
S004 | 1 | 1 | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 1 | ... | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 0 | 1 | 1 |
S005 | 1 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | ... | 0 | 1 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 |
5 rows × 60 columns