數據集下載:
鏈接:https://pan.baidu.com/s/1EMaN6uuQJlsrvaO2NQKgkQ
提取碼:tl58
一、項目背景
1.背景
該數據集記錄了280份來自不同國家、不同性別兩個學期的學生記錄,包括當前受教育程度、班級、所選課程、成績、出勤特征、以及家長參與等信息,通過分析數據並建立模型預測學生成績。
2.要求
從數據預處理(20分)、模型建立分析(30分)、參數調優(10分)、預測效果(10分)、數據可視化(10分)、項目報告(20分)給出綜合分數
3.數據集介紹
- gender-學生性別(“M”或“FM”)
- National-學生國籍(’ Kuwait’,’ Lebanon’,’ Egypt’,’ SaudiArabia’,’ USA’,’ Jordan’,’ Venezuela’,’ Iran’,’ Tunis’,’ Morocco’,’ Syria’,’ Palestine’,’ Iraq’,’ Lybia)
- PlaceofBirth-學生出生地(“KuwaIT”、“Jordan”、“Iraq ” 、“lebanon”、“SaudiArabia ”、“USA ”、“Palestine”、“Egypt”、“Tunis”、“Iran”、“Lybia ”、“Syria ”、“Morocco”、“venzuela”)
- StageID-學生所屬教育級別(“lowerlevel”、“MiddleSchool ”、“HighSchool”)
- GradeId-所屬年級(“G-01”、“G-02”、“G-03”、“G-04”、“G-05”、“G-06”、“G-07”、“G-08”、“G-09”、“G-10”、“G-11”、“G-12”)
- SectionID-學生所屬教室(‘A’,‘B’,‘C’)
- Topic--課程('IT'、 'Math'、 'Arabic' 、'Science'、'English'、 'Quran' 、'Spanish' 、'French' 、'History'、 'Biology' 'Chemistry' 、'Geology')
- Semester-學年(“F”、“S”)
- Relation-家長負責學生(’mom’,’father’)
- raisedhands-學生在課堂上舉起手多少次(數字:0-100)
- VisITedResources-學生訪問課程內容的次數(數字:0-100)
- AnnouncementsView-學生檢查新公告的次數(數字:0-100)
- Discussion-學生參加討論小組的次數(數字:0-100)
- ParentAnsweringSurvey-家長是否回答學校提供的調查(’Yes’,’No’)
- ParentschoolSatisfaction-家長對學校的滿意度(’Yes’,’No’)
- StudentAbsenceDays-每名學生缺勤天數(above-7, under-7)
- Class-學生成績分類(L、M、H)
二、分析過程
1.加載數據並並導入相關的包
import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
from sklearn import preprocessing, svm
from sklearn.linear_model import Perceptron
from sklearn.tree import DecisionTreeClassifier
data = pd.read_csv('xAPI-Edu-Data.csv')
data.head()
查看數據 :
| gender | NationalITy | PlaceofBirth | StageID | GradeID | SectionID | Topic | Semester | Relation | raisedhands | VisITedResources | AnnouncementsView | Discussion | ParentAnsweringSurvey | ParentschoolSatisfaction | StudentAbsenceDays | Class | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | M | KW | KuwaIT | lowerlevel | G-04 | A | IT | F | Father | 15 | 16 | 2 | 20 | Yes | Good | Under-7 | M |
| 1 | M | KW | KuwaIT | lowerlevel | G-04 | A | IT | F | Father | 20 | 20 | 3 | 25 | Yes | Good | Under-7 | M |
| 2 | M | KW | KuwaIT | lowerlevel | G-04 | A | IT | F | Father | 10 | 7 | 0 | 30 | No | Bad | Above-7 | L |
| 3 | M | KW | KuwaIT | lowerlevel | G-04 | A | IT | F | Father | 30 | 25 | 5 | 35 | No | Bad | Above-7 | L |
| 4 | M | KW | KuwaIT | lowerlevel | G-04 | A | IT | F | Father | 40 | 50 | 12 | 50 | No | Bad | Above-7 | M |
2.處理數據
2.1查看數據規格
data.shape
2.2查看列信息
data.info()

每列都是480行數據,且沒有空值,不需要再對數據進行預處理。
2.3查看學生成績類別
data.Class.unique()

學生成績一共分為三類['L', 'M', 'H'],這將作為評判學生的標准。
L:0-59 不及格;
M:60-89 中等;
H:90-100 高分。
3.分析數據
3.1學生成績分析
ax = sns.countplot(x='Class', data=data, order=['L', 'M', 'H'])
#給每列上方加上百分比
for a in ax.patches:
ax.annotate('{:0.2f}%'.format((a.get_height()*100)/len(data)),
(a.get_x()+0.24, a.get_height()+2))
plt.show()

大部分學生都處於中等成績,高分學生人數占總人數的26.46%。
#查看不及格學生信息
data.loc[data["Class"]=='L']
數據:
| gender | NationalITy | PlaceofBirth | StageID | GradeID | SectionID | Topic | Semester | Relation | raisedhands | VisITedResources | AnnouncementsView | Discussion | ParentAnsweringSurvey | ParentschoolSatisfaction | StudentAbsenceDays | Class | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 2 | M | KW | KuwaIT | lowerlevel | G-04 | A | IT | F | Father | 10 | 7 | 0 | 30 | No | Bad | Above-7 | L |
| 3 | M | KW | KuwaIT | lowerlevel | G-04 | A | IT | F | Father | 30 | 25 | 5 | 35 | No | Bad | Above-7 | L |
| 6 | M | KW | KuwaIT | MiddleSchool | G-07 | A | Math | F | Father | 35 | 12 | 0 | 17 | No | Bad | Above-7 | L |
| 12 | M | KW | KuwaIT | lowerlevel | G-04 | A | IT | F | Father | 5 | 1 | 0 | 11 | No | Bad | Above-7 | L |
| 13 | M | lebanon | lebanon | MiddleSchool | G-08 | A | Math | F | Father | 20 | 14 | 12 | 19 | No | Bad | Above-7 | L |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 469 | F | Jordan | Jordan | MiddleSchool | G-08 | A | Chemistry | S | Father | 9 | 6 | 15 | 85 | No | Bad | Above-7 | L |
| 474 | F | Jordan | Jordan | MiddleSchool | G-08 | A | Chemistry | F | Father | 2 | 7 | 4 | 8 | No | Bad | Above-7 | L |
| 475 | F | Jordan | Jordan | MiddleSchool | G-08 | A | Chemistry | S | Father | 5 | 4 | 5 | 8 | No | Bad | Above-7 | L |
| 478 | F | Jordan | Jordan | MiddleSchool | G-08 | A | History | F | Father | 30 | 17 | 14 | 57 | No | Bad | Above-7 | L |
| 479 | F | Jordan | Jordan | MiddleSchool | G-08 | A | History | S | Father | 35 | 14 | 23 | 62 | No | Bad | Above-7 | L |
127 rows × 17 columns
似乎不及格的學生缺課天數都超過七天,數值偏低,沒有學校調查。
3.2學生成績與性別關系
#分兩個10*5的畫布 fig, axarr = plt.subplots(2,figsize=(10,10)) #學生性別柱形圖 sns.countplot(x='gender', data=data, order=['M','F'], ax=axarr[0]) #不同性別與成績的柱形圖 sns.countplot(x='gender', hue='Class', data=data, order=['M', 'F'],hue_order = ['L', 'M', 'H'], ax=axarr[1]) plt.show()

由圖可以明顯看出女學生的不及格人數要遠遠少於男同學,且女學生的中等和高分段學生人數基本持平,男女學生在高分段人數相差不大。
推測:性別可能影響的學生成績。
3.3學生國籍與成績的關系
fig, axarr = plt.subplots(2,figsize=(10,10)) #學生國籍柱形圖 sns.countplot(x='NationalITy', data=data, ax=axarr[0]) #不同國籍與成績柱形圖 sns.countplot(x='NationalITy', hue='Class', data=data,hue_order = ['L', 'M', 'H'], ax=axarr[1]) plt.show()

由於除了KW和jordan這兩個國籍的學生之外,其余國籍的學生可分析的人數都比較少,並不能得出有效信息。
對比兩圖:
1.能得出相比於KW的學生,jordan學生不及格人數為KW學生的一半,總體成績更好一些。
2.Iran和Lybia國籍的學生沒有取得高分的。
#查看Iran國籍的學生
data.loc[data['NationalITy'] == 'Iran']

#查看Lybia國籍的學生
data.loc[data['NationalITy'] == 'Lybia']

在觀察后,發現Lybia國籍學生似乎與所有未通過考試的學生有相似的數據(缺課超過7天,數值偏低,沒有學校調查等)。
3.4學生所屬教育級別與成績的關系
fig, axarr = plt.subplots(2,figsize=(6,6)) sns.countplot(x='StageID', data=data, ax=axarr[0]) sns.countplot(x='StageID', hue='Class', data=data, hue_order = ['L', 'M', 'H'], ax=axarr[1]) plt.show()

由圖可看出,不管是所屬哪個教育階段,都是中等成績的人數偏多。
3.5學生所屬年級與成績的關系
fig, axarr = plt.subplots(2,figsize=(7,7))
sns.countplot(x='GradeID',
data=data,
order=['G-02', 'G-04', 'G-05', 'G-06', 'G-07', 'G-08', 'G-09', 'G-10', 'G-11', 'G-12'],
ax=axarr[0])
sns.countplot(x='GradeID',
hue='Class',
data=data,
order=['G-02', 'G-04', 'G-05', 'G-06', 'G-07', 'G-08', 'G-09', 'G-10', 'G-11', 'G-12'],
hue_order = ['L', 'M', 'H'],
ax=axarr[1])
plt.show()

從這些結果來看,五年級、九年級和十年級的學生人數很少。 除此之外,沒有五年級學生及格,也沒有九年級學生取得高分。
# 查看五年級的樣本
data.loc[data['GradeID']=='G-05']

#查看九年級的樣本
data.loc[data['GradeID']=='G-09']

在觀察后,發現五年級學生似乎與所有未通過考試的學生有相似的數據(缺課超過7天,數值偏低,沒有學校調查等) 並且在對比九年級的學生成績之后發現,這樣的情況也發生在九年級當中。
3.6學生所屬教室與成績的關系
fig, axarr = plt.subplots(2,figsize=(5,5))
sns.countplot(x='SectionID', data=data,
order=['A', 'B', 'C'], ax = axarr[0])
sns.countplot(x='SectionID', hue='Class',
data=data, order=['A', 'B', 'C'],
hue_order = ['L', 'M', 'H'], ax = axarr[1])
plt.show()

三個班的總體趨勢都差不多,我們並不能得出什么有效信息
3.7學生所選課程與成績的關系
fig, axarr = plt.subplots(2,figsize=(10,10)) sns.countplot(x='Topic', data=data, ax = axarr[0]) sns.countplot(x='Topic', hue='Class', data=data,hue_order = ['L', 'M', 'H'], ax = axarr[1]) plt.show()

在圖中可以看到一個有趣的現象,Geology專業沒有不及格的學生。 那么為什么呢? 繼續分析
3.8不同學年學生與成績的關系
fig, axarr = plt.subplots(2,figsize=(10,10)) sns.countplot(x='Semester', data=data, ax = axarr[0]) sns.countplot(x='Semester', hue='Class', data=data,hue_order = ['L', 'M', 'H'], ax = axarr[1]) plt.show()

由圖可以看出,第二學年的不及格人數比第一學年少,高分人數都比第一學年多。
推測:學年與成績相關。
3.9學生監護人與學生成績的關系
fig, axarr = plt.subplots(2,figsize=(5,5)) sns.countplot(x='Relation', data=data, ax = axarr[0]) sns.countplot(x='Relation', hue='Class', data=data,hue_order = ['L', 'M', 'H'], ax = axarr[1]) plt.show()

從這兩張表可以看出,母親作為監護人和學生不太可能不及格之間似乎有關聯。
3.10學生在課堂舉手次數、訪問課程內容次數、檢查新公告次數、參加討論次數與成績的關系
sns.pairplot(data, hue="Class",
diag_kind="kde",
hue_order = ['L', 'M', 'H'],
markers=["o", "s", "D"])
plt.show()

#查看課程的舉手,訪問課程,查看新公告,討論此處中位數
data.groupby('Topic').median()

在這里我們可以看出五年級和九年級的數據比其他大多數年級少上許多
3.11家長是否回答學生提供的調查與學生成績的關系
fig, axarr = plt.subplots(2,figsize=(10,10))
sns.countplot(x='ParentAnsweringSurvey', data=data,
order=['Yes', 'No'], ax = axarr[0])
sns.countplot(x='ParentAnsweringSurvey', hue='Class',
data=data, order=['Yes', 'No'], hue_order = ['L', 'M', 'H'],
ax = axarr[1])
plt.show()

觀察這兩張圖可能會覺得家長是否回答學校調查與學生成績有關,但是我們並不知道是不是學生的成績導致他們是否回應學校的調查,這無法從數據得出。
3.12家長對於學校滿意度與學生成績的關系
fig, axarr = plt.subplots(2,figsize=(10,10))
sns.countplot(x='ParentschoolSatisfaction', data=data,
order=['Good', 'Bad'], ax = axarr[0])
sns.countplot(x='ParentschoolSatisfaction', hue='Class',
data=data, order=['Good', 'Bad'],
hue_order = ['L', 'M', 'H'], ax = axarr[1])
plt.show()

看到家長對於學校滿意的結果時,也會出現上次的因果關系。
3.13學生缺勤天數與學生成績的關系
fig, axarr = plt.subplots(2,figsize=(10,10))
sns.countplot(x='StudentAbsenceDays', data=data,
order=['Under-7', 'Above-7'],
ax = axarr[0])
sns.countplot(x='StudentAbsenceDays', hue='Class',
data=data, order=['Under-7', 'Above-7'],
hue_order = ['L', 'M', 'H'],
ax = axarr[1])
plt.show()

觀察得出,學習時間與學生成績有很強的相關性,缺課查過七天的學生很少取得高分,缺課少於七天的學生很少不及格。
綜上分析,學生成績與訪問資源、學生缺席日、舉手、公告意見、調查答案、監護人、家長滿意度、討論、性別和學期這些屬性有關。
4.建立模型預測與學生成績的關系
4.1處理非數據列
#將年級轉化為數據
gradeID_dict = {"G-01" : 1,
"G-02" : 2,
"G-03" : 3,
"G-04" : 4,
"G-05" : 5,
"G-06" : 6,
"G-07" : 7,
"G-08" : 8,
"G-09" : 9,
"G-10" : 10,
"G-11" : 11,
"G-12" : 12}
data = data.replace({"GradeID" : gradeID_dict})
#將成績轉化為數據
class_dict = {"L" : -1,
"M" : 0,
"H" : 1}
data = data.replace({"Class" : class_dict})
# 轉換成Scale數據
data["GradeID"] = preprocessing.scale(data["GradeID"])
data["raisedhands"] = preprocessing.scale(data["raisedhands"])
data["VisITedResources"] = preprocessing.scale(data["VisITedResources"])
data["AnnouncementsView"] = preprocessing.scale(data["AnnouncementsView"])
data["Discussion"] = preprocessing.scale(data["Discussion"])
# 使用虛擬編碼轉換 將11列轉換成64列
data = pd.get_dummies(data, columns=["gender",
"NationalITy",
"PlaceofBirth",
"SectionID",
"StageID",
"Topic",
"Semester",
"Relation",
"ParentAnsweringSurvey",
"ParentschoolSatisfaction",
"StudentAbsenceDays"])
data.head()
部分數據截圖:

4.2列出成績與其他屬性列的相關性
corr = data.corr() corr.iloc[[5]]
部分數據截圖:

由表中數據可以看出訪問資源、學生缺席日、舉手、公告意見、調查答案、監護人、家長滿意度、討論、性別和學期都與Class有很強的相關性,這和我們之前的分析一樣。
5.訓練與預測
5.1使用Perception分類器
perc = Perceptron(eta0=0.1, random_state=15)
#data_train與data_test之比為7:3
results=[]
#進行多次預測
for _ in range(1000):
# 隨機生成0.7的數據集
data_train = data.sample(frac=0.7)
# 數據集lable
data_train_X = data_train.loc[:, lambda x: [l for l in data if l != "Class"]]
data_train_Y = data_train.loc[:, lambda x: "Class"]
# 剩下的就是測試數據集
data_test = data.loc[~data.index.isin(data_train.index)]
# 測試數據集lable
data_test_X = data_test.loc[:, lambda x: [l for l in data if l != "Class"]]
data_test_Y = data_test.loc[:, lambda x: "Class"]
# 用 .fit()方法進行訓練
perc.fit(data_train_X, data_train_Y)
#預測結果准確率
results.append(perc.score(data_test_X, data_test_Y))
#結果圖
plt.plot([*range(0,1000)],results)

Final = np.hstack(results)
print('Minimum Accuracy Score: %.8f' % Final[Final.argmin()])
print('Maximum Accuracy Score: %.8f' % Final[Final.argmax()])
print('Average Accuracy Score: %.8f' % np.average(Final))

Perceptron 分類器平均預測結果准確率是0.65
5.2使用決策樹分類器
#data_train與data_test之比為7:3
results2 = []
for i in range(1,15):
# 訓練集
data_train = data.sample(frac=0.7)
# lable
data_train_X = data_train.loc[:, lambda x: [l for l in data if l != "Class"]]
data_train_Y = data_train.loc[:, lambda x: "Class"]
# 測試集
data_test = data.loc[~data.index.isin(data_train.index)]
# label
data_test_X = data_test.loc[:, lambda x: [l for l in data if l != "Class"]]
data_test_Y = data_test.loc[:, lambda x: "Class"]
#建立不同深度決策樹
tree = DecisionTreeClassifier(random_state=56, criterion='gini', max_depth=i)
# .fit() 訓練
tree.fit(data_train_X, data_train_Y)
#結果
results2.append(tree.score(data_test_X, data_test_Y))
#繪制不同深度決策樹准確率折線圖
plt.plot([*range(1,15)],results2) plt.grid(True, linestyle='--', alpha=0.5)

#輸出最大值及對應深度
print(max(results2),results2.index(max(results2)))

綜上,對比兩個不同分類器得出的結果,深度為7的決策樹分類器經過訓練之后預測的結果更准確。
