教育データ分析入門1 (第5回)

  • 5.1 Pandasを用いた可視化
    • 5.1.1 準備
    • 5.1.2 ヒストグラム
    • 5.1.3 散布図
    • 5.1.4 散布図行列
  • 5.2 Groupbyを用いたグルーピング
    • 5.2.1準備
    • 5.2.2 Groupby
  • 5.3 Good-Poor分析
  • 練習問題
  • 前々回の授業では、アンケートのデータを読み込み、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'>
No description has been provided for this image
In [5]:
# 列名を指定しないとすべての列が描画される。
data.plot(kind="hist")
Out[5]:
<AxesSubplot: ylabel='Frequency'>
No description has been provided for this image
In [6]:
# 上のグラフは少し見にくいので
# binsで階級数、alphaでグラフの透明度を設定する。
data.plot(y=["R","L"],kind="hist",bins=22,alpha=0.5)
Out[6]:
<AxesSubplot: ylabel='Frequency'>
No description has been provided for this image
  • このグラフで分かることは、
    • 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'>
No description has been provided for this image
  • ばらつきはあるが、全体的な傾向としてListeningの点数が高い人はReadingの点数も高いという傾向が見られる。
  • このような傾向を見ることが散布図のひとつの目的である。
In [8]:
# cで3番目の値を指定するとドットの色の濃さが3番目で指定した
data.plot(x="L",y="R",kind="scatter",c="W")
Out[8]:
<AxesSubplot: xlabel='L', ylabel='R'>
No description has been provided for this image

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)
No description has been provided for this image

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
No description has been provided for this image
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'>
No description has been provided for this image
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'>
No description has been provided for this image
  • このグラフを項目特性曲線と呼びます。
  • このグラフから得られる情報を項目の識別力と考えることができます。
  • 「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