ユニファ開発者ブログ

ユニファ株式会社プロダクトデベロップメント本部メンバーによるブログです。

子どもの学力に影響を与えている要因は?:全国学力テスト疑似データを用いた分析

はじめに

こんにちは、データエンジニアリングチームの島田です。

ユニファではテクノロジーを活用したサービスを通じて子育て環境の充実に寄与することを目指しており、社会課題解決に向けて日々挑戦を続けています。また、私自身も子どもを持つ一人の親として、子育てや教育に関してこれまで以上に関心を寄せるようになりました。

特に子育てや教育に関するデータ利活用に興味があるのですが、誰でも自由にアクセスできるデータはまだまだ少ないのが現状です。そういった中で、文部科学省が全国学力・学習状況調査(全国学力テスト)パブリックユースデータ(疑似データ)を公開していることを知り、せっかくなのでこのデータを使って子どもの学力差について分析してみましたので今回はこちらについてお話します。

また、本記事の内容は先述した通り、文部科学省によって特定の児童生徒個人・学校等を示さない疑似的に作られたデータを使った分析であり、本記事の結果が子どもの学力差の要因を直接的に示すようなエビデンスにはならないことを念の為あらかじめお断りしておきます。

本記事で用いるデータ

本記事では、全国学力・学習状況調査(全国学力テスト)のパブリックユースデータ(疑似データ)の生徒データ(Excel: 3126KB)をサンプルデータとして使用します。生徒データには中学校3年生を対象とした学力テストの結果および学校や家での勉強や生活の様子を調査した結果(一部)が含まれています。

今回はPythonを使って分析したいと思いますので、まずは以下のように生徒データを読み込みます。

import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
import pandas as pd
import scipy.stats as stats
import statsmodels.api as sm
import statsmodels.formula.api as smf
from statsmodels.formula.api import ols

df = pd.read_excel("1404609_2_1.xlsx")
print(df.shape)
df.head()

このサンプルデータにはどうやら生徒2,000名のテスト結果が含まれているようです。またカラムを見てみると、実施年は2015年、地域規模(1: 大都市, 2: 中核市, 3: その他の市, 4: 町村)、性別(1: 男子, 2: 女子, 0: 不明)、さらには国語A,国語B,数学A,数学B,理科の5科目における正答数と100点満点に換算した正答率が含まれていることがわかります。その他のカラムについても詳しくはパブリックユースデータのレイアウト(PDF: 784KB)に記載されています。

科目ごとの正答数と正答率の確認

5科目それぞれにおける正答数の分布を見てみます。また、カーネル密度関数も合わせて確認しておきます。カーネル密度推定はいくつかやり方がありますが、ここでは最も簡単なPandasを使った方法で分布を求めてみます。

plt.subplots_adjust(wspace=0.6, hspace=0.6)
row = 3
col  = 2

plt.subplot(row, col, 1)
plt.xlim(0, 33)
plt.title('正答数_国A')
df["正答数_国A"].plot(kind="hist")
plt.subplot(row, col, 1)
df["正答数_国A"].plot(kind="kde", secondary_y=True)

plt.subplot(row, col, 2)
plt.xlim(0, 9)
plt.title('正答数_国B')
df["正答数_国B"].plot(kind="hist")
plt.subplot(row, col, 2)
df["正答数_国B"].plot(kind="kde", secondary_y=True)

plt.subplot(row, col, 3)
plt.xlim(0, 36)
plt.title('正答数_数A')
df["正答数_数A"].plot(kind="hist")
plt.subplot(row, col, 3)
df["正答数_数A"].plot(kind="kde", secondary_y=True)

plt.subplot(row, col, 4)
plt.xlim(0, 15)
plt.title('正答数_数B')
df["正答数_数B"].plot(kind="hist")
plt.subplot(row, col, 4)
df["正答数_数B"].plot(kind="kde", secondary_y=True)

plt.subplot(row, col, 5)
plt.xlim(0, 25)
plt.title('正答数_理')
df["正答数_理"].plot(kind="hist")
plt.subplot(row, col, 5)
df["正答数_理"].plot(kind="kde", secondary_y=True)

plt.show()

国語はAとB両方とも同じような傾向をしていますが、数学についてはAよりもBの方が分布の山が左に寄っていることがわかります。このことから、数学Aよりも数学Bの方が難しい問題だと予想できます。実際に調査概要を見ても、Aは基礎、Bは実践(応用)といった記述がされています。また、理科については分布のちょうど中央あたりで山があり、調査概要でも基礎を中心として実践も一部含まれた一体型の内容であったことがわかります。

次に、100点満点換算した正答率の基本統計量も確認しておきましょう。

df[['正答率_国A', '正答率_国B', '正答率_数A', '正答率_数B', '正答率_理']].describe()

国語Aはどのパーセンタイルにおいても高得点であることがわかります。一方、数学Bはやはり全体的に得点が低いですね。

ここで、5科目全体の平均点も算出しカラムに追加して、基本統計量を確認しておきます。

df['平均正答率_全科目'] = df[['正答率_国A', '正答率_国B', '正答率_数A', '正答率_数B', '正答率_理']].mean(axis='columns')
df['平均正答率_全科目'].describe()

5科目の平均点は60点程度で、50%パーセンタイルでも60点程とほぼ同じくらいなので正規分布の形をしていそうです。

男女間における5科目平均正答率の差

前項では正答数や正答率について傾向を見てきました。ここでは、男女間における5科目の平均点に差があるかを見ていきます。

まずは性別ごとの5科目平均正答率を比較してみましょう。

boy_score = df['平均正答率_全科目'][df['性別'] == 1]
girl_score = df['平均正答率_全科目'][df['性別'] == 2]
print(‘男子:’, boy_score.mean)
print(‘女子:’, girl_score.mean)

男女間でほとんど差は無いように見えます。次に箱ひげ図と正答率の分布も確認しておきましょう。

fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
sns.boxplot(x='性別', y='平均正答率_全科目', data=df, ax=ax)
sns.stripplot(x='性別', y='平均正答率_全科目', data=df, dodge=True, jitter=True, color='black', ax=ax)

男子の方がわずかに全体的に高いようにも見えますが、ほとんど差はない印象を受けます。

念の為、性別ごとの5科目平均正答率についてt検定をしてみます。

res = st.ttest_ind(girl_score, boy_score, equal_var=False)
print(res)

やはり有意な差はありませんでした。

朝食摂取頻度ごとの5科目平均正答率の確認

次に生徒の日々の生活習慣がテストの点数にどのような影響を及ぼしているかを確認してみます。全国学力テストでは生活習慣に関する質問も設けられており、どのような質問かは生徒質問用紙(PDF/1.03MB)で確認が出来ます。

本項では、最初の質問「朝食を毎日食べている(1: している, 2: どちらかといえば,している, 3: あまりしていない, 4: 全くしていない)」について、回答ごとの5科目平均正答率の違いを確認してみます。

fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
sns.boxplot(x='生徒質問紙回答_001', y='平均正答率_全科目', data=df)
sns.stripplot(x='生徒質問紙回答_001', y='平均正答率_全科目', data=df, dodge=True, jitter=True, color='black', ax=ax)

朝食頻度については「4: 全くしていない」と回答した生徒は一人もいませんでした。それ以外の回答ごとの5科目平均正答率の箱ひげ図と分布は上図に示したとおりです。朝食頻度ごとで5科目平均正答率に差があるようにも見えます。

朝食摂取頻度ごとで5科目平均正答率がどの程度異なっているか

前項の結果を見ると、朝食がしっかりと摂取できているほど5科目の正答率が高い傾向にあるように見えました。本項では朝食摂取頻度ごとで5科目平均正答率がどの程度異なっているかを確認してみます。具体的には、「あまりしていない」を基準として、「どちらかといえば, している」、「している」だとどのくらい5科目平均正答率が高いのかを(単)回帰分析を使って推定します。

モデルを定義する際、ここでは「あまりしていない」を基準とするのでダミー変数を0に変更し、「どちらかといえば, している」を1、「している」を2にそれぞれ変更しておきます。

このとき、朝食摂取頻度による5科目平均正答率の違いを表す回帰式は次のようになります。

y = β0 + β1x

yは5科目平均正答率、xは朝食摂取頻度、β0は切片となります。また、傾きβ1は「あまりしていない(0)」と比べて「どちらかといえば, している(1)」と「している(2)」がどの程度5科目平均正答率が高いかを示しています。

そして今回は回帰分析にはstatsmodelライブラリを使用し、formula APIを使ってモデル式を定義します。

また、カラム名は”平均正答率全科目”を”all_mean_score”、”生徒質問紙回答001”を”breakfast”のように英語表記に変更しておきます。

準備ができたので、モデル式を以下のようにして最小二乗法でフィッティングします。

form_const = 'all_mean_score ~ breakfast'
res_const = ols(form_const, data=df).fit()
print(res_const.summary())

上記の結果から、推定された式は次のようになります。

y = 52.9 + 4.36x

これはつまり、「あまりしていない(0)」と回答した生徒に比べて「どちらかといえば, している(1)」は約4.36点、「している(2)」は約9.7点高いことを意味しています。

交絡因子の調整

さて、前項で朝食摂取頻度がテストの点数に影響していることが示唆されました。しかし、単回帰分析による結果では朝食摂取頻度の効果を考える上では不十分です。例えば、就寝時刻が遅いと朝起きても食欲がなく朝食摂取頻度が低い傾向があるかもしれない。あるいは就寝時刻がバラバラだと体内リズムが崩れ、日中の頭の回転に影響してテストの点数が低い傾向に出ているのかもしれません。この場合、朝食摂取頻度が高いとテストの点数が高くなるのは見かけ上の関係に過ぎなく、実際には朝食摂取頻度とテストの点数には何も関係性が無いということになります。これまでの話を図示すると以下のようになります。

図のように2つの変数(今回であれば、朝食摂取頻度と5科目平均点)両方に対して影響を及ぼす因子を交絡因子と呼びます。

朝食摂取頻度の効果を知りたいとすると、この就寝時刻をうまいこと統制して一定にしなければなりません。つまり、就寝時刻が仮に同じであっても、朝食摂取頻度と5科目平均点との関係性を見ることが出来れば、それは朝食摂取頻度の真の推定値に近づいたということになります。

こういった交絡因子の調整の際には、重回帰分析を使うことで真の効果の推定値に近づくことができますので、やってみましょう。

生徒質問用紙(PDF/1.03MB)をもう一度見てみると、2番目の質問に「毎日, 同じくらいの時刻に寝ている(1: している, 2: どちらかといえば,している, 3: あまりしていない, 4: 全くしていない)」という就寝時刻に関する質問があります。この質問に対する回答はサンプルデータの”生徒質問紙回答_002”というカラムにありますので、こちらも”bedtime”のようにカラム名を英語表記に変更し、モデル式の変数に加えて再度フィッティングしてみます。

form_c = 'all_mean_score ~ breakfast + bedtime'
res_c = ols(form_c, data=df).fit()
print(res_c.summary())

上記の結果から、推定された式は次のようになります。

y = 51.5 + 4.12x1 + 0.91x2

前項で得られた朝食摂取頻度のみで推定された式と比べると、breakfastの係数が小さくなっていることがわかります。このことから、朝食摂取頻度と5科目平均点との正の相関関係の一部分は交絡因子である就寝時刻によるものだったということがわかります。

さて、当然ですが交絡因子は就寝時刻だけでなく他の要因も考えられます。例えば生徒自身の”やり抜く力”や”チャレンジ精神”といったいわゆる非認知能力もテストの点数に影響してきそうです。生徒質問用紙(PDF/1.03MB)の4番目と5番目に非認知能力に該当するような質問がありますので、最後にこれらも変数に加えてフィッティングしてみたいと思います。なお、4, 5番目の質問に対する回答データのカラム名も”accomplish”、”challenge”のように英語表記に変更しておきます。

form_c2 = 'all_mean_score ~ breakfast + bedtime + accomplish + challenge'
res_c2 = ols(form_c2, data=df).fit()
print(res_c2.summary())

上記の結果から、推定された式は次のようになります。

y = 47.6 + 4.01x1 + 0.76x2 + 1.62x3 + 0.16x4

これまでに得られた推定式と比べると、breakfastの係数が更に小さくなっていることがわかります。また、bedtimeのp値を見てみると有意差なしに変わっていて、accomplishが正の係数で有意差有りとなっています。ただ、これらの交絡因子を統制してもなお、breakfastの係数は正でかつ有意差有りとなっていることから、朝食摂取頻度が高いほど5科目平均点が高いということが言えそうです。

ここまでやってきたように、交絡因子を適切に調整することで朝食摂取頻度とテストの点数の真の効果に近づくことができました。もちろん、テストの点数に影響を及ぼしそうな交絡因子は他にもたくさんあると容易に想像できると思います。ですので、これまでの分析で朝食摂取頻度がテストの点数に影響を与える真の効果を推定できたわけではありませんのでその点ご注意ください。

最後に

本記事では、文部科学省が公開している全国学力・学習状況調査(全国学力テスト)のパブリックユースデータ(疑似データ)を用いて子どもの学力差に影響を与えている要因について分析してみました。交絡因子に気をつけながら真の効果を推定していく分析は非常に面白いですね。

ここではやりませんでしたが、例えば男女間で理数系科目の点数に差があるのか?といった色々な観点があると思います。ですので、まずは何を明らかにしたいのか?といった目的を明確にしてデータを見ていくのが大事ですね。

また、日本政府では家庭の経済状況や子どもの学力といった幅広い情報を一元化するデータベースを構築する動きもあり、今後ますます子育てや教育におけるデータ活用が進むと考えられます。

ユニファでは子育て環境の充実に貢献していくことに関心がある仲間を募集中です!

unifa-e.com