PISA 2018データを読むほぼそのままである。
2023年12月5日、PISA 2022 resultsが公開された。データはPISA 2022 Databaseからダウンロードできる。ここではSPSS用データの生徒アンケート(STU_QQQ_SPSS.zipを展開して得られるCY08MSP_STU_QQQ.savという2Gバイトほどのファイル)をPythonで読み込んでみる。
SPSSのsavデータを読むにはpandasの read_spss()
でもできるが,これはpyreadstatパッケージを呼び出しているだけである。以下ではpyreadstatを直接使う(pip install pyreadstat
しておく):
import pyreadstat
メモリの多いマシンなら全部読み込んでしまうのが手っ取り早い:
df, meta = pyreadstat.read_sav('CY08MSP_STU_QQQ.sav')
dfは
import sys sys.getsizeof(df)
とすればわかるように,約7Gのメモリを占める。
df.shape
と打ち込めば613744行1279列であることがわかる。
これ以降は次と同じである。
まずはメタデータだけ読んでみよう:
df, meta = pyreadstat.read_sav('CY08MSP_STU_QQQ.sav', metadataonly=True)
メタデータに含まれる変数名とその説明を表示する(長い出力に注意!):
meta.column_names_to_labels
出力は次のようになる:
{'CNT': 'Country code 3-character', 'CNTRYID': 'Country Identifier', 'CNTSCHID': 'Intl. School ID', 'CNTSTUID': 'Intl. Student ID', ・・・(以下略)・・・ }
これでは見にくいので,JSON形式で保存しておくことにする:
import json with open("column_names_to_labels.json", "w") as f: json.dump(meta.column_names_to_labels, f, ensure_ascii=False, indent=2) with open("variable_value_labels.json", "w") as f: json.dump(meta.variable_value_labels, f, ensure_ascii=False, indent=2)
これらはそれぞれ変数ラベルとその説明,変数ラベルとその値の説明である。例えば "IC182Q02JA"
というラベルについては前者が
"IC182Q02JA": "Agree/disagree: I am interested in learning [computer programming].",
後者が
"IC182Q02JA": { "1.0": "Strongly disagree", "2.0": "Disagree", "3.0": "Agree", "4.0": "Strongly agree", "95.0": "Valid Skip", "97.0": "Not Applicable", "98.0": "Invalid", "99.0": "No Response" },
となっている。国名(3文字)とこの項目だけを改めて読み込んでみよう:
df, meta = pyreadstat.read_sav('CY08MSP_STU_QQQ.sav', usecols=['CNT', 'IC182Q02JA'])
"IC182Q02JA"
の回答の度数分布を表示する:
df["IC182Q02JA"].value_counts()
結果は
IC182Q02JA 3.0 134464 2.0 93118 4.0 50305 1.0 44096 Name: count, dtype: int64
となって順番通りにならない。value_counts(sort=False)
としてもランダムな順序になる。そこで
df[['CNT', 'IC182Q02JA']].groupby('IC182Q02JA').count()
とすると
CNT IC182Q02JA 1.0 44096 2.0 93118 3.0 134464 4.0 50305
のように表らしくなってくれる。あるいは別解として
import pandas as pd pd.crosstab(df["IC182Q02JA"], "count") # "count" はダミー
とすると
col_0 count IC182Q02JA 1.0 44096 2.0 93118 3.0 134464 4.0 50305
になってくれるようだ。crosstab()
の本来の使い方は
pd.crosstab(df["CNT"], df["IC182Q02JA"])
のようにして国名と回答の度数分布を
IC182Q02JA 1.0 2.0 3.0 4.0 CNT ALB 207 640 1812 859 ARG 856 1697 3224 1299 AUS 2586 4376 3631 1002 AUT 1365 1569 1416 969 BEL 1180 2285 2200 759 BGR 391 1112 1986 704 ・・・後略・・・
のように出力することである。日本に限れば
df["IC182Q02JA"][df["CNT"] == "JPN"].value_counts() # または df[df["CNT"] == "JPN"].groupby('IC182Q02JA').count()
とすればよい。
国ごとに回答3+回答4の割合を求めるには
s = pd.crosstab(df["CNT"], df["IC182Q02JA"]).apply(lambda x: (x.iloc[2]+x.iloc[3])/sum(x), axis=1) s.sort_values(inplace=True)
手抜き棒グラフにする:
import matplotlib.pyplot as plt plt.barh(s.index, s) plt.savefig('231206a.svg', bbox_inches="tight")
国名を日本語にし,ドットプロットにしてみる:
co = pd.read_csv("https://okumuralab.org/~okumura/stat/data/countries.csv") dic = { r['三字']: r['国名'] for i,r in co.iterrows() } cj = [ dic[x] for x in s.index ] plt.plot(s, cj, "o") plt.grid() plt.savefig('231206b.svg', bbox_inches="tight")
Last modified: