一、摘要
該數據集來自kaggle,數據集包含了直接影響學生成績的原因,本選題應用Python網絡爬蟲方法。
二、選題背景:
影響學生學習成績的因素很多,但就學生本身來說,對學習成績起決定作用的,主要是學生學習.的心理狀態、智能水平、學習方法和學習時間等四個方面的因素。本書根據這四個方面的因素和中學生的學習特點,以方法為線索,從提高學生的認識水平着手,幫助學生提高心理素質、促進智能發展、改進學習方法和科學安排時間。
三、過程及代碼:
1 import numpy as np 2 import pandas as pd 3 import matplotlib.pyplot as plt 4 import seaborn as sns 5 from matplotlib.font_manager import FontProperties 6 from sklearn.linear_model import LinearRegression 7 from sklearn.linear_model import ElasticNet 8 from sklearn.ensemble import RandomForestRegressor 9 from sklearn.ensemble import ExtraTreesRegressor 10 from sklearn.ensemble import GradientBoostingRegressor 11 from sklearn.svm import SVR 12 from sklearn.model_selection import train_test_split 13 from sklearn.preprocessing import MinMaxScaler 14 from sklearn.metrics import mean_squared_error, mean_absolute_error, median_absolute_error 15 import scipy 16 import pickle 17 18 19 # 初始化數據 20 plt.rcParams['font.sans-serif'] = ['SimHei'] # 中文字體設置-黑體 21 plt.rcParams['axes.unicode_minus'] = False # 解決保存圖像是負號'-'顯示為方塊的問題 22 sns.set(font='SimHei') # 解決Seaborn中文顯示問題 23 student = pd.read_csv('student-mat.csv') 24 # print(student.head()) 25 26 # 分析G3數據屬性 27 # print(student['G3'].describe())
1 # 根據人數多少統計各分數段的學生人數 2 grade_counts = student['G3'].value_counts().sort_values().plot.barh(width=.9,color=sns.color_palette('inferno',40)) 3 grade_counts.axes.set_title('各分數值的學生分布',fontsize=30) 4 grade_counts.set_xlabel('學生數量', fontsize=30) 5 grade_counts.set_ylabel('最終成績', fontsize=30) 6 plt.show()
1 # 從低到高展示成績分布圖 2 grade_distribution = sns.countplot(student['G3']) 3 grade_distribution.set_title('成績分布圖', fontsize=30) 4 grade_distribution.set_xlabel('期末成績', fontsize=20) 5 grade_distribution.set_ylabel('人數統計', fontsize=20) 6 plt.show() 7 8 # 檢查各個列是否有null值,如果沒有表示成績中的0分確實是0分 9 # print(student.isnull().any())
1 # 分析性別比例 2 male_studs = len(student[student['sex'] == 'M']) 3 female_studs = len(student[student['sex'] == 'F']) 4 print('男同學數量:',male_studs) 5 print('女同學數量:',female_studs)
1 # 分析年齡分布比例(曲線圖) 2 age_distribution = sns.kdeplot(student['age'], shade=True) 3 age_distribution.axes.set_title('學生年齡分布圖', fontsize=30) 4 age_distribution.set_xlabel('年齡', fontsize=20) 5 age_distribution.set_ylabel('比例', fontsize=20) 6 plt.show()
1 # 分性別年齡分布圖(柱狀圖) 2 age_distribution_sex = sns.countplot('age', hue='sex', data=student) 3 age_distribution_sex.axes.set_title('不同年齡段的學生人數', fontsize=30) 4 age_distribution_sex.set_xlabel('年齡', fontsize=30) 5 age_distribution_sex.set_ylabel('人數', fontsize=30) 6 plt.show()
1 # 各年齡段的成績箱型圖 2 age_grade_boxplot = sns.boxplot(x='age', y='G3', data=student) 3 age_grade_boxplot.axes.set_title('年齡與分數', fontsize = 30) 4 age_grade_boxplot.set_xlabel('年齡', fontsize = 20) 5 age_grade_boxplot.set_ylabel('分數', fontsize = 20) 6 plt.show()
1 # 各年齡段的成績分布圖 2 age_grade_swarmplot = sns.swarmplot(x='age', y='G3', data=student) 3 age_grade_swarmplot.axes.set_title('年齡與分數', fontsize = 30) 4 age_grade_swarmplot.set_xlabel('年齡', fontsize = 20) 5 age_grade_swarmplot.set_ylabel('分數', fontsize = 20) 6 plt.show()
1 # 城鄉學生計數 2 areas_countplot = sns.countplot(student['address']) 3 areas_countplot.axes.set_title('城鄉學生', fontsize = 30) 4 areas_countplot.set_xlabel('家庭住址', fontsize = 20) 5 areas_countplot.set_ylabel('計數', fontsize = 20) 6 plt.show()
1 # Grade distribution by address 2 sns.kdeplot(student.loc[student['address'] == 'U', 'G3'], label='Urban', shade = True) 3 sns.kdeplot(student.loc[student['address'] == 'R', 'G3'], label='Rural', shade = True) 4 plt.title('城市學生獲得了更好的成績嗎?', fontsize = 20) 5 plt.xlabel('分數', fontsize = 20) 6 plt.ylabel('占比', fontsize = 20) 7 plt.show() 8 9 # 選取G3屬性值 10 labels = student['G3'] 11 12 # 刪除school,G1和G2屬性 13 student = student.drop(['school', 'G1', 'G2'], axis='columns') 14 15 # 對離散變量進行獨熱編碼 16 student = pd.get_dummies(student) 17 18 # 選取相關性最強的8個 19 most_correlated = student.corr().abs()['G3'].sort_values(ascending=False) 20 most_correlated = most_correlated[:9] 21 print(most_correlated)
1 # 失敗次數成績分布圖 2 failures_swarmplot = sns.swarmplot(x=student['failures'],y=student['G3']) 3 failures_swarmplot.axes.set_title('失敗次數少的學生分數更高嗎?', fontsize = 30) 4 failures_swarmplot.set_xlabel('失敗次數', fontsize = 20) 5 failures_swarmplot.set_ylabel('最終成績', fontsize = 20) 6 plt.show()
1 # 雙親受教育水平的影響 2 family_ed = student['Fedu'] + student['Medu'] 3 family_ed_boxplot = sns.boxplot(x=family_ed,y=student['G3']) 4 family_ed_boxplot.axes.set_title('雙親受教育水平的影響', fontsize = 30) 5 family_ed_boxplot.set_xlabel('家庭教育水平(Mother + Father)', fontsize = 20) 6 family_ed_boxplot.set_ylabel('最終成績', fontsize = 20) 7 plt.show()
1 # 學生自己的升學意志對成績的影響 2 personal_wish = sns.boxplot(x = student['higher_yes'], y=student['G3']) 3 personal_wish.axes.set_title('學生升學意願對成績的影響', fontsize = 30) 4 personal_wish.set_xlabel('更高級的教育 (1 = 是)', fontsize = 20) 5 personal_wish.set_ylabel('最終成績', fontsize = 20) 6 plt.show()
1 # 分割數據集 2 X_train, X_test, y_train, y_test = train_test_split(student, labels, test_size = 0.25, random_state=42) 3 4 # 計算平均絕對誤差和均方根誤差 5 # MAE-平均絕對誤差 6 # RMSE-均方根誤差 7 def evaluate_predictions(predictions, true): 8 mae = np.mean(abs(predictions - true)) 9 rmse = np.sqrt(np.mean((predictions - true) ** 2)) 10 11 return mae, rmse 12 13 # 求中位數 14 median_pred = X_train['G3'].median() 15 16 # 所有中位數的列表 17 median_preds = [median_pred for _ in range(len(X_test))] 18 19 # 存儲真實的G3值以傳遞給函數 20 true = X_test['G3'] 21 22 # 展示基准 23 mb_mae, mb_rmse = evaluate_predictions(median_preds, true) 24 print('Median Baseline MAE: {:.4f}'.format(mb_mae)) 25 print('Median Baseline RMSE: {:.4f}'.format(mb_rmse)) 26 27 # 通過訓練集訓練和測試集測試來生成多個線性模型 28 def evaluate(X_train, X_test, y_train, y_test): 29 # 模型名稱 30 model_name_list = ['Linear Regression', 'ElasticNet Regression', 31 'Random Forest', 'Extra Trees', 'SVM', 32 'Gradient Boosted', 'Baseline'] 33 X_train = X_train.drop('G3', axis='columns') 34 X_test = X_test.drop('G3', axis='columns') 35 36 # 實例化模型 37 model1 = LinearRegression() 38 model2 = ElasticNet(alpha=1.0, l1_ratio=0.5) 39 model3 = RandomForestRegressor(n_estimators=100) 40 model4 = ExtraTreesRegressor(n_estimators=100) 41 model5 = SVR(kernel='rbf', degree=3, C=1.0, gamma='auto') 42 model6 = GradientBoostingRegressor(n_estimators=50) 43 44 # 結果數據框 45 results = pd.DataFrame(columns=['mae', 'rmse'], index = model_name_list) 46 47 # 每種模型的訓練和預測 48 for i, model in enumerate([model1, model2, model3, model4, model5, model6]): 49 model.fit(X_train, y_train) 50 predictions = model.predict(X_test) 51 52 # 誤差標准 53 mae = np.mean(abs(predictions - y_test)) 54 rmse = np.sqrt(np.mean((predictions - y_test) ** 2)) 55 56 # 將結果插入結果框 57 model_name = model_name_list[i] 58 results.loc[model_name, :] = [mae, rmse] 59 60 # 中值基准度量 61 baseline = np.median(y_train) 62 baseline_mae = np.mean(abs(baseline - y_test)) 63 baseline_rmse = np.sqrt(np.mean((baseline - y_test) ** 2)) 64 65 results.loc['Baseline', :] = [baseline_mae, baseline_rmse] 66 67 return results 68 results = evaluate(X_train, X_test, y_train, y_test) 69 print(results) 70 71 # 找出最合適的模型 72 plt.figure(figsize=(12, 8)) 73 74 # 平均絕對誤差 75 ax = plt.subplot(1, 2, 1) 76 results.sort_values('mae', ascending = True).plot.bar(y = 'mae', color = 'b', ax = ax, fontsize=20) 77 plt.title('平均絕對誤差', fontsize=20) 78 plt.ylabel('MAE', fontsize=20) 79 80 # 均方根誤差 81 ax = plt.subplot(1, 2, 2) 82 results.sort_values('rmse', ascending = True).plot.bar(y = 'rmse', color = 'r', ax = ax, fontsize=20) 83 plt.title('均方根誤差', fontsize=20) 84 plt.ylabel('RMSE',fontsize=20) 85 plt.tight_layout() 86 plt.show() 87 88 # 保存線性回歸模型 89 model = LinearRegression() 90 model.fit(X_train, y_train) 91 filename = 'LR_Model' 92 pickle.dump(model, open(filename, 'wb'))
完整代碼:
1 import numpy as np 2 import pandas as pd 3 import matplotlib.pyplot as plt 4 import seaborn as sns 5 from matplotlib.font_manager import FontProperties 6 from sklearn.linear_model import LinearRegression 7 from sklearn.linear_model import ElasticNet 8 from sklearn.ensemble import RandomForestRegressor 9 from sklearn.ensemble import ExtraTreesRegressor 10 from sklearn.ensemble import GradientBoostingRegressor 11 from sklearn.svm import SVR 12 from sklearn.model_selection import train_test_split 13 from sklearn.preprocessing import MinMaxScaler 14 from sklearn.metrics import mean_squared_error, mean_absolute_error, median_absolute_error 15 import scipy 16 import pickle 17 18 19 # 初始化數據 20 plt.rcParams['font.sans-serif'] = ['SimHei'] # 中文字體設置-黑體 21 plt.rcParams['axes.unicode_minus'] = False # 解決保存圖像是負號'-'顯示為方塊的問題 22 sns.set(font='SimHei') # 解決Seaborn中文顯示問題 23 student = pd.read_csv('student-mat.csv') 24 25 # print(student.head()) 26 27 # 分析G3數據屬性 28 # print(student['G3'].describe()) 29 30 # 根據人數多少統計各分數段的學生人數 31 grade_counts = student['G3'].value_counts().sort_values().plot.barh(width=.9,color=sns.color_palette('inferno',40)) 32 grade_counts.axes.set_title('各分數值的學生分布',fontsize=30) 33 grade_counts.set_xlabel('學生數量', fontsize=30) 34 grade_counts.set_ylabel('最終成績', fontsize=30) 35 plt.show() 36 37 # 從低到高展示成績分布圖 38 grade_distribution = sns.countplot(student['G3']) 39 grade_distribution.set_title('成績分布圖', fontsize=30) 40 grade_distribution.set_xlabel('期末成績', fontsize=20) 41 grade_distribution.set_ylabel('人數統計', fontsize=20) 42 plt.show() 43 44 # 檢查各個列是否有null值,如果沒有表示成績中的0分確實是0分 45 46 # print(student.isnull().any()) 47 48 # 分析性別比例 49 male_studs = len(student[student['sex'] == 'M']) 50 female_studs = len(student[student['sex'] == 'F']) 51 print('男同學數量:',male_studs) 52 print('女同學數量:',female_studs) 53 54 # 分析年齡分布比例(曲線圖) 55 age_distribution = sns.kdeplot(student['age'], shade=True) 56 age_distribution.axes.set_title('學生年齡分布圖', fontsize=30) 57 age_distribution.set_xlabel('年齡', fontsize=20) 58 age_distribution.set_ylabel('比例', fontsize=20) 59 plt.show() 60 61 # 分性別年齡分布圖(柱狀圖) 62 age_distribution_sex = sns.countplot('age', hue='sex', data=student) 63 age_distribution_sex.axes.set_title('不同年齡段的學生人數', fontsize=30) 64 age_distribution_sex.set_xlabel('年齡', fontsize=30) 65 age_distribution_sex.set_ylabel('人數', fontsize=30) 66 plt.show() 67 68 # 各年齡段的成績箱型圖 69 age_grade_boxplot = sns.boxplot(x='age', y='G3', data=student) 70 age_grade_boxplot.axes.set_title('年齡與分數', fontsize = 30) 71 age_grade_boxplot.set_xlabel('年齡', fontsize = 20) 72 age_grade_boxplot.set_ylabel('分數', fontsize = 20) 73 plt.show() 74 75 # 各年齡段的成績分布圖 76 age_grade_swarmplot = sns.swarmplot(x='age', y='G3', data=student) 77 age_grade_swarmplot.axes.set_title('年齡與分數', fontsize = 30) 78 age_grade_swarmplot.set_xlabel('年齡', fontsize = 20) 79 age_grade_swarmplot.set_ylabel('分數', fontsize = 20) 80 plt.show() 81 82 # 城鄉學生計數 83 areas_countplot = sns.countplot(student['address']) 84 areas_countplot.axes.set_title('城鄉學生', fontsize = 30) 85 areas_countplot.set_xlabel('家庭住址', fontsize = 20) 86 areas_countplot.set_ylabel('計數', fontsize = 20) 87 plt.show() 88 89 # Grade distribution by address 90 sns.kdeplot(student.loc[student['address'] == 'U', 'G3'], label='Urban', shade = True) 91 sns.kdeplot(student.loc[student['address'] == 'R', 'G3'], label='Rural', shade = True) 92 plt.title('城市學生獲得了更好的成績嗎?', fontsize = 20) 93 plt.xlabel('分數', fontsize = 20) 94 plt.ylabel('占比', fontsize = 20) 95 plt.show() 96 97 # 選取G3屬性值 98 labels = student['G3'] 99 100 # 刪除school,G1和G2屬性 101 student = student.drop(['school', 'G1', 'G2'], axis='columns') 102 103 # 對離散變量進行獨熱編碼 104 student = pd.get_dummies(student) 105 106 # 選取相關性最強的8個 107 most_correlated = student.corr().abs()['G3'].sort_values(ascending=False) 108 most_correlated = most_correlated[:9] 109 print(most_correlated) 110 111 # 失敗次數成績分布圖 112 failures_swarmplot = sns.swarmplot(x=student['failures'],y=student['G3']) 113 failures_swarmplot.axes.set_title('失敗次數少的學生分數更高嗎?', fontsize = 30) 114 failures_swarmplot.set_xlabel('失敗次數', fontsize = 20) 115 failures_swarmplot.set_ylabel('最終成績', fontsize = 20) 116 plt.show() 117 118 # 雙親受教育水平的影響 119 family_ed = student['Fedu'] + student['Medu'] 120 family_ed_boxplot = sns.boxplot(x=family_ed,y=student['G3']) 121 family_ed_boxplot.axes.set_title('雙親受教育水平的影響', fontsize = 30) 122 family_ed_boxplot.set_xlabel('家庭教育水平(Mother + Father)', fontsize = 20) 123 family_ed_boxplot.set_ylabel('最終成績', fontsize = 20) 124 plt.show() 125 126 # 學生自己的升學意志對成績的影響 127 personal_wish = sns.boxplot(x = student['higher_yes'], y=student['G3']) 128 personal_wish.axes.set_title('學生升學意願對成績的影響', fontsize = 30) 129 personal_wish.set_xlabel('更高級的教育 (1 = 是)', fontsize = 20) 130 personal_wish.set_ylabel('最終成績', fontsize = 20) 131 plt.show() 132 133 # 分割數據集 134 X_train, X_test, y_train, y_test = train_test_split(student, labels, test_size = 0.25, random_state=42) 135 136 # 計算平均絕對誤差和均方根誤差 137 138 # MAE-平均絕對誤差 139 140 # RMSE-均方根誤差 141 def evaluate_predictions(predictions, true): 142 mae = np.mean(abs(predictions - true)) 143 rmse = np.sqrt(np.mean((predictions - true) ** 2)) 144 145 return mae, rmse 146 147 # 求中位數 148 median_pred = X_train['G3'].median() 149 150 # 所有中位數的列表 151 median_preds = [median_pred for _ in range(len(X_test))] 152 153 # 存儲真實的G3值以傳遞給函數 154 true = X_test['G3'] 155 156 # 展示基准 157 mb_mae, mb_rmse = evaluate_predictions(median_preds, true) 158 print('Median Baseline MAE: {:.4f}'.format(mb_mae)) 159 print('Median Baseline RMSE: {:.4f}'.format(mb_rmse)) 160 161 # 通過訓練集訓練和測試集測試來生成多個線性模型 162 def evaluate(X_train, X_test, y_train, y_test): 163 164 # 模型名稱 165 model_name_list = ['Linear Regression', 'ElasticNet Regression', 166 'Random Forest', 'Extra Trees', 'SVM', 167 'Gradient Boosted', 'Baseline'] 168 X_train = X_train.drop('G3', axis='columns') 169 X_test = X_test.drop('G3', axis='columns') 170 171 # 實例化模型 172 model1 = LinearRegression() 173 model2 = ElasticNet(alpha=1.0, l1_ratio=0.5) 174 model3 = RandomForestRegressor(n_estimators=100) 175 model4 = ExtraTreesRegressor(n_estimators=100) 176 model5 = SVR(kernel='rbf', degree=3, C=1.0, gamma='auto') 177 model6 = GradientBoostingRegressor(n_estimators=50) 178 179 # 結果數據框 180 results = pd.DataFrame(columns=['mae', 'rmse'], index = model_name_list) 181 182 # 每種模型的訓練和預測 183 for i, model in enumerate([model1, model2, model3, model4, model5, model6]): 184 model.fit(X_train, y_train) 185 predictions = model.predict(X_test) 186 187 # 誤差標准 188 mae = np.mean(abs(predictions - y_test)) 189 rmse = np.sqrt(np.mean((predictions - y_test) ** 2)) 190 191 # 將結果插入結果框 192 model_name = model_name_list[i] 193 results.loc[model_name, :] = [mae, rmse] 194 195 # 中值基准度量 196 baseline = np.median(y_train) 197 baseline_mae = np.mean(abs(baseline - y_test)) 198 baseline_rmse = np.sqrt(np.mean((baseline - y_test) ** 2)) 199 200 results.loc['Baseline', :] = [baseline_mae, baseline_rmse] 201 202 return results 203 results = evaluate(X_train, X_test, y_train, y_test) 204 print(results) 205 206 # 找出最合適的模型 207 plt.figure(figsize=(12, 8)) 208 209 # 平均絕對誤差 210 ax = plt.subplot(1, 2, 1) 211 results.sort_values('mae', ascending = True).plot.bar(y = 'mae', color = 'b', ax = ax, fontsize=20) 212 plt.title('平均絕對誤差', fontsize=20) 213 plt.ylabel('MAE', fontsize=20) 214 215 # 均方根誤差 216 ax = plt.subplot(1, 2, 2) 217 results.sort_values('rmse', ascending = True).plot.bar(y = 'rmse', color = 'r', ax = ax, fontsize=20) 218 plt.title('均方根誤差', fontsize=20) 219 plt.ylabel('RMSE',fontsize=20) 220 plt.tight_layout() 221 plt.show() 222 223 # 保存線性回歸模型 224 model = LinearRegression() 225 model.fit(X_train, y_train) 226 filename = 'LR_Model' 227 pickle.dump(model, open(filename, 'wb'))
四、總結:
通過以上分析,可以初步得出以下的結論:
1.雙親受教育水平會影響孩子成績,父母學歷越高,學生成績越好
2.學生升學意願會影響學生自身成績
3.考試准備充分的同學成績較高